@anaralabs/lector 3.14.12 → 3.14.13
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 +15 -15
- package/dist/_tsup-dts-rollup.d.ts +63 -2
- package/dist/index.js +807 -179
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext, memo, forwardRef, useMemo, cloneElement, useEffect, useRef,
|
|
3
|
+
import React, { createContext, memo, forwardRef, useMemo, cloneElement, useEffect, useRef, useLayoutEffect, useContext, useState, useCallback, useSyncExternalStore, createRef, useImperativeHandle } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { useFloating, autoUpdate, offset, flip, shift, useDismiss, useInteractions, inline } from '@floating-ui/react';
|
|
6
6
|
import { useStore, createStore } from 'zustand';
|
|
@@ -9,7 +9,7 @@ import { useShallow } from 'zustand/react/shallow';
|
|
|
9
9
|
import { useDebounce } from 'use-debounce';
|
|
10
10
|
import { v4 } from 'uuid';
|
|
11
11
|
import { Slot } from '@radix-ui/react-slot';
|
|
12
|
-
import { useVirtualizer, elementScroll,
|
|
12
|
+
import { useVirtualizer, elementScroll, observeElementRect } from '@tanstack/react-virtual';
|
|
13
13
|
|
|
14
14
|
// src/lib/clamp.ts
|
|
15
15
|
var clamp = (value, minimum, maximum) => {
|
|
@@ -219,6 +219,19 @@ function createDarkModeColorMap(colors) {
|
|
|
219
219
|
return map;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// src/lib/viewport-zoom.ts
|
|
223
|
+
var viewportZoomHandlers = /* @__PURE__ */ new WeakMap();
|
|
224
|
+
function applyViewportZoom(viewport, zoom) {
|
|
225
|
+
if (viewport) viewportZoomHandlers.get(viewport)?.(zoom);
|
|
226
|
+
}
|
|
227
|
+
function registerViewportZoom(viewport, apply) {
|
|
228
|
+
viewportZoomHandlers.set(viewport, apply);
|
|
229
|
+
return () => {
|
|
230
|
+
if (viewportZoomHandlers.get(viewport) === apply)
|
|
231
|
+
viewportZoomHandlers.delete(viewport);
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
222
235
|
// src/lib/zoom.ts
|
|
223
236
|
var USE_LAYOUT_ZOOM = typeof navigator !== "undefined" && /AppleWebKit/i.test(navigator.userAgent) && !/Chrome|Chromium|Android/i.test(navigator.userAgent);
|
|
224
237
|
var getFitWidthZoom = (containerWidth, viewports, zoomOptions) => {
|
|
@@ -271,6 +284,7 @@ var PDFStore = createZustandContext(
|
|
|
271
284
|
minZoom,
|
|
272
285
|
maxZoom
|
|
273
286
|
);
|
|
287
|
+
applyViewportZoom(state.viewportRef.current, newZoom);
|
|
274
288
|
return newZoom === state.zoom && isZoomFitWidth === state.isZoomFitWidth ? state : { zoom: newZoom, isZoomFitWidth };
|
|
275
289
|
});
|
|
276
290
|
},
|
|
@@ -285,7 +299,8 @@ var PDFStore = createZustandContext(
|
|
|
285
299
|
get().updateZoom(clampedZoom, true);
|
|
286
300
|
return clampedZoom;
|
|
287
301
|
},
|
|
288
|
-
currentPage: 1,
|
|
302
|
+
currentPage: initialState.initialPage ?? 1,
|
|
303
|
+
initialPage: initialState.initialPage ?? 1,
|
|
289
304
|
setCurrentPage: (val) => {
|
|
290
305
|
set(
|
|
291
306
|
(state) => state.currentPage === val ? state : { currentPage: val }
|
|
@@ -310,9 +325,18 @@ var PDFStore = createZustandContext(
|
|
|
310
325
|
);
|
|
311
326
|
},
|
|
312
327
|
pageProxies: initialState.pageProxies,
|
|
328
|
+
pagesLoaded: initialState.pageProxies.length === initialState.pdfDocumentProxy.numPages,
|
|
329
|
+
loadPdfPageProxies: () => initialState.pageResources ? initialState.pageResources.loadAll() : Promise.resolve(get().pageProxies),
|
|
330
|
+
isPageLoaded: (pageNumber) => Boolean(
|
|
331
|
+
initialState.pageResources?.get(pageNumber) ?? get().pageProxies[pageNumber - 1]
|
|
332
|
+
),
|
|
333
|
+
loadPdfPageProxy: (pageNumber) => initialState.pageResources ? initialState.pageResources.load(pageNumber) : Promise.resolve().then(() => get().getPdfPageProxy(pageNumber)),
|
|
313
334
|
getPdfPageProxy: (pageNumber) => {
|
|
314
|
-
const proxy = get().pageProxies[pageNumber - 1];
|
|
315
|
-
if (!proxy)
|
|
335
|
+
const proxy = initialState.pageResources?.get(pageNumber) ?? get().pageProxies[pageNumber - 1];
|
|
336
|
+
if (!proxy)
|
|
337
|
+
throw new Error(
|
|
338
|
+
initialState.pageResources && Number.isInteger(pageNumber) && pageNumber >= 1 && pageNumber <= initialState.pdfDocumentProxy.numPages ? `Page ${pageNumber} is not loaded. Await loadPdfPageProxy(${pageNumber}) first.` : `Page ${pageNumber} does not exist`
|
|
339
|
+
);
|
|
316
340
|
return proxy;
|
|
317
341
|
},
|
|
318
342
|
textContent: [],
|
|
@@ -665,23 +689,44 @@ var AnnotationTooltip = ({
|
|
|
665
689
|
};
|
|
666
690
|
var createAnnotationStore = () => createStore((set) => ({
|
|
667
691
|
annotations: [],
|
|
668
|
-
addAnnotation: (annotation) => set((state) =>
|
|
669
|
-
annotations
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
692
|
+
addAnnotation: (annotation) => set((state) => {
|
|
693
|
+
const annotations = [...state.annotations, annotation];
|
|
694
|
+
updatePageIndex(state.annotations, annotations, /* @__PURE__ */ new Map(), [
|
|
695
|
+
annotation
|
|
696
|
+
]);
|
|
697
|
+
return { annotations };
|
|
698
|
+
}),
|
|
699
|
+
updateAnnotation: (id, updates) => set((state) => {
|
|
700
|
+
const changes = /* @__PURE__ */ new Map();
|
|
701
|
+
const keys = Object.keys(updates);
|
|
702
|
+
const annotations = state.annotations.map((annotation) => {
|
|
703
|
+
if (annotation.id !== id || keys.every((key) => Object.is(annotation[key], updates[key])))
|
|
704
|
+
return annotation;
|
|
705
|
+
const updated = changes.get(annotation) ?? {
|
|
674
706
|
...annotation,
|
|
675
707
|
...updates
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
})
|
|
684
|
-
|
|
708
|
+
};
|
|
709
|
+
changes.set(annotation, updated);
|
|
710
|
+
return updated;
|
|
711
|
+
});
|
|
712
|
+
if (!changes.size) return state;
|
|
713
|
+
updatePageIndex(state.annotations, annotations, changes);
|
|
714
|
+
return { annotations };
|
|
715
|
+
}),
|
|
716
|
+
deleteAnnotation: (id) => set((state) => {
|
|
717
|
+
const changes = /* @__PURE__ */ new Map();
|
|
718
|
+
const annotations = state.annotations.filter((annotation) => {
|
|
719
|
+
if (annotation.id !== id) return true;
|
|
720
|
+
changes.set(annotation, null);
|
|
721
|
+
return false;
|
|
722
|
+
});
|
|
723
|
+
if (!changes.size) return state;
|
|
724
|
+
updatePageIndex(state.annotations, annotations, changes);
|
|
725
|
+
return { annotations };
|
|
726
|
+
}),
|
|
727
|
+
setAnnotations: (annotations) => set(
|
|
728
|
+
(state) => state.annotations === annotations ? state : { annotations }
|
|
729
|
+
)
|
|
685
730
|
}));
|
|
686
731
|
var AnnotationsStore = createZustandContext(
|
|
687
732
|
() => createAnnotationStore()
|
|
@@ -713,9 +758,7 @@ function annotationsByPage(annotations) {
|
|
|
713
758
|
if (index) return index;
|
|
714
759
|
index = /* @__PURE__ */ new Map();
|
|
715
760
|
for (const annotation of annotations) {
|
|
716
|
-
const pages =
|
|
717
|
-
for (const rect of annotation.highlights) pages.add(rect.pageNumber);
|
|
718
|
-
for (const rect of annotation.underlines ?? []) pages.add(rect.pageNumber);
|
|
761
|
+
const pages = annotationPages(annotation);
|
|
719
762
|
for (const page of pages) {
|
|
720
763
|
let values = index.get(page);
|
|
721
764
|
if (!values) {
|
|
@@ -728,6 +771,45 @@ function annotationsByPage(annotations) {
|
|
|
728
771
|
indexes.set(annotations, index);
|
|
729
772
|
return index;
|
|
730
773
|
}
|
|
774
|
+
function annotationPages(annotation) {
|
|
775
|
+
const pages = /* @__PURE__ */ new Set([annotation.pageNumber]);
|
|
776
|
+
for (const rect of annotation.highlights) pages.add(rect.pageNumber);
|
|
777
|
+
for (const rect of annotation.underlines ?? []) pages.add(rect.pageNumber);
|
|
778
|
+
return pages;
|
|
779
|
+
}
|
|
780
|
+
function isOnPage(annotation, page) {
|
|
781
|
+
return annotation.pageNumber === page || annotation.highlights.some((rect) => rect.pageNumber === page) || annotation.underlines?.some((rect) => rect.pageNumber === page);
|
|
782
|
+
}
|
|
783
|
+
function updatePageIndex(previous, next, changes, appended = []) {
|
|
784
|
+
const previousIndex = indexes.get(previous);
|
|
785
|
+
if (!previousIndex) return;
|
|
786
|
+
const index = new Map(previousIndex);
|
|
787
|
+
const affected = /* @__PURE__ */ new Set();
|
|
788
|
+
const movedTo = /* @__PURE__ */ new Set();
|
|
789
|
+
for (const [before, after] of changes) {
|
|
790
|
+
const oldPages = annotationPages(before);
|
|
791
|
+
for (const page of oldPages) affected.add(page);
|
|
792
|
+
if (after)
|
|
793
|
+
for (const page of annotationPages(after)) {
|
|
794
|
+
affected.add(page);
|
|
795
|
+
if (!oldPages.has(page)) movedTo.add(page);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
for (const page of affected) {
|
|
799
|
+
const values = movedTo.has(page) ? next.filter((annotation) => isOnPage(annotation, page)) : (previousIndex.get(page) ?? []).flatMap((annotation) => {
|
|
800
|
+
if (!changes.has(annotation)) return [annotation];
|
|
801
|
+
const updated = changes.get(annotation);
|
|
802
|
+
return updated && isOnPage(updated, page) ? [updated] : [];
|
|
803
|
+
});
|
|
804
|
+
if (values.length) index.set(page, values);
|
|
805
|
+
else index.delete(page);
|
|
806
|
+
}
|
|
807
|
+
for (const annotation of appended)
|
|
808
|
+
for (const page of annotationPages(annotation)) {
|
|
809
|
+
index.set(page, [...index.get(page) ?? [], annotation]);
|
|
810
|
+
}
|
|
811
|
+
indexes.set(next, index);
|
|
812
|
+
}
|
|
731
813
|
function usePageAnnotations(pageNumber) {
|
|
732
814
|
return useAnnotations(
|
|
733
815
|
useShallow(
|
|
@@ -1035,7 +1117,7 @@ var usePdfJump = () => {
|
|
|
1035
1117
|
}
|
|
1036
1118
|
let scrollOffset;
|
|
1037
1119
|
if (align === "center") {
|
|
1038
|
-
const viewportHeight = virtualizer.
|
|
1120
|
+
const viewportHeight = virtualizer.scrollRect?.height ?? 0;
|
|
1039
1121
|
const rectCenter = pageStart + rectTop + rectHeight / 2;
|
|
1040
1122
|
scrollOffset = rectCenter - viewportHeight / 2;
|
|
1041
1123
|
} else {
|
|
@@ -2108,6 +2190,69 @@ function applyContextRecolor(ctx, map, options) {
|
|
|
2108
2190
|
function removeContextRecolor(ctx) {
|
|
2109
2191
|
ctx[RECOLOR_CLEANUP]?.();
|
|
2110
2192
|
}
|
|
2193
|
+
|
|
2194
|
+
// src/lib/viewport-invalidation.ts
|
|
2195
|
+
var viewportInvalidationRegistry = /* @__PURE__ */ new WeakMap();
|
|
2196
|
+
var notifyCallbacks = (entry) => {
|
|
2197
|
+
entry.rafId = null;
|
|
2198
|
+
entry.callbacks.forEach((callback) => {
|
|
2199
|
+
callback();
|
|
2200
|
+
});
|
|
2201
|
+
};
|
|
2202
|
+
var scheduleInvalidation = (entry) => {
|
|
2203
|
+
if (entry.rafId !== null) {
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
entry.rafId = requestAnimationFrame(() => {
|
|
2207
|
+
notifyCallbacks(entry);
|
|
2208
|
+
});
|
|
2209
|
+
};
|
|
2210
|
+
var subscribeToViewportInvalidation = (viewport, callback) => {
|
|
2211
|
+
let entry = viewportInvalidationRegistry.get(viewport);
|
|
2212
|
+
if (!entry) {
|
|
2213
|
+
const callbacks = /* @__PURE__ */ new Set();
|
|
2214
|
+
const schedule = () => {
|
|
2215
|
+
const currentEntry = viewportInvalidationRegistry.get(viewport);
|
|
2216
|
+
if (!currentEntry) {
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
scheduleInvalidation(currentEntry);
|
|
2220
|
+
};
|
|
2221
|
+
entry = {
|
|
2222
|
+
callbacks,
|
|
2223
|
+
scrollListener: schedule,
|
|
2224
|
+
windowResizeListener: schedule,
|
|
2225
|
+
resizeObserver: typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule),
|
|
2226
|
+
rafId: null
|
|
2227
|
+
};
|
|
2228
|
+
viewport.addEventListener("scroll", entry.scrollListener, {
|
|
2229
|
+
passive: true
|
|
2230
|
+
});
|
|
2231
|
+
window.addEventListener("resize", entry.windowResizeListener, {
|
|
2232
|
+
passive: true
|
|
2233
|
+
});
|
|
2234
|
+
entry.resizeObserver?.observe(viewport);
|
|
2235
|
+
viewportInvalidationRegistry.set(viewport, entry);
|
|
2236
|
+
}
|
|
2237
|
+
entry.callbacks.add(callback);
|
|
2238
|
+
return () => {
|
|
2239
|
+
const currentEntry = viewportInvalidationRegistry.get(viewport);
|
|
2240
|
+
if (!currentEntry) {
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
currentEntry.callbacks.delete(callback);
|
|
2244
|
+
if (currentEntry.callbacks.size > 0) {
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
if (currentEntry.rafId !== null) {
|
|
2248
|
+
cancelAnimationFrame(currentEntry.rafId);
|
|
2249
|
+
}
|
|
2250
|
+
viewport.removeEventListener("scroll", currentEntry.scrollListener);
|
|
2251
|
+
window.removeEventListener("resize", currentEntry.windowResizeListener);
|
|
2252
|
+
currentEntry.resizeObserver?.disconnect();
|
|
2253
|
+
viewportInvalidationRegistry.delete(viewport);
|
|
2254
|
+
};
|
|
2255
|
+
};
|
|
2111
2256
|
var listeners = /* @__PURE__ */ new Set();
|
|
2112
2257
|
var media;
|
|
2113
2258
|
var getSnapshot = () => Math.min(window.devicePixelRatio || 1, 3);
|
|
@@ -2369,7 +2514,53 @@ var useCanvasLayer = ({ background }) => {
|
|
|
2369
2514
|
releaseBuffer();
|
|
2370
2515
|
throw error;
|
|
2371
2516
|
}
|
|
2517
|
+
let continuation = null;
|
|
2518
|
+
let idleTimer = null;
|
|
2519
|
+
let unsubscribeViewport = null;
|
|
2520
|
+
let priorityStopped = false;
|
|
2521
|
+
const clearPriorityWait = () => {
|
|
2522
|
+
if (idleTimer !== null) clearTimeout(idleTimer);
|
|
2523
|
+
idleTimer = null;
|
|
2524
|
+
unsubscribeViewport?.();
|
|
2525
|
+
unsubscribeViewport = null;
|
|
2526
|
+
};
|
|
2527
|
+
const resumeWhenVisible = () => {
|
|
2528
|
+
if (priorityStopped || !continuation) return;
|
|
2529
|
+
const { virtualizer, viewportRef } = store.getState();
|
|
2530
|
+
const item = virtualizer?.getVirtualItems().find((candidate) => candidate.index === pageNumber - 1);
|
|
2531
|
+
const offset3 = virtualizer?.scrollOffset;
|
|
2532
|
+
const height = virtualizer?.scrollRect?.height;
|
|
2533
|
+
if (virtualizer?.isScrolling && item && offset3 != null && height && (item.end < offset3 || item.start > offset3 + height)) {
|
|
2534
|
+
if (!unsubscribeViewport && viewportRef.current) {
|
|
2535
|
+
unsubscribeViewport = subscribeToViewportInvalidation(
|
|
2536
|
+
viewportRef.current,
|
|
2537
|
+
resumeWhenVisible
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
if (idleTimer === null) {
|
|
2541
|
+
idleTimer = setTimeout(() => {
|
|
2542
|
+
idleTimer = null;
|
|
2543
|
+
resumeWhenVisible();
|
|
2544
|
+
}, 160);
|
|
2545
|
+
}
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
clearPriorityWait();
|
|
2549
|
+
const proceed = continuation;
|
|
2550
|
+
continuation = null;
|
|
2551
|
+
proceed();
|
|
2552
|
+
};
|
|
2553
|
+
const stopPriority = () => {
|
|
2554
|
+
priorityStopped = true;
|
|
2555
|
+
continuation = null;
|
|
2556
|
+
clearPriorityWait();
|
|
2557
|
+
};
|
|
2558
|
+
renderingTask.onContinue = (proceed) => {
|
|
2559
|
+
continuation = proceed;
|
|
2560
|
+
resumeWhenVisible();
|
|
2561
|
+
};
|
|
2372
2562
|
renderingTask.promise.then(() => {
|
|
2563
|
+
stopPriority();
|
|
2373
2564
|
if (cancelled) {
|
|
2374
2565
|
restoreRecolor?.();
|
|
2375
2566
|
releaseBuffer();
|
|
@@ -2414,6 +2605,7 @@ var useCanvasLayer = ({ background }) => {
|
|
|
2414
2605
|
releaseBuffer();
|
|
2415
2606
|
}
|
|
2416
2607
|
}).catch((error) => {
|
|
2608
|
+
stopPriority();
|
|
2417
2609
|
restoreRecolor?.();
|
|
2418
2610
|
releaseBuffer();
|
|
2419
2611
|
if (cancelled) return;
|
|
@@ -2423,6 +2615,7 @@ var useCanvasLayer = ({ background }) => {
|
|
|
2423
2615
|
});
|
|
2424
2616
|
return () => {
|
|
2425
2617
|
cancelled = true;
|
|
2618
|
+
stopPriority();
|
|
2426
2619
|
void renderingTask.cancel();
|
|
2427
2620
|
};
|
|
2428
2621
|
}, [
|
|
@@ -2454,71 +2647,6 @@ var useCanvasLayer = ({ background }) => {
|
|
|
2454
2647
|
canvasRef
|
|
2455
2648
|
};
|
|
2456
2649
|
};
|
|
2457
|
-
|
|
2458
|
-
// src/lib/viewport-invalidation.ts
|
|
2459
|
-
var viewportInvalidationRegistry = /* @__PURE__ */ new WeakMap();
|
|
2460
|
-
var notifyCallbacks = (entry) => {
|
|
2461
|
-
entry.rafId = null;
|
|
2462
|
-
entry.callbacks.forEach((callback) => {
|
|
2463
|
-
callback();
|
|
2464
|
-
});
|
|
2465
|
-
};
|
|
2466
|
-
var scheduleInvalidation = (entry) => {
|
|
2467
|
-
if (entry.rafId !== null) {
|
|
2468
|
-
return;
|
|
2469
|
-
}
|
|
2470
|
-
entry.rafId = requestAnimationFrame(() => {
|
|
2471
|
-
notifyCallbacks(entry);
|
|
2472
|
-
});
|
|
2473
|
-
};
|
|
2474
|
-
var subscribeToViewportInvalidation = (viewport, callback) => {
|
|
2475
|
-
let entry = viewportInvalidationRegistry.get(viewport);
|
|
2476
|
-
if (!entry) {
|
|
2477
|
-
const callbacks = /* @__PURE__ */ new Set();
|
|
2478
|
-
const schedule = () => {
|
|
2479
|
-
const currentEntry = viewportInvalidationRegistry.get(viewport);
|
|
2480
|
-
if (!currentEntry) {
|
|
2481
|
-
return;
|
|
2482
|
-
}
|
|
2483
|
-
scheduleInvalidation(currentEntry);
|
|
2484
|
-
};
|
|
2485
|
-
entry = {
|
|
2486
|
-
callbacks,
|
|
2487
|
-
scrollListener: schedule,
|
|
2488
|
-
windowResizeListener: schedule,
|
|
2489
|
-
resizeObserver: typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule),
|
|
2490
|
-
rafId: null
|
|
2491
|
-
};
|
|
2492
|
-
viewport.addEventListener("scroll", entry.scrollListener, {
|
|
2493
|
-
passive: true
|
|
2494
|
-
});
|
|
2495
|
-
window.addEventListener("resize", entry.windowResizeListener, {
|
|
2496
|
-
passive: true
|
|
2497
|
-
});
|
|
2498
|
-
entry.resizeObserver?.observe(viewport);
|
|
2499
|
-
viewportInvalidationRegistry.set(viewport, entry);
|
|
2500
|
-
}
|
|
2501
|
-
entry.callbacks.add(callback);
|
|
2502
|
-
return () => {
|
|
2503
|
-
const currentEntry = viewportInvalidationRegistry.get(viewport);
|
|
2504
|
-
if (!currentEntry) {
|
|
2505
|
-
return;
|
|
2506
|
-
}
|
|
2507
|
-
currentEntry.callbacks.delete(callback);
|
|
2508
|
-
if (currentEntry.callbacks.size > 0) {
|
|
2509
|
-
return;
|
|
2510
|
-
}
|
|
2511
|
-
if (currentEntry.rafId !== null) {
|
|
2512
|
-
cancelAnimationFrame(currentEntry.rafId);
|
|
2513
|
-
}
|
|
2514
|
-
viewport.removeEventListener("scroll", currentEntry.scrollListener);
|
|
2515
|
-
window.removeEventListener("resize", currentEntry.windowResizeListener);
|
|
2516
|
-
currentEntry.resizeObserver?.disconnect();
|
|
2517
|
-
viewportInvalidationRegistry.delete(viewport);
|
|
2518
|
-
};
|
|
2519
|
-
};
|
|
2520
|
-
|
|
2521
|
-
// src/hooks/layers/useDetailCanvasLayer.tsx
|
|
2522
2650
|
var DETAIL_RENDER_IDLE_MS = 80;
|
|
2523
2651
|
var SCROLL_POLL_MS = 160;
|
|
2524
2652
|
var ZOOM_DEBOUNCE_MS = 200;
|
|
@@ -4311,6 +4439,37 @@ var Outline = ({
|
|
|
4311
4439
|
});
|
|
4312
4440
|
}) });
|
|
4313
4441
|
};
|
|
4442
|
+
function PageLoadingBoundary({
|
|
4443
|
+
pageNumber,
|
|
4444
|
+
children,
|
|
4445
|
+
fallback = null
|
|
4446
|
+
}) {
|
|
4447
|
+
const loaded = usePdf((state) => state.isPageLoaded(pageNumber));
|
|
4448
|
+
const loadPage = usePdf((state) => state.loadPdfPageProxy);
|
|
4449
|
+
const [failure, setFailure] = useState(null);
|
|
4450
|
+
const [attempt, setAttempt] = useState(0);
|
|
4451
|
+
useEffect(() => {
|
|
4452
|
+
if (loaded) return;
|
|
4453
|
+
let disposed = false;
|
|
4454
|
+
setFailure(null);
|
|
4455
|
+
void loadPage(pageNumber).catch((error) => {
|
|
4456
|
+
if (!disposed) setFailure({ pageNumber, error });
|
|
4457
|
+
});
|
|
4458
|
+
return () => {
|
|
4459
|
+
disposed = true;
|
|
4460
|
+
};
|
|
4461
|
+
}, [pageNumber, loaded, loadPage, attempt]);
|
|
4462
|
+
if (loaded) return children;
|
|
4463
|
+
if (failure?.pageNumber === pageNumber)
|
|
4464
|
+
return /* @__PURE__ */ jsxs("div", { role: "alert", children: [
|
|
4465
|
+
"Unable to load page ",
|
|
4466
|
+
pageNumber,
|
|
4467
|
+
".",
|
|
4468
|
+
" ",
|
|
4469
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setAttempt((value) => value + 1), children: "Retry" })
|
|
4470
|
+
] });
|
|
4471
|
+
return fallback;
|
|
4472
|
+
}
|
|
4314
4473
|
var Page = memo(
|
|
4315
4474
|
({
|
|
4316
4475
|
children,
|
|
@@ -4318,14 +4477,16 @@ var Page = memo(
|
|
|
4318
4477
|
style,
|
|
4319
4478
|
...props
|
|
4320
4479
|
}) => {
|
|
4321
|
-
const
|
|
4480
|
+
const loaded = usePdf((state) => state.isPageLoaded(pageNumber));
|
|
4481
|
+
const getPage = usePdf((state) => state.getPdfPageProxy);
|
|
4482
|
+
const proxy = loaded ? getPage(pageNumber) : void 0;
|
|
4322
4483
|
const viewport = usePdf((state) => state.viewports[pageNumber - 1]);
|
|
4323
4484
|
const colorScheme = usePdf((state) => state.colorScheme);
|
|
4324
4485
|
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
4325
4486
|
const pageBackground = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors)("#ffffff") : "white";
|
|
4326
|
-
const width = viewport?.width ?? (
|
|
4327
|
-
const height = viewport?.height ?? (
|
|
4328
|
-
return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value:
|
|
4487
|
+
const width = viewport?.width ?? (proxy?.view[2] ?? 0) - (proxy?.view[0] ?? 0);
|
|
4488
|
+
const height = viewport?.height ?? (proxy?.view[3] ?? 0) - (proxy?.view[1] ?? 0);
|
|
4489
|
+
return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value: pageNumber, children: /* @__PURE__ */ jsx(
|
|
4329
4490
|
Primitive.div,
|
|
4330
4491
|
{
|
|
4331
4492
|
style: {
|
|
@@ -4343,8 +4504,9 @@ var Page = memo(
|
|
|
4343
4504
|
height,
|
|
4344
4505
|
...style
|
|
4345
4506
|
},
|
|
4507
|
+
"aria-busy": !loaded || void 0,
|
|
4346
4508
|
...props,
|
|
4347
|
-
children
|
|
4509
|
+
children: /* @__PURE__ */ jsx(PageLoadingBoundary, { pageNumber, children })
|
|
4348
4510
|
}
|
|
4349
4511
|
)
|
|
4350
4512
|
}
|
|
@@ -4465,6 +4627,28 @@ var addEventListenerOptions = {
|
|
|
4465
4627
|
};
|
|
4466
4628
|
var useObserveElement = () => {
|
|
4467
4629
|
const store = PDFStore.useContext();
|
|
4630
|
+
const observeElementRect$1 = (instance, cb) => {
|
|
4631
|
+
let physicalRect;
|
|
4632
|
+
const publish = () => {
|
|
4633
|
+
if (!physicalRect) return;
|
|
4634
|
+
const zoom = store.getState().zoom;
|
|
4635
|
+
cb({
|
|
4636
|
+
width: physicalRect.width / zoom,
|
|
4637
|
+
height: physicalRect.height / zoom
|
|
4638
|
+
});
|
|
4639
|
+
};
|
|
4640
|
+
const stopObserving = observeElementRect(instance, (rect) => {
|
|
4641
|
+
physicalRect = rect;
|
|
4642
|
+
publish();
|
|
4643
|
+
});
|
|
4644
|
+
const unsubscribe = store.subscribe((state, previous) => {
|
|
4645
|
+
if (state.zoom !== previous.zoom) publish();
|
|
4646
|
+
});
|
|
4647
|
+
return () => {
|
|
4648
|
+
stopObserving?.();
|
|
4649
|
+
unsubscribe();
|
|
4650
|
+
};
|
|
4651
|
+
};
|
|
4468
4652
|
const observeElementOffset = (instance, cb) => {
|
|
4469
4653
|
const element = instance.scrollElement;
|
|
4470
4654
|
if (!element) {
|
|
@@ -4474,33 +4658,46 @@ var useObserveElement = () => {
|
|
|
4474
4658
|
if (!targetWindow) {
|
|
4475
4659
|
return;
|
|
4476
4660
|
}
|
|
4477
|
-
let
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
},
|
|
4483
|
-
instance.options.isScrollingResetDelay
|
|
4484
|
-
);
|
|
4485
|
-
const createHandler = (isScrolling) => () => {
|
|
4661
|
+
let isScrolling = false;
|
|
4662
|
+
let idleTimer;
|
|
4663
|
+
const needsIdleFallback = !instance.options.useScrollendEvent || !supportsScrollend;
|
|
4664
|
+
const publish = (scrolling) => {
|
|
4665
|
+
isScrolling = scrolling;
|
|
4486
4666
|
const { horizontal, isRtl } = instance.options;
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4667
|
+
const physicalOffset = horizontal ? element.scrollLeft * (isRtl ? -1 : 1) : element.scrollTop;
|
|
4668
|
+
cb(physicalOffset / store.getState().zoom, isScrolling);
|
|
4669
|
+
};
|
|
4670
|
+
const clearIdleTimer = () => {
|
|
4671
|
+
if (idleTimer !== void 0) targetWindow.clearTimeout(idleTimer);
|
|
4672
|
+
idleTimer = void 0;
|
|
4673
|
+
};
|
|
4674
|
+
const createHandler = (scrolling) => () => {
|
|
4675
|
+
clearIdleTimer();
|
|
4676
|
+
publish(scrolling);
|
|
4677
|
+
if (scrolling && needsIdleFallback) {
|
|
4678
|
+
idleTimer = targetWindow.setTimeout(() => {
|
|
4679
|
+
idleTimer = void 0;
|
|
4680
|
+
publish(false);
|
|
4681
|
+
}, instance.options.isScrollingResetDelay);
|
|
4682
|
+
}
|
|
4492
4683
|
};
|
|
4493
4684
|
const handler = createHandler(true);
|
|
4494
4685
|
const endHandler = createHandler(false);
|
|
4495
4686
|
endHandler();
|
|
4687
|
+
const unsubscribe = store.subscribe((state, previous) => {
|
|
4688
|
+
if (state.zoom !== previous.zoom) publish(isScrolling);
|
|
4689
|
+
});
|
|
4496
4690
|
element.addEventListener("scroll", handler, addEventListenerOptions);
|
|
4497
4691
|
element.addEventListener("scrollend", endHandler, addEventListenerOptions);
|
|
4498
4692
|
return () => {
|
|
4693
|
+
unsubscribe();
|
|
4694
|
+
clearIdleTimer();
|
|
4499
4695
|
element.removeEventListener("scroll", handler);
|
|
4500
4696
|
element.removeEventListener("scrollend", endHandler);
|
|
4501
4697
|
};
|
|
4502
4698
|
};
|
|
4503
4699
|
return {
|
|
4700
|
+
observeElementRect: observeElementRect$1,
|
|
4504
4701
|
observeElementOffset
|
|
4505
4702
|
};
|
|
4506
4703
|
};
|
|
@@ -4510,7 +4707,11 @@ var useScrollFn = () => {
|
|
|
4510
4707
|
(_offset, canSmooth, instance) => {
|
|
4511
4708
|
const zoom = store.getState().zoom;
|
|
4512
4709
|
const offset3 = _offset * zoom;
|
|
4513
|
-
elementScroll(
|
|
4710
|
+
elementScroll(
|
|
4711
|
+
offset3,
|
|
4712
|
+
{ ...canSmooth, adjustments: (canSmooth.adjustments ?? 0) * zoom },
|
|
4713
|
+
instance
|
|
4714
|
+
);
|
|
4514
4715
|
},
|
|
4515
4716
|
[store]
|
|
4516
4717
|
);
|
|
@@ -4628,7 +4829,7 @@ function _toPrimitive(input, hint) {
|
|
|
4628
4829
|
if (typeof input !== "object" || input === null) return input;
|
|
4629
4830
|
var prim = input[Symbol.toPrimitive];
|
|
4630
4831
|
if (prim !== void 0) {
|
|
4631
|
-
var res = prim.call(input, hint
|
|
4832
|
+
var res = prim.call(input, hint);
|
|
4632
4833
|
if (typeof res !== "object") return res;
|
|
4633
4834
|
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
4634
4835
|
}
|
|
@@ -6485,6 +6686,8 @@ var useViewportContainer = ({
|
|
|
6485
6686
|
});
|
|
6486
6687
|
const zoomRafRef = useRef(null);
|
|
6487
6688
|
const lastPushedZoomRef = useRef(null);
|
|
6689
|
+
const initializedZoomRef = useRef(false);
|
|
6690
|
+
const appliedZoomRef = useRef(1);
|
|
6488
6691
|
const updateTransform = useCallback(
|
|
6489
6692
|
(zoomUpdate) => {
|
|
6490
6693
|
if (!elementRef.current || !containerRef.current || !elementWrapperRef.current) {
|
|
@@ -6514,17 +6717,36 @@ var useViewportContainer = ({
|
|
|
6514
6717
|
elementWrapperRef.current.style.height = `${naturalHeight * zoom2}px`;
|
|
6515
6718
|
containerRef.current.scrollTop = translateY;
|
|
6516
6719
|
containerRef.current.scrollLeft = translateX;
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
lastPushedZoomRef.current = z;
|
|
6522
|
-
updateZoom(() => z);
|
|
6523
|
-
});
|
|
6720
|
+
appliedZoomRef.current = zoom2;
|
|
6721
|
+
if (zoomUpdate) {
|
|
6722
|
+
lastPushedZoomRef.current = zoom2;
|
|
6723
|
+
updateZoom(() => zoom2);
|
|
6524
6724
|
}
|
|
6525
6725
|
},
|
|
6526
6726
|
[containerRef, elementRef, elementWrapperRef, updateZoom, store]
|
|
6527
6727
|
);
|
|
6728
|
+
useLayoutEffect(() => {
|
|
6729
|
+
const viewport = containerRef.current;
|
|
6730
|
+
if (!viewport) return;
|
|
6731
|
+
return registerViewportZoom(viewport, (nextZoom) => {
|
|
6732
|
+
if (initializedZoomRef.current && appliedZoomRef.current === nextZoom && zoomRafRef.current === null)
|
|
6733
|
+
return;
|
|
6734
|
+
if (zoomRafRef.current !== null) {
|
|
6735
|
+
cancelAnimationFrame(zoomRafRef.current);
|
|
6736
|
+
zoomRafRef.current = null;
|
|
6737
|
+
}
|
|
6738
|
+
lastPushedZoomRef.current = null;
|
|
6739
|
+
const previousZoom = initializedZoomRef.current ? appliedZoomRef.current : store.getState().zoom;
|
|
6740
|
+
const ratio = previousZoom > 0 && Number.isFinite(previousZoom) ? nextZoom / previousZoom : 1;
|
|
6741
|
+
transformations.current = {
|
|
6742
|
+
zoom: nextZoom,
|
|
6743
|
+
translateX: viewport.scrollLeft * ratio,
|
|
6744
|
+
translateY: viewport.scrollTop * ratio
|
|
6745
|
+
};
|
|
6746
|
+
initializedZoomRef.current = true;
|
|
6747
|
+
updateTransform();
|
|
6748
|
+
});
|
|
6749
|
+
}, [containerRef, store, updateTransform]);
|
|
6528
6750
|
useEffect(() => {
|
|
6529
6751
|
if (USE_LAYOUT_ZOOM && gestureTransformAppliedRef.current && !isPinching)
|
|
6530
6752
|
updateTransform();
|
|
@@ -6537,6 +6759,16 @@ var useViewportContainer = ({
|
|
|
6537
6759
|
};
|
|
6538
6760
|
}, []);
|
|
6539
6761
|
useEffect(() => {
|
|
6762
|
+
if (!initializedZoomRef.current && containerRef.current) {
|
|
6763
|
+
initializedZoomRef.current = true;
|
|
6764
|
+
transformations.current = {
|
|
6765
|
+
zoom,
|
|
6766
|
+
translateX: containerRef.current.scrollLeft,
|
|
6767
|
+
translateY: containerRef.current.scrollTop
|
|
6768
|
+
};
|
|
6769
|
+
updateTransform();
|
|
6770
|
+
return;
|
|
6771
|
+
}
|
|
6540
6772
|
if (transformations.current.zoom === zoom || !containerRef.current) {
|
|
6541
6773
|
return;
|
|
6542
6774
|
}
|
|
@@ -6549,7 +6781,7 @@ var useViewportContainer = ({
|
|
|
6549
6781
|
zoomRafRef.current = null;
|
|
6550
6782
|
}
|
|
6551
6783
|
lastPushedZoomRef.current = null;
|
|
6552
|
-
const prevZoom =
|
|
6784
|
+
const prevZoom = appliedZoomRef.current;
|
|
6553
6785
|
if (!prevZoom || !Number.isFinite(prevZoom)) {
|
|
6554
6786
|
transformations.current = {
|
|
6555
6787
|
translateX: containerRef.current.scrollLeft,
|
|
@@ -6689,11 +6921,26 @@ var useViewportContainer = ({
|
|
|
6689
6921
|
translateY: newTranslateY
|
|
6690
6922
|
};
|
|
6691
6923
|
newMemo.lastZoom = newZoom;
|
|
6692
|
-
|
|
6924
|
+
if (zoomRafRef.current === null) {
|
|
6925
|
+
zoomRafRef.current = requestAnimationFrame(() => {
|
|
6926
|
+
zoomRafRef.current = null;
|
|
6927
|
+
updateTransform(true);
|
|
6928
|
+
});
|
|
6929
|
+
}
|
|
6693
6930
|
return newMemo;
|
|
6694
6931
|
},
|
|
6695
6932
|
onPinchStart: () => setIsPinching(true),
|
|
6696
|
-
onPinchEnd: () =>
|
|
6933
|
+
onPinchEnd: () => {
|
|
6934
|
+
const pendingFrame = zoomRafRef.current !== null;
|
|
6935
|
+
if (zoomRafRef.current !== null) {
|
|
6936
|
+
cancelAnimationFrame(zoomRafRef.current);
|
|
6937
|
+
zoomRafRef.current = null;
|
|
6938
|
+
}
|
|
6939
|
+
setIsPinching(false);
|
|
6940
|
+
if (pendingFrame || gestureTransformAppliedRef.current) {
|
|
6941
|
+
updateTransform(pendingFrame);
|
|
6942
|
+
}
|
|
6943
|
+
}
|
|
6697
6944
|
},
|
|
6698
6945
|
{
|
|
6699
6946
|
target: containerRef
|
|
@@ -6746,6 +6993,7 @@ var Pages = ({
|
|
|
6746
6993
|
const [tempItems, setTempItems] = useState([]);
|
|
6747
6994
|
const viewports = usePdf((state) => state.viewports);
|
|
6748
6995
|
const numPages = usePdf((state) => state.pdfDocumentProxy.numPages);
|
|
6996
|
+
const initialPage = usePdf((state) => state.initialPage);
|
|
6749
6997
|
const isPinching = usePdf((state) => state.isPinching);
|
|
6750
6998
|
const elementWrapperRef = useRef(null);
|
|
6751
6999
|
const elementRef = useRef(null);
|
|
@@ -6757,7 +7005,7 @@ var Pages = ({
|
|
|
6757
7005
|
});
|
|
6758
7006
|
const setVirtualizer = usePdf((state) => state.setVirtualizer);
|
|
6759
7007
|
const { scrollToFn } = useScrollFn();
|
|
6760
|
-
const { observeElementOffset } = useObserveElement();
|
|
7008
|
+
const { observeElementOffset, observeElementRect } = useObserveElement();
|
|
6761
7009
|
const viewportsRef = useRef(viewports);
|
|
6762
7010
|
viewportsRef.current = viewports;
|
|
6763
7011
|
const estimateSize = useCallback(
|
|
@@ -6769,15 +7017,22 @@ var Pages = ({
|
|
|
6769
7017
|
[]
|
|
6770
7018
|
// Stable — reads from ref
|
|
6771
7019
|
);
|
|
7020
|
+
const [startingOffset] = useState(
|
|
7021
|
+
() => initialOffset ?? viewports.slice(0, initialPage - 1).reduce(
|
|
7022
|
+
(offset3, viewport) => offset3 + viewport.height + EXTRA_HEIGHT + gap,
|
|
7023
|
+
0
|
|
7024
|
+
)
|
|
7025
|
+
);
|
|
6772
7026
|
const virtualizer = useVirtualizer({
|
|
6773
7027
|
count: numPages || 0,
|
|
6774
7028
|
getScrollElement: () => containerRef.current,
|
|
6775
7029
|
estimateSize,
|
|
6776
7030
|
observeElementOffset,
|
|
7031
|
+
observeElementRect,
|
|
6777
7032
|
overscan: virtualizerOptions?.overscan ?? 0,
|
|
6778
7033
|
scrollToFn,
|
|
6779
7034
|
gap,
|
|
6780
|
-
initialOffset,
|
|
7035
|
+
initialOffset: startingOffset,
|
|
6781
7036
|
// Never trust scrollend alone to reset isScrolling: a single missed
|
|
6782
7037
|
// event (a documented browser flake — TanStack flipped this default to
|
|
6783
7038
|
// false in later 3.x) would pin isScrolling=true forever, and every
|
|
@@ -6785,6 +7040,21 @@ var Pages = ({
|
|
|
6785
7040
|
// false keeps the isScrollingResetDelay debounce active as a fallback.
|
|
6786
7041
|
useScrollendEvent: false
|
|
6787
7042
|
});
|
|
7043
|
+
const previousViewports = useRef(viewports);
|
|
7044
|
+
useLayoutEffect(() => {
|
|
7045
|
+
if (isPinching) return;
|
|
7046
|
+
const previous = previousViewports.current;
|
|
7047
|
+
previousViewports.current = viewports;
|
|
7048
|
+
if (previous === viewports) return;
|
|
7049
|
+
let resized = false;
|
|
7050
|
+
for (let index = 0; index < viewports.length; index++) {
|
|
7051
|
+
if (previous[index]?.height !== viewports[index]?.height) {
|
|
7052
|
+
virtualizer.resizeItem(index, viewports[index].height + EXTRA_HEIGHT);
|
|
7053
|
+
resized = true;
|
|
7054
|
+
}
|
|
7055
|
+
}
|
|
7056
|
+
if (resized) setTempItems([]);
|
|
7057
|
+
}, [viewports, virtualizer, isPinching]);
|
|
6788
7058
|
useEffect(() => {
|
|
6789
7059
|
if (onOffsetChange && virtualizer.scrollOffset)
|
|
6790
7060
|
onOffsetChange(virtualizer.scrollOffset);
|
|
@@ -6931,6 +7201,164 @@ async function mapConcurrent(items, concurrency, map, isCancelled = () => false)
|
|
|
6931
7201
|
return results;
|
|
6932
7202
|
}
|
|
6933
7203
|
|
|
7204
|
+
// src/lib/page-resources.ts
|
|
7205
|
+
var PageResources = class {
|
|
7206
|
+
constructor(document2, firstPage, rotation, onError, concurrency = 16) {
|
|
7207
|
+
this.document = document2;
|
|
7208
|
+
this.rotation = rotation;
|
|
7209
|
+
this.onError = onError;
|
|
7210
|
+
this.concurrency = concurrency;
|
|
7211
|
+
this.pages.set(firstPage.pageNumber, firstPage);
|
|
7212
|
+
const viewport = this.viewportFor(firstPage);
|
|
7213
|
+
this.viewports = Array.from({ length: document2.numPages }, () => viewport);
|
|
7214
|
+
if (document2.numPages === 1) this.completePages = [firstPage];
|
|
7215
|
+
}
|
|
7216
|
+
pages = /* @__PURE__ */ new Map();
|
|
7217
|
+
requests = /* @__PURE__ */ new Map();
|
|
7218
|
+
queue = [];
|
|
7219
|
+
active = 0;
|
|
7220
|
+
disposed = false;
|
|
7221
|
+
listeners = /* @__PURE__ */ new Set();
|
|
7222
|
+
notification;
|
|
7223
|
+
viewports;
|
|
7224
|
+
completePages;
|
|
7225
|
+
viewportUpdates = /* @__PURE__ */ new Map();
|
|
7226
|
+
viewportFor(page) {
|
|
7227
|
+
return page.getViewport({
|
|
7228
|
+
scale: 1,
|
|
7229
|
+
rotation: this.rotation + (page.rotate || 0)
|
|
7230
|
+
});
|
|
7231
|
+
}
|
|
7232
|
+
get(pageNumber) {
|
|
7233
|
+
return this.pages.get(pageNumber);
|
|
7234
|
+
}
|
|
7235
|
+
getSnapshot = () => ({
|
|
7236
|
+
viewports: this.viewports,
|
|
7237
|
+
pageProxies: this.completePages
|
|
7238
|
+
});
|
|
7239
|
+
subscribe = (listener) => {
|
|
7240
|
+
this.listeners.add(listener);
|
|
7241
|
+
return () => {
|
|
7242
|
+
this.listeners.delete(listener);
|
|
7243
|
+
};
|
|
7244
|
+
};
|
|
7245
|
+
/** Visible pages move ahead of the background queue; in-flight requests are shared. */
|
|
7246
|
+
load = (pageNumber) => {
|
|
7247
|
+
if (this.disposed)
|
|
7248
|
+
return Promise.reject(new Error("Document page loading was cancelled"));
|
|
7249
|
+
if (!Number.isInteger(pageNumber) || pageNumber < 1 || pageNumber > this.document.numPages)
|
|
7250
|
+
return Promise.reject(
|
|
7251
|
+
new RangeError(`Page ${pageNumber} does not exist`)
|
|
7252
|
+
);
|
|
7253
|
+
const page = this.pages.get(pageNumber);
|
|
7254
|
+
if (page) return Promise.resolve(page);
|
|
7255
|
+
const existing = this.requests.get(pageNumber);
|
|
7256
|
+
if (existing) {
|
|
7257
|
+
const queued = this.queue.indexOf(pageNumber);
|
|
7258
|
+
if (queued > 0) {
|
|
7259
|
+
this.queue.splice(queued, 1);
|
|
7260
|
+
this.queue.unshift(pageNumber);
|
|
7261
|
+
}
|
|
7262
|
+
return existing.promise;
|
|
7263
|
+
}
|
|
7264
|
+
const request = this.createRequest(pageNumber);
|
|
7265
|
+
this.queue.unshift(pageNumber);
|
|
7266
|
+
this.pump();
|
|
7267
|
+
return request.promise;
|
|
7268
|
+
};
|
|
7269
|
+
createRequest(pageNumber) {
|
|
7270
|
+
let resolve;
|
|
7271
|
+
let reject;
|
|
7272
|
+
const promise = new Promise((res, rej) => {
|
|
7273
|
+
resolve = res;
|
|
7274
|
+
reject = rej;
|
|
7275
|
+
});
|
|
7276
|
+
const request = { promise, resolve, reject };
|
|
7277
|
+
this.requests.set(pageNumber, request);
|
|
7278
|
+
return request;
|
|
7279
|
+
}
|
|
7280
|
+
/** Start after the reader mounts, so unrelated page metadata cannot delay its first render. */
|
|
7281
|
+
start = () => {
|
|
7282
|
+
if (this.disposed) return;
|
|
7283
|
+
for (let pageNumber = 1; pageNumber <= this.document.numPages; pageNumber++) {
|
|
7284
|
+
if (this.pages.has(pageNumber) || this.requests.has(pageNumber)) continue;
|
|
7285
|
+
const request = this.createRequest(pageNumber);
|
|
7286
|
+
void request.promise.catch(() => {
|
|
7287
|
+
});
|
|
7288
|
+
this.queue.push(pageNumber);
|
|
7289
|
+
}
|
|
7290
|
+
this.pump();
|
|
7291
|
+
};
|
|
7292
|
+
loadAll = async () => {
|
|
7293
|
+
if (this.disposed) throw new Error("Document page loading was cancelled");
|
|
7294
|
+
if (this.completePages) return this.completePages;
|
|
7295
|
+
this.start();
|
|
7296
|
+
await Promise.all(
|
|
7297
|
+
Array.from({ length: this.document.numPages }, (_, index) => {
|
|
7298
|
+
const pageNumber = index + 1;
|
|
7299
|
+
return this.pages.get(pageNumber) ?? this.requests.get(pageNumber).promise;
|
|
7300
|
+
})
|
|
7301
|
+
);
|
|
7302
|
+
return this.completePages;
|
|
7303
|
+
};
|
|
7304
|
+
pump() {
|
|
7305
|
+
while (!this.disposed && this.active < this.concurrency && this.queue.length) {
|
|
7306
|
+
const pageNumber = this.queue.shift();
|
|
7307
|
+
const request = this.requests.get(pageNumber);
|
|
7308
|
+
this.active++;
|
|
7309
|
+
void Promise.resolve().then(() => {
|
|
7310
|
+
if (this.disposed)
|
|
7311
|
+
throw new Error("Document page loading was cancelled");
|
|
7312
|
+
return this.document.getPage(pageNumber);
|
|
7313
|
+
}).then((page) => {
|
|
7314
|
+
if (this.disposed) return;
|
|
7315
|
+
const viewport = this.viewportFor(page);
|
|
7316
|
+
this.pages.set(pageNumber, page);
|
|
7317
|
+
this.viewportUpdates.set(pageNumber - 1, viewport);
|
|
7318
|
+
if (this.pages.size === this.document.numPages) {
|
|
7319
|
+
this.completePages = Array.from(
|
|
7320
|
+
{ length: this.document.numPages },
|
|
7321
|
+
(_, index) => this.pages.get(index + 1)
|
|
7322
|
+
);
|
|
7323
|
+
}
|
|
7324
|
+
this.requests.delete(pageNumber);
|
|
7325
|
+
request.resolve(page);
|
|
7326
|
+
this.notify();
|
|
7327
|
+
}).catch((error) => {
|
|
7328
|
+
if (!this.disposed) {
|
|
7329
|
+
this.requests.delete(pageNumber);
|
|
7330
|
+
request.reject(error);
|
|
7331
|
+
this.onError(error);
|
|
7332
|
+
}
|
|
7333
|
+
}).finally(() => {
|
|
7334
|
+
this.active--;
|
|
7335
|
+
this.pump();
|
|
7336
|
+
});
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
notify() {
|
|
7340
|
+
if (this.notification !== void 0) return;
|
|
7341
|
+
this.notification = setTimeout(() => {
|
|
7342
|
+
this.notification = void 0;
|
|
7343
|
+
if (this.disposed) return;
|
|
7344
|
+
this.viewports = this.viewports.slice();
|
|
7345
|
+
for (const [index, viewport] of this.viewportUpdates)
|
|
7346
|
+
this.viewports[index] = viewport;
|
|
7347
|
+
this.viewportUpdates.clear();
|
|
7348
|
+
for (const listener of this.listeners) listener();
|
|
7349
|
+
}, 0);
|
|
7350
|
+
}
|
|
7351
|
+
dispose = () => {
|
|
7352
|
+
this.disposed = true;
|
|
7353
|
+
clearTimeout(this.notification);
|
|
7354
|
+
this.listeners.clear();
|
|
7355
|
+
this.queue = [];
|
|
7356
|
+
for (const request of this.requests.values())
|
|
7357
|
+
request.reject(new Error("Document page loading was cancelled"));
|
|
7358
|
+
this.requests.clear();
|
|
7359
|
+
};
|
|
7360
|
+
};
|
|
7361
|
+
|
|
6934
7362
|
// src/lib/pdfjs.ts
|
|
6935
7363
|
var pdfJsPromise = null;
|
|
6936
7364
|
var loadPdfJs = () => {
|
|
@@ -7056,6 +7484,8 @@ var usePDFDocumentContext = ({
|
|
|
7056
7484
|
onError,
|
|
7057
7485
|
source,
|
|
7058
7486
|
initialRotation = 0,
|
|
7487
|
+
progressive = false,
|
|
7488
|
+
initialPage = 1,
|
|
7059
7489
|
isZoomFitWidth,
|
|
7060
7490
|
zoom = 1,
|
|
7061
7491
|
zoomOptions,
|
|
@@ -7078,7 +7508,32 @@ var usePDFDocumentContext = ({
|
|
|
7078
7508
|
darkModeColorsRef.current = darkModeColors;
|
|
7079
7509
|
useEffect(() => {
|
|
7080
7510
|
let isDisposed = false;
|
|
7511
|
+
let pageResources;
|
|
7081
7512
|
const generateViewports = async (pdf) => {
|
|
7513
|
+
const firstPageNumber = Number.isFinite(initialPage) ? Math.max(1, Math.min(pdf.numPages, Math.trunc(initialPage))) : 1;
|
|
7514
|
+
if (progressive) {
|
|
7515
|
+
const firstPage = await pdf.getPage(firstPageNumber);
|
|
7516
|
+
if (isDisposed) return;
|
|
7517
|
+
pageResources = new PageResources(pdf, firstPage, rotation, (error) => {
|
|
7518
|
+
if (isDisposed) return;
|
|
7519
|
+
console.error("Error generating PDF viewports", error);
|
|
7520
|
+
onErrorRef.current?.({ error, phase: "viewport-generation", source });
|
|
7521
|
+
});
|
|
7522
|
+
setInitialState({
|
|
7523
|
+
isZoomFitWidth,
|
|
7524
|
+
viewports: pageResources.getSnapshot().viewports,
|
|
7525
|
+
pageProxies: pageResources.getSnapshot().pageProxies ?? [],
|
|
7526
|
+
pdfDocumentProxy: pdf,
|
|
7527
|
+
zoom,
|
|
7528
|
+
zoomOptions,
|
|
7529
|
+
pageResources,
|
|
7530
|
+
initialPage: firstPageNumber,
|
|
7531
|
+
colorScheme: colorSchemeRef.current,
|
|
7532
|
+
darkModeColors: darkModeColorsRef.current,
|
|
7533
|
+
renderColorMapRef
|
|
7534
|
+
});
|
|
7535
|
+
return;
|
|
7536
|
+
}
|
|
7082
7537
|
const pageProxies = [];
|
|
7083
7538
|
const viewports = await mapConcurrent(
|
|
7084
7539
|
Array.from({ length: pdf.numPages }, (_, index) => index),
|
|
@@ -7104,6 +7559,7 @@ var usePDFDocumentContext = ({
|
|
|
7104
7559
|
pdfDocumentProxy: pdf,
|
|
7105
7560
|
zoom,
|
|
7106
7561
|
zoomOptions,
|
|
7562
|
+
initialPage: firstPageNumber,
|
|
7107
7563
|
colorScheme: colorSchemeRef.current,
|
|
7108
7564
|
darkModeColors: darkModeColorsRef.current,
|
|
7109
7565
|
renderColorMapRef
|
|
@@ -7166,6 +7622,7 @@ var usePDFDocumentContext = ({
|
|
|
7166
7622
|
});
|
|
7167
7623
|
return () => {
|
|
7168
7624
|
isDisposed = true;
|
|
7625
|
+
pageResources?.dispose();
|
|
7169
7626
|
void loadingTask?.destroy();
|
|
7170
7627
|
};
|
|
7171
7628
|
};
|
|
@@ -7207,6 +7664,28 @@ var ColorSchemeSync = ({
|
|
|
7207
7664
|
]);
|
|
7208
7665
|
return null;
|
|
7209
7666
|
};
|
|
7667
|
+
var PageResourcesSync = ({ resources }) => {
|
|
7668
|
+
const store = PDFStore.useContext();
|
|
7669
|
+
useEffect(() => {
|
|
7670
|
+
if (!resources) return;
|
|
7671
|
+
const update = () => {
|
|
7672
|
+
const snapshot = resources.getSnapshot();
|
|
7673
|
+
store.setState((state) => ({
|
|
7674
|
+
viewports: snapshot.viewports,
|
|
7675
|
+
pageProxies: snapshot.pageProxies ?? state.pageProxies,
|
|
7676
|
+
pagesLoaded: Boolean(snapshot.pageProxies)
|
|
7677
|
+
}));
|
|
7678
|
+
};
|
|
7679
|
+
const unsubscribe = resources.subscribe(update);
|
|
7680
|
+
update();
|
|
7681
|
+
const timer = setTimeout(resources.start, 0);
|
|
7682
|
+
return () => {
|
|
7683
|
+
clearTimeout(timer);
|
|
7684
|
+
unsubscribe();
|
|
7685
|
+
};
|
|
7686
|
+
}, [resources, store]);
|
|
7687
|
+
return null;
|
|
7688
|
+
};
|
|
7210
7689
|
var Root = forwardRef(
|
|
7211
7690
|
({
|
|
7212
7691
|
children,
|
|
@@ -7218,6 +7697,8 @@ var Root = forwardRef(
|
|
|
7218
7697
|
zoom,
|
|
7219
7698
|
zoomOptions,
|
|
7220
7699
|
documentOptions,
|
|
7700
|
+
progressive,
|
|
7701
|
+
initialPage,
|
|
7221
7702
|
colorScheme,
|
|
7222
7703
|
darkModeColors,
|
|
7223
7704
|
...props
|
|
@@ -7230,6 +7711,8 @@ var Root = forwardRef(
|
|
|
7230
7711
|
zoom,
|
|
7231
7712
|
zoomOptions,
|
|
7232
7713
|
documentOptions,
|
|
7714
|
+
progressive,
|
|
7715
|
+
initialPage,
|
|
7233
7716
|
colorScheme,
|
|
7234
7717
|
darkModeColors
|
|
7235
7718
|
});
|
|
@@ -7245,6 +7728,7 @@ var Root = forwardRef(
|
|
|
7245
7728
|
};
|
|
7246
7729
|
}, [documentId]);
|
|
7247
7730
|
return /* @__PURE__ */ jsx(Primitive.div, { ref, ...props, children: initialState ? /* @__PURE__ */ jsxs(PDFStore.Provider, { initialValue: initialState, children: [
|
|
7731
|
+
/* @__PURE__ */ jsx(PageResourcesSync, { resources: initialState.pageResources }),
|
|
7248
7732
|
/* @__PURE__ */ jsx(
|
|
7249
7733
|
ColorSchemeSync,
|
|
7250
7734
|
{
|
|
@@ -7339,7 +7823,10 @@ var Search = ({
|
|
|
7339
7823
|
}) => {
|
|
7340
7824
|
const [result, setResult] = useState(null);
|
|
7341
7825
|
const [attempt, setAttempt] = useState(0);
|
|
7826
|
+
const document2 = usePdf((state) => state.pdfDocumentProxy);
|
|
7342
7827
|
const proxies = usePdf((state) => state.pageProxies);
|
|
7828
|
+
const pagesLoaded = usePdf((state) => state.pagesLoaded);
|
|
7829
|
+
const loadPages = usePdf((state) => state.loadPdfPageProxies);
|
|
7343
7830
|
const setTextContent = usePdf((state) => state.setTextContent);
|
|
7344
7831
|
const retry = useCallback(() => {
|
|
7345
7832
|
setResult(null);
|
|
@@ -7348,22 +7835,27 @@ var Search = ({
|
|
|
7348
7835
|
useEffect(() => {
|
|
7349
7836
|
let disposed = false;
|
|
7350
7837
|
setResult(null);
|
|
7351
|
-
|
|
7352
|
-
void
|
|
7353
|
-
if (disposed) return;
|
|
7838
|
+
let task;
|
|
7839
|
+
void loadPages().then((pages) => {
|
|
7840
|
+
if (disposed || !pagesLoaded) return;
|
|
7841
|
+
task = acquireDocumentText(pages);
|
|
7842
|
+
return task.promise;
|
|
7843
|
+
}).then((text) => {
|
|
7844
|
+
if (disposed || !text) return;
|
|
7354
7845
|
setTextContent(text);
|
|
7355
|
-
setResult({ pages: proxies, status: "ready" });
|
|
7846
|
+
setResult({ document: document2, pages: proxies, status: "ready" });
|
|
7356
7847
|
}).catch((error) => {
|
|
7357
7848
|
if (disposed) return;
|
|
7358
7849
|
console.error("Error extracting PDF text", error);
|
|
7359
|
-
setResult({ pages: proxies, status: "error", error });
|
|
7850
|
+
setResult({ document: document2, pages: proxies, status: "error", error });
|
|
7360
7851
|
});
|
|
7361
7852
|
return () => {
|
|
7362
7853
|
disposed = true;
|
|
7363
|
-
task
|
|
7854
|
+
task?.release();
|
|
7364
7855
|
};
|
|
7365
|
-
}, [proxies, setTextContent, attempt]);
|
|
7366
|
-
if (!result || result.pages !== proxies)
|
|
7856
|
+
}, [document2, proxies, pagesLoaded, loadPages, setTextContent, attempt]);
|
|
7857
|
+
if (!result || result.document !== document2 || result.pages !== proxies)
|
|
7858
|
+
return loading;
|
|
7367
7859
|
if (result.status === "error") {
|
|
7368
7860
|
if (errorFallback) return errorFallback({ error: result.error, retry });
|
|
7369
7861
|
return /* @__PURE__ */ jsxs("div", { role: "alert", children: [
|
|
@@ -7463,7 +7955,7 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
7463
7955
|
isVisible
|
|
7464
7956
|
};
|
|
7465
7957
|
};
|
|
7466
|
-
var
|
|
7958
|
+
var LoadedThumbnail = ({
|
|
7467
7959
|
pageNumber = 1,
|
|
7468
7960
|
eager = false,
|
|
7469
7961
|
...props
|
|
@@ -7472,30 +7964,65 @@ var Thumbnail = ({
|
|
|
7472
7964
|
isFirstPage: eager || pageNumber < 5
|
|
7473
7965
|
});
|
|
7474
7966
|
const { jumpToPage } = usePdfJump();
|
|
7967
|
+
const isCurrentPage = usePdf((state) => state.currentPage === pageNumber);
|
|
7968
|
+
const spacePressed = useRef(false);
|
|
7475
7969
|
return /* @__PURE__ */ jsx("div", { ref: containerRef, style: { minHeight: "150px", minWidth: "10px" }, children: isVisible && /* @__PURE__ */ jsx(
|
|
7476
7970
|
Primitive.canvas,
|
|
7477
7971
|
{
|
|
7972
|
+
"aria-label": `Page ${pageNumber}`,
|
|
7973
|
+
"aria-current": isCurrentPage ? "page" : void 0,
|
|
7478
7974
|
...props,
|
|
7479
7975
|
role: "button",
|
|
7480
7976
|
tabIndex: 0,
|
|
7481
|
-
onClick: (
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
jumpToPage(pageNumber, { behavior: "auto" });
|
|
7977
|
+
onClick: (event) => {
|
|
7978
|
+
props.onClick?.(event);
|
|
7979
|
+
if (!event.defaultPrevented)
|
|
7980
|
+
jumpToPage(pageNumber, { behavior: "auto" });
|
|
7486
7981
|
},
|
|
7487
|
-
onKeyDown: (
|
|
7488
|
-
|
|
7489
|
-
|
|
7982
|
+
onKeyDown: (event) => {
|
|
7983
|
+
props.onKeyDown?.(event);
|
|
7984
|
+
if (event.defaultPrevented) return;
|
|
7985
|
+
if (event.key === "Enter") {
|
|
7986
|
+
event.preventDefault();
|
|
7987
|
+
jumpToPage(pageNumber, { behavior: "auto" });
|
|
7988
|
+
} else if (event.key === " ") {
|
|
7989
|
+
event.preventDefault();
|
|
7990
|
+
spacePressed.current = true;
|
|
7490
7991
|
}
|
|
7491
|
-
|
|
7992
|
+
},
|
|
7993
|
+
onKeyUp: (event) => {
|
|
7994
|
+
props.onKeyUp?.(event);
|
|
7995
|
+
if (event.key !== " ") return;
|
|
7996
|
+
const activate = spacePressed.current && !event.defaultPrevented;
|
|
7997
|
+
spacePressed.current = false;
|
|
7998
|
+
if (activate) {
|
|
7999
|
+
event.preventDefault();
|
|
7492
8000
|
jumpToPage(pageNumber, { behavior: "auto" });
|
|
7493
8001
|
}
|
|
7494
8002
|
},
|
|
8003
|
+
onBlur: (event) => {
|
|
8004
|
+
spacePressed.current = false;
|
|
8005
|
+
props.onBlur?.(event);
|
|
8006
|
+
},
|
|
7495
8007
|
ref: canvasRef
|
|
7496
8008
|
}
|
|
7497
8009
|
) });
|
|
7498
8010
|
};
|
|
8011
|
+
var Thumbnail = (props) => /* @__PURE__ */ jsx(
|
|
8012
|
+
PageLoadingBoundary,
|
|
8013
|
+
{
|
|
8014
|
+
pageNumber: props.pageNumber ?? 1,
|
|
8015
|
+
fallback: /* @__PURE__ */ jsx(
|
|
8016
|
+
"div",
|
|
8017
|
+
{
|
|
8018
|
+
"aria-label": `Loading page ${props.pageNumber ?? 1}`,
|
|
8019
|
+
"aria-busy": "true",
|
|
8020
|
+
style: { minHeight: 150 }
|
|
8021
|
+
}
|
|
8022
|
+
),
|
|
8023
|
+
children: /* @__PURE__ */ jsx(LoadedThumbnail, { ...props })
|
|
8024
|
+
}
|
|
8025
|
+
);
|
|
7499
8026
|
function VirtualizedThumbnails({
|
|
7500
8027
|
children,
|
|
7501
8028
|
count,
|
|
@@ -7508,6 +8035,9 @@ function VirtualizedThumbnails({
|
|
|
7508
8035
|
const containerRef = useRef(null);
|
|
7509
8036
|
useImperativeHandle(ref, () => containerRef.current);
|
|
7510
8037
|
const [focusPage, setFocusPage] = useState(null);
|
|
8038
|
+
const focusPageLoaded = usePdf(
|
|
8039
|
+
(state) => focusPage === null ? false : state.isPageLoaded(focusPage)
|
|
8040
|
+
);
|
|
7511
8041
|
const itemHeight = Number.isFinite(virtualize.itemHeight) ? Math.max(1, virtualize.itemHeight) : 150;
|
|
7512
8042
|
const virtualizer = useVirtualizer({
|
|
7513
8043
|
count,
|
|
@@ -7525,7 +8055,7 @@ function VirtualizedThumbnails({
|
|
|
7525
8055
|
canvas.focus({ preventScroll: true });
|
|
7526
8056
|
setFocusPage(null);
|
|
7527
8057
|
}
|
|
7528
|
-
}, [focusPage, items]);
|
|
8058
|
+
}, [focusPage, focusPageLoaded, items]);
|
|
7529
8059
|
return /* @__PURE__ */ jsx(
|
|
7530
8060
|
Primitive.div,
|
|
7531
8061
|
{
|
|
@@ -7658,12 +8188,42 @@ var CurrentZoom = ({ ...props }) => {
|
|
|
7658
8188
|
// src/lib/search.ts
|
|
7659
8189
|
var normalizedPages = /* @__PURE__ */ new WeakMap();
|
|
7660
8190
|
function normalize(page) {
|
|
7661
|
-
|
|
7662
|
-
if (
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
8191
|
+
const cached = normalizedPages.get(page);
|
|
8192
|
+
if (cached?.text === page.text) return cached;
|
|
8193
|
+
const lower = page.text.toLowerCase();
|
|
8194
|
+
const result = { text: page.text, lower };
|
|
8195
|
+
if (lower.length !== page.text.length) {
|
|
8196
|
+
result.starts = new Uint32Array(lower.length);
|
|
8197
|
+
result.ends = new Uint32Array(lower.length);
|
|
8198
|
+
let original = 0;
|
|
8199
|
+
let normalized = 0;
|
|
8200
|
+
for (const character of page.text) {
|
|
8201
|
+
const length = character.toLowerCase().length;
|
|
8202
|
+
for (let i = 0; i < length; i++) {
|
|
8203
|
+
result.starts[normalized + i] = original + (length === character.length ? i : 0);
|
|
8204
|
+
result.ends[normalized + i] = original + (length === character.length ? i + 1 : character.length);
|
|
8205
|
+
}
|
|
8206
|
+
original += character.length;
|
|
8207
|
+
normalized += length;
|
|
8208
|
+
}
|
|
8209
|
+
}
|
|
8210
|
+
normalizedPages.set(page, result);
|
|
8211
|
+
return result;
|
|
8212
|
+
}
|
|
8213
|
+
function createMatch(page, normalized, start, length, searchText, textSize, score) {
|
|
8214
|
+
const end = Math.min(normalized.lower.length, start + length);
|
|
8215
|
+
const matchIndex = normalized.starts?.[start] ?? start;
|
|
8216
|
+
const matchEnd = normalized.ends?.[end - 1] ?? end;
|
|
8217
|
+
const matchLength = matchEnd - matchIndex;
|
|
8218
|
+
return {
|
|
8219
|
+
pageNumber: page.pageNumber,
|
|
8220
|
+
text: page.text.slice(matchIndex, matchEnd + textSize),
|
|
8221
|
+
score,
|
|
8222
|
+
matchIndex,
|
|
8223
|
+
isExactMatch: score === 1,
|
|
8224
|
+
searchText,
|
|
8225
|
+
...matchLength !== searchText.length ? { matchLength } : {}
|
|
8226
|
+
};
|
|
7667
8227
|
}
|
|
7668
8228
|
function createBoundedDistance(query, maxDistance) {
|
|
7669
8229
|
const length = query.length;
|
|
@@ -7726,7 +8286,8 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
7726
8286
|
let steps = 0;
|
|
7727
8287
|
let deadline = cooperative ? performance.now() + budget : 0;
|
|
7728
8288
|
for (const page of pages) {
|
|
7729
|
-
const
|
|
8289
|
+
const normalized = normalize(page);
|
|
8290
|
+
const lower = normalized.lower;
|
|
7730
8291
|
const exactStarts = [];
|
|
7731
8292
|
let index = 0;
|
|
7732
8293
|
while (true) {
|
|
@@ -7739,16 +8300,19 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
7739
8300
|
exactCount++;
|
|
7740
8301
|
if (maxDistance > 0) exactStarts.push(matchIndex);
|
|
7741
8302
|
if (exactMatches.length < limit) {
|
|
7742
|
-
exactMatches.push(
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
8303
|
+
exactMatches.push(
|
|
8304
|
+
createMatch(
|
|
8305
|
+
page,
|
|
8306
|
+
normalized,
|
|
8307
|
+
matchIndex,
|
|
8308
|
+
query.length,
|
|
8309
|
+
searchText,
|
|
8310
|
+
textSize,
|
|
8311
|
+
1
|
|
8312
|
+
)
|
|
8313
|
+
);
|
|
7750
8314
|
}
|
|
7751
|
-
index = matchIndex +
|
|
8315
|
+
index = matchIndex + query.length;
|
|
7752
8316
|
}
|
|
7753
8317
|
if (!(maxDistance > 0)) continue;
|
|
7754
8318
|
index = 0;
|
|
@@ -7758,11 +8322,11 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
7758
8322
|
yield;
|
|
7759
8323
|
deadline = performance.now() + budget;
|
|
7760
8324
|
}
|
|
7761
|
-
while (exactIndex < exactStarts.length && exactStarts[exactIndex] +
|
|
8325
|
+
while (exactIndex < exactStarts.length && exactStarts[exactIndex] + query.length <= index)
|
|
7762
8326
|
exactIndex++;
|
|
7763
8327
|
const exactStart = exactStarts[exactIndex];
|
|
7764
8328
|
if (exactStart !== void 0 && index >= exactStart) {
|
|
7765
|
-
index = exactStart +
|
|
8329
|
+
index = exactStart + query.length;
|
|
7766
8330
|
continue;
|
|
7767
8331
|
}
|
|
7768
8332
|
const edits = distance(lower, index);
|
|
@@ -7772,14 +8336,15 @@ function* runSearch(pages, searchText, options = {}, cooperative = false, budget
|
|
|
7772
8336
|
if (fuzzyMatches.length < limit || score > (fuzzyMatches[limit - 1]?.score ?? Infinity)) {
|
|
7773
8337
|
insertMatch(
|
|
7774
8338
|
fuzzyMatches,
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
8339
|
+
createMatch(
|
|
8340
|
+
page,
|
|
8341
|
+
normalized,
|
|
8342
|
+
index,
|
|
8343
|
+
query.length,
|
|
8344
|
+
searchText,
|
|
8345
|
+
textSize,
|
|
8346
|
+
score
|
|
8347
|
+
),
|
|
7783
8348
|
limit
|
|
7784
8349
|
);
|
|
7785
8350
|
}
|
|
@@ -7885,32 +8450,37 @@ var useSearch = () => {
|
|
|
7885
8450
|
|
|
7886
8451
|
// src/hooks/search/useSearchPosition.tsx
|
|
7887
8452
|
async function calculateHighlightRects(pageProxy, textMatch) {
|
|
7888
|
-
const matchLength = textMatch.searchText ? textMatch.searchText.length : textMatch.text.length;
|
|
7889
|
-
if (matchLength
|
|
8453
|
+
const matchLength = textMatch.matchLength ?? (textMatch.searchText ? textMatch.searchText.length : textMatch.text.length);
|
|
8454
|
+
if (!Number.isFinite(matchLength) || matchLength <= 0 || !Number.isFinite(textMatch.matchIndex) || textMatch.matchIndex < 0)
|
|
7890
8455
|
return [];
|
|
7891
8456
|
const matchEnd = textMatch.matchIndex + matchLength;
|
|
7892
8457
|
const reader = pageProxy.streamTextContent().getReader();
|
|
7893
8458
|
const matchRects = [];
|
|
7894
8459
|
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
8460
|
+
const styles = {};
|
|
7895
8461
|
let offset3 = 0;
|
|
7896
8462
|
let foundEnd = false;
|
|
7897
8463
|
try {
|
|
7898
8464
|
read: while (true) {
|
|
7899
8465
|
const { value, done } = await reader.read();
|
|
7900
8466
|
if (done) break;
|
|
8467
|
+
Object.assign(styles, value.styles);
|
|
7901
8468
|
for (const item of value.items) {
|
|
7902
8469
|
if (!("str" in item) || item.str.length === 0) continue;
|
|
7903
8470
|
const length = item.str.length;
|
|
7904
8471
|
const start = Math.max(0, textMatch.matchIndex - offset3);
|
|
7905
8472
|
const end = Math.min(length, matchEnd - offset3);
|
|
7906
|
-
if (end > start) {
|
|
7907
|
-
matchRects.push(
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
8473
|
+
if (end > start && item.str.slice(start, end).trim()) {
|
|
8474
|
+
matchRects.push(
|
|
8475
|
+
getItemRect(
|
|
8476
|
+
item,
|
|
8477
|
+
styles[item.fontName],
|
|
8478
|
+
viewport,
|
|
8479
|
+
start,
|
|
8480
|
+
end,
|
|
8481
|
+
textMatch.pageNumber
|
|
8482
|
+
)
|
|
8483
|
+
);
|
|
7914
8484
|
}
|
|
7915
8485
|
offset3 += length;
|
|
7916
8486
|
if (offset3 >= matchEnd) {
|
|
@@ -7927,6 +8497,61 @@ async function calculateHighlightRects(pageProxy, textMatch) {
|
|
|
7927
8497
|
}
|
|
7928
8498
|
return mergeAdjacentRects(matchRects);
|
|
7929
8499
|
}
|
|
8500
|
+
var measurementContext;
|
|
8501
|
+
function getTextFractions(item, style, start, end) {
|
|
8502
|
+
const length = item.str.length;
|
|
8503
|
+
let from = start / length;
|
|
8504
|
+
let to = end / length;
|
|
8505
|
+
if ((start > 0 || end < length) && style?.fontFamily && !style.vertical && typeof document !== "undefined") {
|
|
8506
|
+
if (measurementContext === void 0) {
|
|
8507
|
+
const canvas = document.createElement("canvas");
|
|
8508
|
+
canvas.width = canvas.height = 0;
|
|
8509
|
+
measurementContext = canvas.getContext("2d");
|
|
8510
|
+
}
|
|
8511
|
+
if (measurementContext) {
|
|
8512
|
+
measurementContext.font = `100px ${style.fontFamily}`;
|
|
8513
|
+
const width = measurementContext.measureText(item.str).width;
|
|
8514
|
+
if (width > 0) {
|
|
8515
|
+
from = start === 0 ? 0 : measurementContext.measureText(item.str.slice(0, start)).width / width;
|
|
8516
|
+
to = end === length ? 1 : measurementContext.measureText(item.str.slice(0, end)).width / width;
|
|
8517
|
+
}
|
|
8518
|
+
}
|
|
8519
|
+
}
|
|
8520
|
+
return item.dir === "rtl" ? [1 - to, 1 - from] : [from, to];
|
|
8521
|
+
}
|
|
8522
|
+
function getItemRect(item, style, viewport, start, end, pageNumber) {
|
|
8523
|
+
const angle = Math.atan2(item.transform[1], item.transform[0]) - (style?.vertical ? Math.PI / 2 : 0);
|
|
8524
|
+
const cosine = Math.cos(angle);
|
|
8525
|
+
const sine = Math.sin(angle);
|
|
8526
|
+
const height = Math.hypot(item.transform[2], item.transform[3]);
|
|
8527
|
+
const ascent = (style?.ascent ?? (style?.descent ? 1 + style.descent : 1)) * height;
|
|
8528
|
+
const width = style?.vertical ? item.height : item.width;
|
|
8529
|
+
const [startFraction, endFraction] = getTextFractions(
|
|
8530
|
+
item,
|
|
8531
|
+
style,
|
|
8532
|
+
start,
|
|
8533
|
+
end
|
|
8534
|
+
);
|
|
8535
|
+
const from = startFraction * width;
|
|
8536
|
+
const to = endFraction * width;
|
|
8537
|
+
let left = Infinity;
|
|
8538
|
+
let top = Infinity;
|
|
8539
|
+
let right = -Infinity;
|
|
8540
|
+
let bottom = -Infinity;
|
|
8541
|
+
for (const along of [from, to]) {
|
|
8542
|
+
for (const above of [ascent, ascent - height]) {
|
|
8543
|
+
const [x, y] = viewport.convertToViewportPoint(
|
|
8544
|
+
item.transform[4] + along * cosine - above * sine,
|
|
8545
|
+
item.transform[5] + along * sine + above * cosine
|
|
8546
|
+
);
|
|
8547
|
+
left = Math.min(left, x);
|
|
8548
|
+
top = Math.min(top, y);
|
|
8549
|
+
right = Math.max(right, x);
|
|
8550
|
+
bottom = Math.max(bottom, y);
|
|
8551
|
+
}
|
|
8552
|
+
}
|
|
8553
|
+
return { pageNumber, left, top, width: right - left, height: bottom - top };
|
|
8554
|
+
}
|
|
7930
8555
|
function mergeAdjacentRects(rects) {
|
|
7931
8556
|
if (rects.length <= 1) return rects;
|
|
7932
8557
|
const merged = [];
|
|
@@ -7935,10 +8560,13 @@ function mergeAdjacentRects(rects) {
|
|
|
7935
8560
|
for (let i = 1; i < rects.length; i++) {
|
|
7936
8561
|
const next = rects[i];
|
|
7937
8562
|
if (!next) continue;
|
|
7938
|
-
if (Math.abs(current.top - next.top) < 2 && Math.abs(current.height - next.height) < 2) {
|
|
8563
|
+
if (Math.abs(current.top - next.top) < 2 && Math.abs(current.height - next.height) < 2 && Math.max(current.left, next.left) <= Math.min(current.left + current.width, next.left + next.width) + 2) {
|
|
7939
8564
|
current = {
|
|
7940
8565
|
...current,
|
|
7941
|
-
|
|
8566
|
+
left: Math.min(current.left, next.left),
|
|
8567
|
+
top: Math.min(current.top, next.top),
|
|
8568
|
+
width: Math.max(current.left + current.width, next.left + next.width) - Math.min(current.left, next.left),
|
|
8569
|
+
height: Math.max(current.top + current.height, next.top + next.height) - Math.min(current.top, next.top)
|
|
7942
8570
|
};
|
|
7943
8571
|
} else {
|
|
7944
8572
|
merged.push(current);
|