@anaralabs/lector 3.2.0 → 3.4.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/README.md +34 -1
- package/dist/_tsup-dts-rollup.d.ts +87 -8
- package/dist/index.d.ts +2 -0
- package/dist/index.js +743 -298
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { AnnotationLayer, TextLayer, getDocument } from 'pdfjs-dist';
|
|
4
|
-
import React, { createContext, forwardRef, useContext,
|
|
4
|
+
import React, { createContext, forwardRef, useContext, useState, useRef, useCallback, useEffect, cloneElement, createRef, useLayoutEffect } from 'react';
|
|
5
5
|
import { useVirtualizer, elementScroll, debounce } from '@tanstack/react-virtual';
|
|
6
6
|
import { useStore, createStore } from 'zustand';
|
|
7
7
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
8
8
|
import { useDebounce } from 'use-debounce';
|
|
9
|
-
import {
|
|
9
|
+
import { v4 } from 'uuid';
|
|
10
10
|
import { useFloating, autoUpdate, offset, shift, useDismiss, useInteractions } from '@floating-ui/react';
|
|
11
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
11
12
|
|
|
12
13
|
// ../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
13
14
|
function r(e) {
|
|
@@ -130,7 +131,16 @@ var PDFStore = createZustandContext(
|
|
|
130
131
|
set({
|
|
131
132
|
customSelectionRects: val
|
|
132
133
|
});
|
|
133
|
-
}
|
|
134
|
+
},
|
|
135
|
+
coloredHighlights: [],
|
|
136
|
+
addColoredHighlight: (value) => set((prevState) => ({
|
|
137
|
+
coloredHighlights: [...prevState.coloredHighlights, value]
|
|
138
|
+
})),
|
|
139
|
+
deleteColoredHighlight: (uuid) => set((prevState) => ({
|
|
140
|
+
coloredHighlights: prevState.coloredHighlights.filter(
|
|
141
|
+
(rect) => rect.uuid !== uuid
|
|
142
|
+
)
|
|
143
|
+
}))
|
|
134
144
|
}));
|
|
135
145
|
}
|
|
136
146
|
);
|
|
@@ -160,10 +170,74 @@ var cancellable = (promise) => {
|
|
|
160
170
|
}
|
|
161
171
|
};
|
|
162
172
|
};
|
|
173
|
+
|
|
174
|
+
// src/hooks/pages/usePdfJump.tsx
|
|
175
|
+
var usePdfJump = () => {
|
|
176
|
+
const virtualizer = usePdf((state) => state.virtualizer);
|
|
177
|
+
const setHighlight = usePdf((state) => state.setHighlight);
|
|
178
|
+
const jumpToPage = (pageIndex, options) => {
|
|
179
|
+
if (!virtualizer) return;
|
|
180
|
+
const defaultOptions = {
|
|
181
|
+
align: "start",
|
|
182
|
+
behavior: "smooth"
|
|
183
|
+
};
|
|
184
|
+
const finalOptions = { ...defaultOptions, ...options };
|
|
185
|
+
virtualizer.scrollToIndex(pageIndex - 1, finalOptions);
|
|
186
|
+
};
|
|
187
|
+
const jumpToOffset = (offset2) => {
|
|
188
|
+
if (!virtualizer) return;
|
|
189
|
+
virtualizer.scrollToOffset(offset2, {
|
|
190
|
+
align: "start",
|
|
191
|
+
behavior: "smooth"
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
const jumpToHighlightRects = (rects, type, align = "start", additionalOffset = 0) => {
|
|
195
|
+
if (!virtualizer) return;
|
|
196
|
+
setHighlight(rects);
|
|
197
|
+
const firstPage = Math.min(...rects.map((rect) => rect.pageNumber));
|
|
198
|
+
const pageOffset = virtualizer.getOffsetForIndex(firstPage - 1, "start");
|
|
199
|
+
if (pageOffset === null) return;
|
|
200
|
+
const targetRect = rects.find((rect) => rect.pageNumber === firstPage);
|
|
201
|
+
if (!targetRect) return;
|
|
202
|
+
const isNumber = pageOffset?.[0] != null;
|
|
203
|
+
if (!isNumber) return;
|
|
204
|
+
const pageStart = pageOffset[0] ?? 0;
|
|
205
|
+
let rectTop;
|
|
206
|
+
let rectHeight;
|
|
207
|
+
if (type === "percent") {
|
|
208
|
+
const estimatePageHeight = virtualizer.options.estimateSize(
|
|
209
|
+
firstPage - 1
|
|
210
|
+
);
|
|
211
|
+
rectTop = targetRect.top / 100 * estimatePageHeight;
|
|
212
|
+
rectHeight = targetRect.height / 100 * estimatePageHeight;
|
|
213
|
+
} else {
|
|
214
|
+
rectTop = targetRect.top;
|
|
215
|
+
rectHeight = targetRect.height;
|
|
216
|
+
}
|
|
217
|
+
let scrollOffset;
|
|
218
|
+
if (align === "center") {
|
|
219
|
+
const viewportHeight = virtualizer.scrollElement?.clientHeight || 0;
|
|
220
|
+
const rectCenter = pageStart + rectTop + rectHeight / 2;
|
|
221
|
+
scrollOffset = rectCenter - viewportHeight / 2;
|
|
222
|
+
} else {
|
|
223
|
+
scrollOffset = pageStart + rectTop;
|
|
224
|
+
}
|
|
225
|
+
scrollOffset += additionalOffset;
|
|
226
|
+
const adjustedOffset = Math.max(0, scrollOffset);
|
|
227
|
+
virtualizer.scrollToOffset(adjustedOffset, {
|
|
228
|
+
align: "start",
|
|
229
|
+
// Always use start when we've calculated our own centering
|
|
230
|
+
behavior: "smooth"
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
return { jumpToPage, jumpToOffset, jumpToHighlightRects };
|
|
234
|
+
};
|
|
163
235
|
var LinkService = class {
|
|
164
236
|
_pdfDocumentProxy;
|
|
165
237
|
externalLinkEnabled = true;
|
|
166
238
|
isInPresentationMode = false;
|
|
239
|
+
_currentPageNumber = 0;
|
|
240
|
+
_pageNavigationCallback;
|
|
167
241
|
get pdfDocumentProxy() {
|
|
168
242
|
if (!this._pdfDocumentProxy) {
|
|
169
243
|
throw new Error("pdfDocumentProxy is not set");
|
|
@@ -173,17 +247,66 @@ var LinkService = class {
|
|
|
173
247
|
constructor() {
|
|
174
248
|
}
|
|
175
249
|
get pagesCount() {
|
|
176
|
-
return this.
|
|
250
|
+
return this._pdfDocumentProxy?.numPages || 0;
|
|
251
|
+
}
|
|
252
|
+
get page() {
|
|
253
|
+
return this._currentPageNumber;
|
|
254
|
+
}
|
|
255
|
+
set page(value) {
|
|
256
|
+
this._currentPageNumber = value;
|
|
257
|
+
if (this._pageNavigationCallback) {
|
|
258
|
+
this._pageNavigationCallback(value);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Required for link annotations to work
|
|
262
|
+
setDocument(pdfDocument) {
|
|
263
|
+
this._pdfDocumentProxy = pdfDocument;
|
|
264
|
+
}
|
|
265
|
+
setViewer() {
|
|
177
266
|
}
|
|
178
267
|
getDestinationHash(dest) {
|
|
179
|
-
return
|
|
268
|
+
if (!dest) return "";
|
|
269
|
+
const destRef = dest[0];
|
|
270
|
+
if (dest.length > 1 && typeof dest[1] === "object" && dest[1] !== null && "url" in dest[1]) {
|
|
271
|
+
const urlDest = dest[1];
|
|
272
|
+
return urlDest.url;
|
|
273
|
+
}
|
|
274
|
+
if (destRef && typeof destRef === "object") {
|
|
275
|
+
if ("num" in destRef) {
|
|
276
|
+
const numRef = destRef;
|
|
277
|
+
return `#page=${numRef.num + 1}`;
|
|
278
|
+
}
|
|
279
|
+
if ("gen" in destRef) {
|
|
280
|
+
const genRef = destRef;
|
|
281
|
+
const refNum = genRef.num ?? 0;
|
|
282
|
+
return `#page=${refNum + 1}`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (typeof destRef === "number") {
|
|
286
|
+
return `#page=${destRef + 1}`;
|
|
287
|
+
}
|
|
288
|
+
return `#dest-${String(dest)}`;
|
|
180
289
|
}
|
|
181
|
-
getAnchorUrl() {
|
|
182
|
-
|
|
290
|
+
getAnchorUrl(hash) {
|
|
291
|
+
if (hash.startsWith("http://") || hash.startsWith("https://")) {
|
|
292
|
+
return hash;
|
|
293
|
+
}
|
|
294
|
+
return `#${hash}`;
|
|
183
295
|
}
|
|
184
296
|
addLinkAttributes(link, url, newWindow) {
|
|
185
|
-
link
|
|
186
|
-
|
|
297
|
+
if (!link) return;
|
|
298
|
+
const isExternalLink = url.startsWith("http://") || url.startsWith("https://");
|
|
299
|
+
if (isExternalLink && this.externalLinkEnabled) {
|
|
300
|
+
link.href = url;
|
|
301
|
+
link.target = newWindow === false ? "" : "_blank";
|
|
302
|
+
link.rel = "noopener noreferrer";
|
|
303
|
+
} else if (!isExternalLink) {
|
|
304
|
+
link.href = url;
|
|
305
|
+
link.target = "";
|
|
306
|
+
} else {
|
|
307
|
+
link.href = "#";
|
|
308
|
+
link.target = "";
|
|
309
|
+
}
|
|
187
310
|
}
|
|
188
311
|
async goToDestination(dest) {
|
|
189
312
|
let explicitDest;
|
|
@@ -197,11 +320,70 @@ var LinkService = class {
|
|
|
197
320
|
if (!explicitDest) {
|
|
198
321
|
return;
|
|
199
322
|
}
|
|
200
|
-
|
|
201
|
-
|
|
323
|
+
if (explicitDest.length > 1 && typeof explicitDest[1] === "object" && explicitDest[1] !== null && "url" in explicitDest[1]) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const destRef = explicitDest[0];
|
|
327
|
+
let pageIndex;
|
|
328
|
+
if (destRef && typeof destRef === "object") {
|
|
329
|
+
if ("num" in destRef) {
|
|
330
|
+
try {
|
|
331
|
+
const refProxy = destRef;
|
|
332
|
+
pageIndex = await this.pdfDocumentProxy.getPageIndex(refProxy);
|
|
333
|
+
} catch (_error) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
} else if (typeof destRef === "number") {
|
|
340
|
+
pageIndex = destRef;
|
|
341
|
+
} else {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const pageNumber = pageIndex + 1;
|
|
345
|
+
if (this._pageNavigationCallback) {
|
|
346
|
+
this._pageNavigationCallback(pageNumber);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
executeNamedAction() {
|
|
350
|
+
}
|
|
351
|
+
navigateTo(dest) {
|
|
352
|
+
this.goToDestination(dest);
|
|
353
|
+
}
|
|
354
|
+
get rotation() {
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
set rotation(value) {
|
|
358
|
+
}
|
|
359
|
+
goToPage(pageNumber) {
|
|
360
|
+
if (pageNumber >= 1 && pageNumber <= this.pagesCount) {
|
|
361
|
+
if (this._pageNavigationCallback) {
|
|
362
|
+
this._pageNavigationCallback(pageNumber);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
setHash(hash) {
|
|
367
|
+
if (hash.startsWith("#page=")) {
|
|
368
|
+
const pageNumber = parseInt(hash.substring(6), 10);
|
|
369
|
+
if (!isNaN(pageNumber)) {
|
|
370
|
+
this.goToPage(pageNumber);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
executeSetOCGState() {
|
|
375
|
+
}
|
|
376
|
+
// Method to register navigation callback
|
|
377
|
+
registerPageNavigationCallback(callback) {
|
|
378
|
+
this._pageNavigationCallback = callback;
|
|
379
|
+
}
|
|
380
|
+
// Method to unregister navigation callback
|
|
381
|
+
unregisterPageNavigationCallback() {
|
|
382
|
+
this._pageNavigationCallback = void 0;
|
|
202
383
|
}
|
|
203
384
|
};
|
|
204
|
-
var
|
|
385
|
+
var defaultLinkService = new LinkService();
|
|
386
|
+
var PDFLinkServiceContext = createContext(defaultLinkService);
|
|
205
387
|
var usePDFLinkService = () => {
|
|
206
388
|
return useContext(PDFLinkServiceContext);
|
|
207
389
|
};
|
|
@@ -230,9 +412,12 @@ var useVisibility = ({
|
|
|
230
412
|
|
|
231
413
|
// src/hooks/layers/useAnnotationLayer.tsx
|
|
232
414
|
var defaultAnnotationLayerParams = {
|
|
233
|
-
renderForms: true
|
|
415
|
+
renderForms: true,
|
|
416
|
+
externalLinksEnabled: true,
|
|
417
|
+
jumpOptions: { behavior: "smooth", align: "start" }
|
|
234
418
|
};
|
|
235
419
|
var useAnnotationLayer = (params) => {
|
|
420
|
+
const mergedParams = { ...defaultAnnotationLayerParams, ...params };
|
|
236
421
|
const annotationLayerRef = useRef(null);
|
|
237
422
|
const annotationLayerObjectRef = useRef(null);
|
|
238
423
|
const linkService = usePDFLinkService();
|
|
@@ -241,6 +426,83 @@ var useAnnotationLayer = (params) => {
|
|
|
241
426
|
});
|
|
242
427
|
const pageNumber = usePDFPageNumber();
|
|
243
428
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
429
|
+
const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
|
|
430
|
+
useEffect(() => {
|
|
431
|
+
linkService.externalLinkEnabled = mergedParams.externalLinksEnabled;
|
|
432
|
+
}, [linkService, mergedParams.externalLinksEnabled]);
|
|
433
|
+
const { jumpToPage } = usePdfJump();
|
|
434
|
+
useEffect(() => {
|
|
435
|
+
if (!jumpToPage) return;
|
|
436
|
+
const handlePageNavigation = (pageNumber2) => {
|
|
437
|
+
jumpToPage(pageNumber2, mergedParams.jumpOptions);
|
|
438
|
+
};
|
|
439
|
+
linkService.registerPageNavigationCallback(handlePageNavigation);
|
|
440
|
+
return () => {
|
|
441
|
+
linkService.unregisterPageNavigationCallback();
|
|
442
|
+
};
|
|
443
|
+
}, [jumpToPage, linkService, mergedParams.jumpOptions]);
|
|
444
|
+
useEffect(() => {
|
|
445
|
+
const style = document.createElement("style");
|
|
446
|
+
style.textContent = `
|
|
447
|
+
.annotationLayer {
|
|
448
|
+
position: absolute;
|
|
449
|
+
left: 0;
|
|
450
|
+
top: 0;
|
|
451
|
+
right: 0;
|
|
452
|
+
bottom: 0;
|
|
453
|
+
overflow: hidden;
|
|
454
|
+
opacity: 1;
|
|
455
|
+
z-index: 3;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.annotationLayer section {
|
|
459
|
+
position: absolute;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.annotationLayer .linkAnnotation > a,
|
|
463
|
+
.annotationLayer .buttonWidgetAnnotation.pushButton > a {
|
|
464
|
+
position: absolute;
|
|
465
|
+
font-size: 1em;
|
|
466
|
+
top: 0;
|
|
467
|
+
left: 0;
|
|
468
|
+
width: 100%;
|
|
469
|
+
height: 100%;
|
|
470
|
+
background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
|
|
471
|
+
cursor: pointer;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.annotationLayer .linkAnnotation > a:hover,
|
|
475
|
+
.annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
|
|
476
|
+
opacity: 0.2;
|
|
477
|
+
background: rgba(255, 255, 0, 1);
|
|
478
|
+
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
|
479
|
+
}
|
|
480
|
+
`;
|
|
481
|
+
document.head.appendChild(style);
|
|
482
|
+
return () => {
|
|
483
|
+
document.head.removeChild(style);
|
|
484
|
+
};
|
|
485
|
+
}, []);
|
|
486
|
+
useEffect(() => {
|
|
487
|
+
if (!annotationLayerRef.current) return;
|
|
488
|
+
const element = annotationLayerRef.current;
|
|
489
|
+
const handleLinkClick = (e) => {
|
|
490
|
+
if (!e.target || !(e.target instanceof HTMLAnchorElement)) return;
|
|
491
|
+
const target = e.target;
|
|
492
|
+
const href = target.getAttribute("href") || "";
|
|
493
|
+
if (href.startsWith("#page=")) {
|
|
494
|
+
e.preventDefault();
|
|
495
|
+
const pageNumber2 = parseInt(href.substring(6), 10);
|
|
496
|
+
if (!isNaN(pageNumber2)) {
|
|
497
|
+
linkService.goToPage(pageNumber2);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
element.addEventListener("click", handleLinkClick);
|
|
502
|
+
return () => {
|
|
503
|
+
element.removeEventListener("click", handleLinkClick);
|
|
504
|
+
};
|
|
505
|
+
}, [linkService]);
|
|
244
506
|
useEffect(() => {
|
|
245
507
|
if (!annotationLayerRef.current) {
|
|
246
508
|
return;
|
|
@@ -252,14 +514,20 @@ var useAnnotationLayer = (params) => {
|
|
|
252
514
|
}
|
|
253
515
|
}, [visible]);
|
|
254
516
|
useEffect(() => {
|
|
255
|
-
if (!annotationLayerRef.current) {
|
|
517
|
+
if (!annotationLayerRef.current || !pdfPageProxy || !pdfDocumentProxy) {
|
|
256
518
|
return;
|
|
257
519
|
}
|
|
520
|
+
if (linkService._pdfDocumentProxy !== pdfDocumentProxy) {
|
|
521
|
+
linkService.setDocument(pdfDocumentProxy);
|
|
522
|
+
}
|
|
258
523
|
annotationLayerRef.current.innerHTML = "";
|
|
524
|
+
annotationLayerRef.current.className = "annotationLayer";
|
|
525
|
+
const viewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
259
526
|
const annotationLayerConfig = {
|
|
260
527
|
div: annotationLayerRef.current,
|
|
261
|
-
viewport
|
|
528
|
+
viewport,
|
|
262
529
|
page: pdfPageProxy,
|
|
530
|
+
linkService,
|
|
263
531
|
accessibilityManager: void 0,
|
|
264
532
|
annotationCanvasMap: void 0,
|
|
265
533
|
annotationEditorUIManager: void 0,
|
|
@@ -269,33 +537,38 @@ var useAnnotationLayer = (params) => {
|
|
|
269
537
|
annotationLayerObjectRef.current = annotationLayer;
|
|
270
538
|
const { cancel } = cancellable(
|
|
271
539
|
(async () => {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
540
|
+
try {
|
|
541
|
+
const annotations = await pdfPageProxy.getAnnotations();
|
|
542
|
+
await annotationLayer.render({
|
|
543
|
+
...annotationLayerConfig,
|
|
544
|
+
...mergedParams,
|
|
545
|
+
annotations,
|
|
546
|
+
linkService
|
|
547
|
+
});
|
|
548
|
+
} catch (_error) {
|
|
549
|
+
}
|
|
280
550
|
})()
|
|
281
551
|
);
|
|
282
|
-
cancel();
|
|
283
552
|
return () => {
|
|
284
553
|
cancel();
|
|
285
554
|
};
|
|
286
|
-
}, [pdfPageProxy,
|
|
555
|
+
}, [pdfPageProxy, pdfDocumentProxy, mergedParams, linkService]);
|
|
287
556
|
return {
|
|
288
557
|
annotationLayerRef
|
|
289
558
|
};
|
|
290
559
|
};
|
|
291
560
|
var AnnotationLayer2 = ({
|
|
292
561
|
renderForms = true,
|
|
562
|
+
externalLinksEnabled = true,
|
|
563
|
+
jumpOptions = { behavior: "smooth", align: "start" },
|
|
293
564
|
className,
|
|
294
565
|
style,
|
|
295
566
|
...props
|
|
296
567
|
}) => {
|
|
297
568
|
const { annotationLayerRef } = useAnnotationLayer({
|
|
298
|
-
renderForms
|
|
569
|
+
renderForms,
|
|
570
|
+
externalLinksEnabled,
|
|
571
|
+
jumpOptions
|
|
299
572
|
});
|
|
300
573
|
return /* @__PURE__ */ jsx(
|
|
301
574
|
"div",
|
|
@@ -335,7 +608,7 @@ var useDpr = () => {
|
|
|
335
608
|
};
|
|
336
609
|
|
|
337
610
|
// src/hooks/layers/useCanvasLayer.tsx
|
|
338
|
-
var useCanvasLayer = () => {
|
|
611
|
+
var useCanvasLayer = ({ background }) => {
|
|
339
612
|
const canvasRef = useRef(null);
|
|
340
613
|
const pageNumber = usePDFPageNumber();
|
|
341
614
|
const dpr = useDpr();
|
|
@@ -357,7 +630,8 @@ var useCanvasLayer = () => {
|
|
|
357
630
|
canvasContext.scale(scale, scale);
|
|
358
631
|
const renderingTask = pdfPageProxy.render({
|
|
359
632
|
canvasContext,
|
|
360
|
-
viewport
|
|
633
|
+
viewport,
|
|
634
|
+
background
|
|
361
635
|
});
|
|
362
636
|
renderingTask.promise.catch((error) => {
|
|
363
637
|
if (error.name === "RenderingCancelledException") {
|
|
@@ -373,26 +647,442 @@ var useCanvasLayer = () => {
|
|
|
373
647
|
canvasRef
|
|
374
648
|
};
|
|
375
649
|
};
|
|
376
|
-
var CanvasLayer = ({
|
|
377
|
-
style,
|
|
378
|
-
|
|
650
|
+
var CanvasLayer = ({
|
|
651
|
+
style,
|
|
652
|
+
background,
|
|
653
|
+
...props
|
|
654
|
+
}) => {
|
|
655
|
+
const { canvasRef } = useCanvasLayer({ background });
|
|
656
|
+
return /* @__PURE__ */ jsx(
|
|
657
|
+
"canvas",
|
|
658
|
+
{
|
|
659
|
+
style: {
|
|
660
|
+
...style,
|
|
661
|
+
position: "absolute",
|
|
662
|
+
top: 0,
|
|
663
|
+
left: 0,
|
|
664
|
+
width: "100%",
|
|
665
|
+
height: "100%"
|
|
666
|
+
},
|
|
667
|
+
...props,
|
|
668
|
+
ref: canvasRef
|
|
669
|
+
}
|
|
670
|
+
);
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
// src/hooks/useSelectionDimensions.tsx
|
|
674
|
+
var MERGE_THRESHOLD = 20;
|
|
675
|
+
var shouldMergeRectangles = (rect1, rect2) => {
|
|
676
|
+
const verticalOverlap = !(rect1.top > rect2.top + rect2.height || rect2.top > rect1.top + rect1.height);
|
|
677
|
+
const horizontallyClose = Math.abs(rect1.left + rect1.width - rect2.left) < MERGE_THRESHOLD || Math.abs(rect2.left + rect2.width - rect1.left) < MERGE_THRESHOLD || rect1.left < rect2.left + rect2.width && rect2.left < rect1.left + rect1.width;
|
|
678
|
+
return verticalOverlap && horizontallyClose;
|
|
679
|
+
};
|
|
680
|
+
var mergeRectangles = (rect1, rect2) => {
|
|
681
|
+
return {
|
|
682
|
+
left: Math.min(rect1.left, rect2.left),
|
|
683
|
+
top: Math.min(rect1.top, rect2.top),
|
|
684
|
+
width: Math.max(rect1.left + rect1.width, rect2.left + rect2.width) - Math.min(rect1.left, rect2.left),
|
|
685
|
+
height: Math.max(rect1.top + rect1.height, rect2.top + rect2.height) - Math.min(rect1.top, rect2.top),
|
|
686
|
+
pageNumber: rect1.pageNumber
|
|
687
|
+
};
|
|
688
|
+
};
|
|
689
|
+
var consolidateRects = (rects) => {
|
|
690
|
+
if (rects.length <= 1) return rects;
|
|
691
|
+
const sortedRectangles = rects.toSorted((a, b) => a.top - b.top);
|
|
692
|
+
let withinThresholdMergedRectangles = sortedRectangles[0];
|
|
693
|
+
const allMergedRectangles = [];
|
|
694
|
+
const blowUpMergedRectangles = (rect) => {
|
|
695
|
+
return {
|
|
696
|
+
...rect,
|
|
697
|
+
left: rect.left - 2,
|
|
698
|
+
top: rect.top - 2,
|
|
699
|
+
width: rect.width + 4,
|
|
700
|
+
height: rect.height + 2
|
|
701
|
+
};
|
|
702
|
+
};
|
|
703
|
+
for (const [index, currentRectangle] of sortedRectangles.entries()) {
|
|
704
|
+
withinThresholdMergedRectangles = mergeRectangles(
|
|
705
|
+
withinThresholdMergedRectangles,
|
|
706
|
+
currentRectangle
|
|
707
|
+
);
|
|
708
|
+
const nextRectangle = sortedRectangles[index + 1];
|
|
709
|
+
if (!nextRectangle) {
|
|
710
|
+
allMergedRectangles.push(
|
|
711
|
+
blowUpMergedRectangles(withinThresholdMergedRectangles)
|
|
712
|
+
);
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
if (!shouldMergeRectangles(withinThresholdMergedRectangles, nextRectangle)) {
|
|
716
|
+
allMergedRectangles.push(
|
|
717
|
+
blowUpMergedRectangles(withinThresholdMergedRectangles)
|
|
718
|
+
);
|
|
719
|
+
withinThresholdMergedRectangles = nextRectangle;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return allMergedRectangles;
|
|
723
|
+
};
|
|
724
|
+
var useSelectionDimensions = () => {
|
|
725
|
+
const store = PDFStore.useContext();
|
|
726
|
+
const getDimension = () => {
|
|
727
|
+
const selection = window.getSelection();
|
|
728
|
+
if (!selection || selection.isCollapsed) return;
|
|
729
|
+
const range = selection.getRangeAt(0);
|
|
730
|
+
const highlights = [];
|
|
731
|
+
const textLayerMap = /* @__PURE__ */ new Map();
|
|
732
|
+
const clientRects = Array.from(range.getClientRects()).filter(
|
|
733
|
+
(rect) => rect.width > 2 && rect.height > 2
|
|
734
|
+
);
|
|
735
|
+
clientRects.forEach((clientRect) => {
|
|
736
|
+
const element = document.elementFromPoint(
|
|
737
|
+
clientRect.left + 1,
|
|
738
|
+
clientRect.top + clientRect.height / 2
|
|
739
|
+
);
|
|
740
|
+
const textLayer = element?.closest(".textLayer");
|
|
741
|
+
if (!textLayer) return;
|
|
742
|
+
const pageNumber = parseInt(
|
|
743
|
+
textLayer.getAttribute("data-page-number") || "1",
|
|
744
|
+
10
|
|
745
|
+
);
|
|
746
|
+
const textLayerRect = textLayer.getBoundingClientRect();
|
|
747
|
+
const zoom = store.getState().zoom;
|
|
748
|
+
const rect = {
|
|
749
|
+
width: clientRect.width / zoom,
|
|
750
|
+
height: clientRect.height / zoom,
|
|
751
|
+
top: (clientRect.top - textLayerRect.top) / zoom,
|
|
752
|
+
left: (clientRect.left - textLayerRect.left) / zoom,
|
|
753
|
+
pageNumber
|
|
754
|
+
};
|
|
755
|
+
if (!textLayerMap.has(pageNumber)) {
|
|
756
|
+
textLayerMap.set(pageNumber, []);
|
|
757
|
+
}
|
|
758
|
+
textLayerMap.get(pageNumber)?.push(rect);
|
|
759
|
+
});
|
|
760
|
+
textLayerMap.forEach((rects) => {
|
|
761
|
+
if (rects.length > 0) {
|
|
762
|
+
highlights.push(...consolidateRects(rects));
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
return {
|
|
766
|
+
highlights: highlights.sort((a, b) => a.pageNumber - b.pageNumber),
|
|
767
|
+
text: range.toString().trim(),
|
|
768
|
+
isCollapsed: false
|
|
769
|
+
};
|
|
770
|
+
};
|
|
771
|
+
const getSelection = () => getDimension();
|
|
772
|
+
return { getDimension, getSelection };
|
|
773
|
+
};
|
|
774
|
+
var SelectionTooltip = ({ children }) => {
|
|
775
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
776
|
+
const lastSelectionRef = useRef(null);
|
|
777
|
+
const viewportRef = usePdf((state) => state.viewportRef);
|
|
778
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
779
|
+
placement: "bottom",
|
|
780
|
+
open: isOpen,
|
|
781
|
+
onOpenChange: setIsOpen,
|
|
782
|
+
whileElementsMounted: autoUpdate,
|
|
783
|
+
middleware: [offset(10), shift({ padding: 8 })]
|
|
784
|
+
});
|
|
785
|
+
const dismiss = useDismiss(context);
|
|
786
|
+
const { getFloatingProps } = useInteractions([dismiss]);
|
|
787
|
+
const updateTooltipPosition = useCallback(() => {
|
|
788
|
+
const selection = document.getSelection();
|
|
789
|
+
if (!selection || selection.isCollapsed) {
|
|
790
|
+
setIsOpen(false);
|
|
791
|
+
lastSelectionRef.current = null;
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const range = selection.getRangeAt(0);
|
|
795
|
+
if (!range) return;
|
|
796
|
+
const rects = range.getClientRects();
|
|
797
|
+
const lastRect = rects[rects.length - 1];
|
|
798
|
+
lastSelectionRef.current = range;
|
|
799
|
+
if (lastRect) {
|
|
800
|
+
refs.setReference({
|
|
801
|
+
getBoundingClientRect: () => ({
|
|
802
|
+
width: lastRect.width,
|
|
803
|
+
height: lastRect.height,
|
|
804
|
+
x: lastRect.left,
|
|
805
|
+
y: lastRect.bottom,
|
|
806
|
+
// Position below the last line of selection
|
|
807
|
+
top: lastRect.bottom,
|
|
808
|
+
right: lastRect.right,
|
|
809
|
+
bottom: lastRect.bottom + lastRect.height,
|
|
810
|
+
left: lastRect.left
|
|
811
|
+
}),
|
|
812
|
+
getClientRects: () => [lastRect]
|
|
813
|
+
});
|
|
814
|
+
setIsOpen(true);
|
|
815
|
+
} else {
|
|
816
|
+
setIsOpen(false);
|
|
817
|
+
}
|
|
818
|
+
}, [refs]);
|
|
819
|
+
useEffect(() => {
|
|
820
|
+
const handleSelectionChange = () => {
|
|
821
|
+
const selection = document.getSelection();
|
|
822
|
+
if (selection && viewportRef.current?.contains(selection.anchorNode)) {
|
|
823
|
+
requestAnimationFrame(updateTooltipPosition);
|
|
824
|
+
} else {
|
|
825
|
+
setIsOpen(false);
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
const handleScroll = () => {
|
|
829
|
+
if (!isOpen || !lastSelectionRef.current) return;
|
|
830
|
+
requestAnimationFrame(updateTooltipPosition);
|
|
831
|
+
};
|
|
832
|
+
document.addEventListener("selectionchange", handleSelectionChange);
|
|
833
|
+
if (viewportRef.current) {
|
|
834
|
+
viewportRef.current.addEventListener("scroll", handleScroll, {
|
|
835
|
+
passive: true
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
return () => {
|
|
839
|
+
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
840
|
+
if (viewportRef.current) {
|
|
841
|
+
viewportRef.current.removeEventListener("scroll", handleScroll);
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
}, [refs, isOpen, viewportRef, updateTooltipPosition]);
|
|
845
|
+
useEffect(() => {
|
|
846
|
+
const handleFloatingClick = (e) => {
|
|
847
|
+
if (refs.floating.current?.contains(e.target)) {
|
|
848
|
+
e.stopPropagation();
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
document.addEventListener("click", handleFloatingClick);
|
|
852
|
+
return () => document.removeEventListener("click", handleFloatingClick);
|
|
853
|
+
}, [refs.floating]);
|
|
854
|
+
return /* @__PURE__ */ jsx(Fragment, { children: isOpen && /* @__PURE__ */ jsx(
|
|
855
|
+
"div",
|
|
856
|
+
{
|
|
857
|
+
ref: refs.setFloating,
|
|
858
|
+
style: {
|
|
859
|
+
...floatingStyles
|
|
860
|
+
},
|
|
861
|
+
...getFloatingProps(),
|
|
862
|
+
children
|
|
863
|
+
}
|
|
864
|
+
) });
|
|
865
|
+
};
|
|
866
|
+
var defaultColors = [
|
|
867
|
+
{
|
|
868
|
+
color: "#e3b127",
|
|
869
|
+
localization: {
|
|
870
|
+
id: "yellow",
|
|
871
|
+
defaultMessage: "Yellow"
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
color: "#419931",
|
|
876
|
+
localization: {
|
|
877
|
+
id: "green",
|
|
878
|
+
defaultMessage: "Green"
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
color: "#4286c9",
|
|
883
|
+
localization: {
|
|
884
|
+
id: "blue",
|
|
885
|
+
defaultMessage: "Blue"
|
|
886
|
+
}
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
color: "#f246b6",
|
|
890
|
+
localization: {
|
|
891
|
+
id: "pink",
|
|
892
|
+
defaultMessage: "Pink"
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
color: "#a53dd1",
|
|
897
|
+
localization: {
|
|
898
|
+
id: "purple",
|
|
899
|
+
defaultMessage: "Purple"
|
|
900
|
+
}
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
color: "#f09037",
|
|
904
|
+
localization: {
|
|
905
|
+
id: "orange",
|
|
906
|
+
defaultMessage: "Orange"
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
color: "#37f0d4",
|
|
911
|
+
localization: {
|
|
912
|
+
id: "teal",
|
|
913
|
+
defaultMessage: "Teal"
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
color: "#3d0ff5",
|
|
918
|
+
localization: {
|
|
919
|
+
id: "purple",
|
|
920
|
+
defaultMessage: "Purple"
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
color: "#f50f26",
|
|
925
|
+
localization: {
|
|
926
|
+
id: "red",
|
|
927
|
+
defaultMessage: "Red"
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
];
|
|
931
|
+
var ColorSelectionTool = ({
|
|
932
|
+
highlighterColors = defaultColors,
|
|
933
|
+
onColorSelection
|
|
934
|
+
}) => {
|
|
935
|
+
return /* @__PURE__ */ jsx(SelectionTooltip, { children: /* @__PURE__ */ jsx(
|
|
936
|
+
"div",
|
|
937
|
+
{
|
|
938
|
+
style: {
|
|
939
|
+
display: "flex",
|
|
940
|
+
gap: "0.5rem",
|
|
941
|
+
padding: "0.5rem",
|
|
942
|
+
backgroundColor: "#363636",
|
|
943
|
+
borderRadius: "0.5rem"
|
|
944
|
+
},
|
|
945
|
+
children: highlighterColors.map((colorItem, index) => /* @__PURE__ */ jsx(
|
|
946
|
+
"button",
|
|
947
|
+
{
|
|
948
|
+
onClick: () => onColorSelection(colorItem),
|
|
949
|
+
title: colorItem.localization.defaultMessage,
|
|
950
|
+
"aria-label": colorItem.localization.defaultMessage,
|
|
951
|
+
style: {
|
|
952
|
+
width: "1.25rem",
|
|
953
|
+
height: "1.25rem",
|
|
954
|
+
borderRadius: "0.25rem",
|
|
955
|
+
cursor: "pointer",
|
|
956
|
+
backgroundColor: colorItem.color
|
|
957
|
+
}
|
|
958
|
+
},
|
|
959
|
+
index
|
|
960
|
+
))
|
|
961
|
+
}
|
|
962
|
+
) });
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
// src/utils/selectionUtils.ts
|
|
966
|
+
var getEndOfHighlight = (selection) => {
|
|
967
|
+
const lastRectangle = selection.rectangles[selection.rectangles.length - 1];
|
|
968
|
+
return lastRectangle.left + lastRectangle.width + 10;
|
|
969
|
+
};
|
|
970
|
+
var getMidHeightOfHighlightLine = (selection) => {
|
|
971
|
+
const lastRectangle = selection.rectangles[selection.rectangles.length - 1];
|
|
972
|
+
return lastRectangle.top + lastRectangle.height / 2;
|
|
973
|
+
};
|
|
974
|
+
var ColoredHighlightComponent = ({
|
|
975
|
+
selection
|
|
976
|
+
}) => {
|
|
977
|
+
const deleteColoredHighlight = usePdf(
|
|
978
|
+
(state) => state.deleteColoredHighlight
|
|
979
|
+
);
|
|
980
|
+
const [showButton, setShowButton] = useState(false);
|
|
981
|
+
return /* @__PURE__ */ jsxs("div", { className: "colored-highlight", children: [
|
|
982
|
+
selection.rectangles.map((rect, index) => /* @__PURE__ */ jsx(
|
|
983
|
+
"span",
|
|
984
|
+
{
|
|
985
|
+
onClick: () => setShowButton(!showButton),
|
|
986
|
+
style: {
|
|
987
|
+
position: "absolute",
|
|
988
|
+
top: rect.top,
|
|
989
|
+
left: rect.left,
|
|
990
|
+
height: rect.height,
|
|
991
|
+
width: rect.width,
|
|
992
|
+
cursor: "pointer",
|
|
993
|
+
zIndex: 30,
|
|
994
|
+
backgroundColor: selection.color,
|
|
995
|
+
// mixBlendMode: "lighten", // changes the color of the text
|
|
996
|
+
mixBlendMode: "darken",
|
|
997
|
+
// best results
|
|
998
|
+
// mixBlendMode: "multiply", // works but coloring has some inconsistencies
|
|
999
|
+
borderRadius: "0.2rem"
|
|
1000
|
+
}
|
|
1001
|
+
},
|
|
1002
|
+
`${selection.uuid}-${index}`
|
|
1003
|
+
)),
|
|
1004
|
+
showButton && /* @__PURE__ */ jsx(
|
|
1005
|
+
"button",
|
|
1006
|
+
{
|
|
1007
|
+
style: {
|
|
1008
|
+
backgroundColor: "white",
|
|
1009
|
+
color: "white",
|
|
1010
|
+
borderRadius: "5px",
|
|
1011
|
+
padding: "5px",
|
|
1012
|
+
cursor: "pointer",
|
|
1013
|
+
boxShadow: "2px 2px 5px black",
|
|
1014
|
+
position: "absolute",
|
|
1015
|
+
top: getMidHeightOfHighlightLine(selection),
|
|
1016
|
+
left: getEndOfHighlight(selection),
|
|
1017
|
+
zIndex: 30,
|
|
1018
|
+
transform: "translateY(-50%)"
|
|
1019
|
+
},
|
|
1020
|
+
onClick: () => deleteColoredHighlight(selection.uuid),
|
|
1021
|
+
children: /* @__PURE__ */ jsx(
|
|
1022
|
+
"svg",
|
|
1023
|
+
{
|
|
1024
|
+
fill: "#000000",
|
|
1025
|
+
version: "1.1",
|
|
1026
|
+
id: "Capa_1",
|
|
1027
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1028
|
+
width: "15px",
|
|
1029
|
+
height: "15px",
|
|
1030
|
+
viewBox: "0 0 485 485",
|
|
1031
|
+
children: /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsxs("g", { children: [
|
|
1032
|
+
/* @__PURE__ */ jsx("rect", { x: "67.224", width: "350.535", height: "71.81" }),
|
|
1033
|
+
/* @__PURE__ */ jsx(
|
|
1034
|
+
"path",
|
|
1035
|
+
{
|
|
1036
|
+
d: "M417.776,92.829H67.237V485h350.537V92.829H417.776z M165.402,431.447h-28.362V146.383h28.362V431.447z M256.689,431.447\n h-28.363V146.383h28.363V431.447z M347.97,431.447h-28.361V146.383h28.361V431.447z"
|
|
1037
|
+
}
|
|
1038
|
+
)
|
|
1039
|
+
] }) })
|
|
1040
|
+
}
|
|
1041
|
+
)
|
|
1042
|
+
},
|
|
1043
|
+
`${selection.uuid}-delete-button`
|
|
1044
|
+
)
|
|
1045
|
+
] });
|
|
1046
|
+
};
|
|
1047
|
+
var ColoredHighlightLayer = ({
|
|
1048
|
+
onHighlight
|
|
379
1049
|
}) => {
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
style: {
|
|
385
|
-
...style,
|
|
386
|
-
position: "absolute",
|
|
387
|
-
top: 0,
|
|
388
|
-
left: 0,
|
|
389
|
-
width: "100%",
|
|
390
|
-
height: "100%"
|
|
391
|
-
},
|
|
392
|
-
...props,
|
|
393
|
-
ref: canvasRef
|
|
394
|
-
}
|
|
1050
|
+
const pageNumber = usePDFPageNumber();
|
|
1051
|
+
const { getSelection } = useSelectionDimensions();
|
|
1052
|
+
const highlights = usePdf(
|
|
1053
|
+
(state) => state.coloredHighlights
|
|
395
1054
|
);
|
|
1055
|
+
const addColoredHighlight = usePdf((state) => state.addColoredHighlight);
|
|
1056
|
+
const handleHighlighting = useCallback((color) => {
|
|
1057
|
+
const { highlights: highlights2, text } = getSelection();
|
|
1058
|
+
if (highlights2[0]) {
|
|
1059
|
+
const highlight = {
|
|
1060
|
+
uuid: v4(),
|
|
1061
|
+
pageNumber: highlights2[0].pageNumber,
|
|
1062
|
+
// usePDFPageNumber() doesn't return the correct page number, so i'm getting the number directly from the first highlight
|
|
1063
|
+
color,
|
|
1064
|
+
rectangles: highlights2,
|
|
1065
|
+
text
|
|
1066
|
+
};
|
|
1067
|
+
addColoredHighlight(highlight);
|
|
1068
|
+
if (onHighlight) onHighlight(highlight);
|
|
1069
|
+
}
|
|
1070
|
+
}, []);
|
|
1071
|
+
return /* @__PURE__ */ jsxs("div", { className: "colored-highlights-layer", children: [
|
|
1072
|
+
highlights.filter((selection) => selection.pageNumber === pageNumber).map((selection) => /* @__PURE__ */ jsx(
|
|
1073
|
+
ColoredHighlightComponent,
|
|
1074
|
+
{
|
|
1075
|
+
selection
|
|
1076
|
+
},
|
|
1077
|
+
selection.uuid
|
|
1078
|
+
)),
|
|
1079
|
+
/* @__PURE__ */ jsx(
|
|
1080
|
+
ColorSelectionTool,
|
|
1081
|
+
{
|
|
1082
|
+
onColorSelection: (colorItem) => handleHighlighting(colorItem.color)
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
] });
|
|
396
1086
|
};
|
|
397
1087
|
|
|
398
1088
|
// src/components/layers/custom-layer.tsx
|
|
@@ -489,68 +1179,6 @@ var TextLayer2 = ({
|
|
|
489
1179
|
}
|
|
490
1180
|
);
|
|
491
1181
|
};
|
|
492
|
-
|
|
493
|
-
// src/hooks/pages/usePdfJump.tsx
|
|
494
|
-
var usePdfJump = () => {
|
|
495
|
-
const virtualizer = usePdf((state) => state.virtualizer);
|
|
496
|
-
const setHighlight = usePdf((state) => state.setHighlight);
|
|
497
|
-
const jumpToPage = (pageIndex, options) => {
|
|
498
|
-
if (!virtualizer) return;
|
|
499
|
-
const defaultOptions = {
|
|
500
|
-
align: "start",
|
|
501
|
-
behavior: "smooth"
|
|
502
|
-
};
|
|
503
|
-
const finalOptions = { ...defaultOptions, ...options };
|
|
504
|
-
virtualizer.scrollToIndex(pageIndex - 1, finalOptions);
|
|
505
|
-
};
|
|
506
|
-
const jumpToOffset = (offset2) => {
|
|
507
|
-
if (!virtualizer) return;
|
|
508
|
-
virtualizer.scrollToOffset(offset2, {
|
|
509
|
-
align: "start",
|
|
510
|
-
behavior: "smooth"
|
|
511
|
-
});
|
|
512
|
-
};
|
|
513
|
-
const jumpToHighlightRects = (rects, type, align = "start", additionalOffset = 0) => {
|
|
514
|
-
if (!virtualizer) return;
|
|
515
|
-
setHighlight(rects);
|
|
516
|
-
const firstPage = Math.min(...rects.map((rect) => rect.pageNumber));
|
|
517
|
-
const pageOffset = virtualizer.getOffsetForIndex(firstPage - 1, "start");
|
|
518
|
-
if (pageOffset === null) return;
|
|
519
|
-
const targetRect = rects.find((rect) => rect.pageNumber === firstPage);
|
|
520
|
-
if (!targetRect) return;
|
|
521
|
-
const isNumber = pageOffset?.[0] != null;
|
|
522
|
-
if (!isNumber) return;
|
|
523
|
-
const pageStart = pageOffset[0] ?? 0;
|
|
524
|
-
let rectTop;
|
|
525
|
-
let rectHeight;
|
|
526
|
-
if (type === "percent") {
|
|
527
|
-
const estimatePageHeight = virtualizer.options.estimateSize(
|
|
528
|
-
firstPage - 1
|
|
529
|
-
);
|
|
530
|
-
rectTop = targetRect.top / 100 * estimatePageHeight;
|
|
531
|
-
rectHeight = targetRect.height / 100 * estimatePageHeight;
|
|
532
|
-
} else {
|
|
533
|
-
rectTop = targetRect.top;
|
|
534
|
-
rectHeight = targetRect.height;
|
|
535
|
-
}
|
|
536
|
-
let scrollOffset;
|
|
537
|
-
if (align === "center") {
|
|
538
|
-
const viewportHeight = virtualizer.scrollElement?.clientHeight || 0;
|
|
539
|
-
const rectCenter = pageStart + rectTop + rectHeight / 2;
|
|
540
|
-
scrollOffset = rectCenter - viewportHeight / 2;
|
|
541
|
-
} else {
|
|
542
|
-
scrollOffset = pageStart + rectTop;
|
|
543
|
-
}
|
|
544
|
-
scrollOffset += additionalOffset;
|
|
545
|
-
const adjustedOffset = Math.max(0, scrollOffset);
|
|
546
|
-
virtualizer.scrollToOffset(adjustedOffset, {
|
|
547
|
-
align: "start",
|
|
548
|
-
// Always use start when we've calculated our own centering
|
|
549
|
-
behavior: "smooth"
|
|
550
|
-
});
|
|
551
|
-
};
|
|
552
|
-
return { jumpToPage, jumpToOffset, jumpToHighlightRects };
|
|
553
|
-
};
|
|
554
1182
|
var usePDFOutline = () => {
|
|
555
1183
|
const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
|
|
556
1184
|
const [outline, setOutline] = useState();
|
|
@@ -2953,12 +3581,10 @@ var Pages = ({
|
|
|
2953
3581
|
toOffset -= size;
|
|
2954
3582
|
}
|
|
2955
3583
|
const scrollSizeProp = virtualizer.options.horizontal ? "scrollWidth" : "scrollHeight";
|
|
2956
|
-
|
|
3584
|
+
virtualizer.scrollElement ? "document" in virtualizer.scrollElement ? (
|
|
2957
3585
|
//@ts-expect-error this is a private stuff
|
|
2958
3586
|
virtualizer.scrollElement.document.documentElement[scrollSizeProp]
|
|
2959
3587
|
) : virtualizer.scrollElement[scrollSizeProp] : 0;
|
|
2960
|
-
const maxOffset = scrollSize - size;
|
|
2961
|
-
console.log(scrollSize, maxOffset, toOffset);
|
|
2962
3588
|
return Math.max(toOffset, 0);
|
|
2963
3589
|
};
|
|
2964
3590
|
}, [zoom, virtualizer]);
|
|
@@ -3151,98 +3777,6 @@ var Search = ({ children, loading = "Loading..." }) => {
|
|
|
3151
3777
|
}, [proxies, getTextContent]);
|
|
3152
3778
|
return isLoading ? loading : children;
|
|
3153
3779
|
};
|
|
3154
|
-
var SelectionTooltip = ({ children }) => {
|
|
3155
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
3156
|
-
const lastSelectionRef = useRef(null);
|
|
3157
|
-
const viewportRef = usePdf((state) => state.viewportRef);
|
|
3158
|
-
const { refs, floatingStyles, context } = useFloating({
|
|
3159
|
-
placement: "bottom",
|
|
3160
|
-
open: isOpen,
|
|
3161
|
-
onOpenChange: setIsOpen,
|
|
3162
|
-
whileElementsMounted: autoUpdate,
|
|
3163
|
-
middleware: [offset(10), shift({ padding: 8 })]
|
|
3164
|
-
});
|
|
3165
|
-
const dismiss = useDismiss(context);
|
|
3166
|
-
const { getFloatingProps } = useInteractions([dismiss]);
|
|
3167
|
-
const updateTooltipPosition = useCallback(() => {
|
|
3168
|
-
const selection = document.getSelection();
|
|
3169
|
-
if (!selection || selection.isCollapsed) {
|
|
3170
|
-
setIsOpen(false);
|
|
3171
|
-
lastSelectionRef.current = null;
|
|
3172
|
-
return;
|
|
3173
|
-
}
|
|
3174
|
-
const range = selection.getRangeAt(0);
|
|
3175
|
-
if (!range) return;
|
|
3176
|
-
const rects = range.getClientRects();
|
|
3177
|
-
const lastRect = rects[rects.length - 1];
|
|
3178
|
-
lastSelectionRef.current = range;
|
|
3179
|
-
if (lastRect) {
|
|
3180
|
-
refs.setReference({
|
|
3181
|
-
getBoundingClientRect: () => ({
|
|
3182
|
-
width: lastRect.width,
|
|
3183
|
-
height: lastRect.height,
|
|
3184
|
-
x: lastRect.left,
|
|
3185
|
-
y: lastRect.bottom,
|
|
3186
|
-
// Position below the last line of selection
|
|
3187
|
-
top: lastRect.bottom,
|
|
3188
|
-
right: lastRect.right,
|
|
3189
|
-
bottom: lastRect.bottom + lastRect.height,
|
|
3190
|
-
left: lastRect.left
|
|
3191
|
-
}),
|
|
3192
|
-
getClientRects: () => [lastRect]
|
|
3193
|
-
});
|
|
3194
|
-
setIsOpen(true);
|
|
3195
|
-
} else {
|
|
3196
|
-
setIsOpen(false);
|
|
3197
|
-
}
|
|
3198
|
-
}, [refs]);
|
|
3199
|
-
useEffect(() => {
|
|
3200
|
-
const handleSelectionChange = () => {
|
|
3201
|
-
const selection = document.getSelection();
|
|
3202
|
-
if (selection && viewportRef.current?.contains(selection.anchorNode)) {
|
|
3203
|
-
requestAnimationFrame(updateTooltipPosition);
|
|
3204
|
-
} else {
|
|
3205
|
-
setIsOpen(false);
|
|
3206
|
-
}
|
|
3207
|
-
};
|
|
3208
|
-
const handleScroll = () => {
|
|
3209
|
-
if (!isOpen || !lastSelectionRef.current) return;
|
|
3210
|
-
requestAnimationFrame(updateTooltipPosition);
|
|
3211
|
-
};
|
|
3212
|
-
document.addEventListener("selectionchange", handleSelectionChange);
|
|
3213
|
-
if (viewportRef.current) {
|
|
3214
|
-
viewportRef.current.addEventListener("scroll", handleScroll, {
|
|
3215
|
-
passive: true
|
|
3216
|
-
});
|
|
3217
|
-
}
|
|
3218
|
-
return () => {
|
|
3219
|
-
document.removeEventListener("selectionchange", handleSelectionChange);
|
|
3220
|
-
if (viewportRef.current) {
|
|
3221
|
-
viewportRef.current.removeEventListener("scroll", handleScroll);
|
|
3222
|
-
}
|
|
3223
|
-
};
|
|
3224
|
-
}, [refs, isOpen, viewportRef, updateTooltipPosition]);
|
|
3225
|
-
useEffect(() => {
|
|
3226
|
-
const handleFloatingClick = (e) => {
|
|
3227
|
-
if (refs.floating.current?.contains(e.target)) {
|
|
3228
|
-
e.stopPropagation();
|
|
3229
|
-
}
|
|
3230
|
-
};
|
|
3231
|
-
document.addEventListener("click", handleFloatingClick);
|
|
3232
|
-
return () => document.removeEventListener("click", handleFloatingClick);
|
|
3233
|
-
}, [refs.floating]);
|
|
3234
|
-
return /* @__PURE__ */ jsx(Fragment, { children: isOpen && /* @__PURE__ */ jsx(
|
|
3235
|
-
"div",
|
|
3236
|
-
{
|
|
3237
|
-
ref: refs.setFloating,
|
|
3238
|
-
style: {
|
|
3239
|
-
...floatingStyles
|
|
3240
|
-
},
|
|
3241
|
-
...getFloatingProps(),
|
|
3242
|
-
children
|
|
3243
|
-
}
|
|
3244
|
-
) });
|
|
3245
|
-
};
|
|
3246
3780
|
var DEFAULT_CONFIG = {
|
|
3247
3781
|
maxHeight: 800,
|
|
3248
3782
|
maxWidth: 400
|
|
@@ -3442,7 +3976,8 @@ var useSearch = () => {
|
|
|
3442
3976
|
text: text.substr(matchIndex, searchText.length + textSize),
|
|
3443
3977
|
score: 1,
|
|
3444
3978
|
matchIndex,
|
|
3445
|
-
isExactMatch: true
|
|
3979
|
+
isExactMatch: true,
|
|
3980
|
+
searchText
|
|
3446
3981
|
});
|
|
3447
3982
|
index = matchIndex + searchText.length;
|
|
3448
3983
|
}
|
|
@@ -3471,7 +4006,8 @@ var useSearch = () => {
|
|
|
3471
4006
|
text: text.substr(index, searchLower.length + textSize),
|
|
3472
4007
|
score,
|
|
3473
4008
|
matchIndex: index,
|
|
3474
|
-
isExactMatch: false
|
|
4009
|
+
isExactMatch: false,
|
|
4010
|
+
searchText
|
|
3475
4011
|
});
|
|
3476
4012
|
index += searchLower.length;
|
|
3477
4013
|
} else {
|
|
@@ -3546,7 +4082,7 @@ var useSearch = () => {
|
|
|
3546
4082
|
async function calculateHighlightRects(pageProxy, textMatch) {
|
|
3547
4083
|
const textContent = await pageProxy.getTextContent();
|
|
3548
4084
|
const items = textContent.items;
|
|
3549
|
-
const matchLength = textMatch.text.length;
|
|
4085
|
+
const matchLength = textMatch.searchText ? textMatch.searchText.length : textMatch.text.length;
|
|
3550
4086
|
const matchRects = [];
|
|
3551
4087
|
let currentIndex = 0;
|
|
3552
4088
|
let remainingMatchLength = matchLength;
|
|
@@ -3617,95 +4153,4 @@ function mergeAdjacentRects(rects) {
|
|
|
3617
4153
|
return merged;
|
|
3618
4154
|
}
|
|
3619
4155
|
|
|
3620
|
-
|
|
3621
|
-
var MERGE_THRESHOLD = 5;
|
|
3622
|
-
var shouldMergeRects = (rect1, rect2) => {
|
|
3623
|
-
const verticalOverlap = !(rect1.top > rect2.top + rect2.height || rect2.top > rect1.top + rect1.height);
|
|
3624
|
-
const horizontallyClose = Math.abs(rect1.left + rect1.width - rect2.left) < MERGE_THRESHOLD || Math.abs(rect2.left + rect2.width - rect1.left) < MERGE_THRESHOLD || rect1.left < rect2.left + rect2.width && rect2.left < rect1.left + rect1.width;
|
|
3625
|
-
return verticalOverlap && horizontallyClose;
|
|
3626
|
-
};
|
|
3627
|
-
var mergeRects = (rect1, rect2) => {
|
|
3628
|
-
return {
|
|
3629
|
-
left: Math.min(rect1.left, rect2.left),
|
|
3630
|
-
top: Math.min(rect1.top, rect2.top),
|
|
3631
|
-
width: Math.max(rect1.left + rect1.width, rect2.left + rect2.width) - Math.min(rect1.left, rect2.left),
|
|
3632
|
-
height: Math.max(rect1.top + rect1.height, rect2.top + rect2.height) - Math.min(rect1.top, rect2.top),
|
|
3633
|
-
pageNumber: rect1.pageNumber
|
|
3634
|
-
};
|
|
3635
|
-
};
|
|
3636
|
-
var consolidateRects = (rects) => {
|
|
3637
|
-
if (rects.length <= 1) return rects;
|
|
3638
|
-
const sortedRects = [...rects].sort((a, b) => a.top - b.top);
|
|
3639
|
-
let hasChanges;
|
|
3640
|
-
do {
|
|
3641
|
-
hasChanges = false;
|
|
3642
|
-
let currentRect = sortedRects[0];
|
|
3643
|
-
const tempResult = [];
|
|
3644
|
-
for (let i = 1; i < sortedRects.length; i++) {
|
|
3645
|
-
const sorted = sortedRects[i];
|
|
3646
|
-
if (!currentRect || !sorted) continue;
|
|
3647
|
-
if (shouldMergeRects(currentRect, sorted)) {
|
|
3648
|
-
currentRect = mergeRects(currentRect, sorted);
|
|
3649
|
-
hasChanges = true;
|
|
3650
|
-
} else {
|
|
3651
|
-
tempResult.push(currentRect);
|
|
3652
|
-
currentRect = sorted;
|
|
3653
|
-
}
|
|
3654
|
-
}
|
|
3655
|
-
if (currentRect) tempResult.push(currentRect);
|
|
3656
|
-
sortedRects.length = 0;
|
|
3657
|
-
sortedRects.push(...tempResult);
|
|
3658
|
-
} while (hasChanges);
|
|
3659
|
-
return sortedRects;
|
|
3660
|
-
};
|
|
3661
|
-
var useSelectionDimensions = () => {
|
|
3662
|
-
const store = PDFStore.useContext();
|
|
3663
|
-
const getDimension = () => {
|
|
3664
|
-
const selection = window.getSelection();
|
|
3665
|
-
if (!selection || selection.isCollapsed) return;
|
|
3666
|
-
const range = selection.getRangeAt(0);
|
|
3667
|
-
const highlights = [];
|
|
3668
|
-
const textLayerMap = /* @__PURE__ */ new Map();
|
|
3669
|
-
const clientRects = Array.from(range.getClientRects()).filter(
|
|
3670
|
-
(rect) => rect.width > 2 && rect.height > 2
|
|
3671
|
-
);
|
|
3672
|
-
clientRects.forEach((clientRect) => {
|
|
3673
|
-
const element = document.elementFromPoint(
|
|
3674
|
-
clientRect.left + 1,
|
|
3675
|
-
clientRect.top + clientRect.height / 2
|
|
3676
|
-
);
|
|
3677
|
-
const textLayer = element?.closest(".textLayer");
|
|
3678
|
-
if (!textLayer) return;
|
|
3679
|
-
const pageNumber = parseInt(
|
|
3680
|
-
textLayer.getAttribute("data-page-number") || "1",
|
|
3681
|
-
10
|
|
3682
|
-
);
|
|
3683
|
-
const textLayerRect = textLayer.getBoundingClientRect();
|
|
3684
|
-
const zoom = store.getState().zoom;
|
|
3685
|
-
const rect = {
|
|
3686
|
-
width: clientRect.width / zoom,
|
|
3687
|
-
height: clientRect.height / zoom,
|
|
3688
|
-
top: (clientRect.top - textLayerRect.top) / zoom,
|
|
3689
|
-
left: (clientRect.left - textLayerRect.left) / zoom,
|
|
3690
|
-
pageNumber
|
|
3691
|
-
};
|
|
3692
|
-
if (!textLayerMap.has(pageNumber)) {
|
|
3693
|
-
textLayerMap.set(pageNumber, []);
|
|
3694
|
-
}
|
|
3695
|
-
textLayerMap.get(pageNumber)?.push(rect);
|
|
3696
|
-
});
|
|
3697
|
-
textLayerMap.forEach((rects) => {
|
|
3698
|
-
if (rects.length > 0) {
|
|
3699
|
-
highlights.push(...consolidateRects(rects));
|
|
3700
|
-
}
|
|
3701
|
-
});
|
|
3702
|
-
return {
|
|
3703
|
-
highlights: highlights.sort((a, b) => a.pageNumber - b.pageNumber),
|
|
3704
|
-
text: range.toString().trim(),
|
|
3705
|
-
isCollapsed: false
|
|
3706
|
-
};
|
|
3707
|
-
};
|
|
3708
|
-
return { getDimension };
|
|
3709
|
-
};
|
|
3710
|
-
|
|
3711
|
-
export { AnnotationLayer2 as AnnotationLayer, CanvasLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer2 as TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|
|
4156
|
+
export { AnnotationLayer2 as AnnotationLayer, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer2 as TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|