@anaralabs/lector 3.14.10 → 3.14.11
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 +47 -70
- package/dist/_tsup-dts-rollup.d.ts +71 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.js +627 -327
- package/esm-only.cjs +8 -0
- package/package.json +11 -4
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext, memo, forwardRef, useMemo, cloneElement, useEffect, useRef, useState, useCallback, useContext, useLayoutEffect, createRef } from 'react';
|
|
3
|
+
import React, { createContext, memo, forwardRef, useMemo, cloneElement, useEffect, useRef, useState, useCallback, useContext, useLayoutEffect, createRef, useSyncExternalStore, 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';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
import { useShallow } from 'zustand/react/shallow';
|
|
8
9
|
import { useDebounce } from 'use-debounce';
|
|
9
10
|
import { v4 } from 'uuid';
|
|
10
11
|
import { Slot } from '@radix-ui/react-slot';
|
|
@@ -265,12 +266,12 @@ var PDFStore = createZustandContext(
|
|
|
265
266
|
updateZoom: (zoom, isZoomFitWidth = false) => {
|
|
266
267
|
const { minZoom, maxZoom } = get().zoomOptions;
|
|
267
268
|
set((state) => {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return { zoom: newZoom, isZoomFitWidth };
|
|
269
|
+
const newZoom = clamp(
|
|
270
|
+
typeof zoom === "function" ? zoom(state.zoom) : zoom,
|
|
271
|
+
minZoom,
|
|
272
|
+
maxZoom
|
|
273
|
+
);
|
|
274
|
+
return newZoom === state.zoom && isZoomFitWidth === state.isZoomFitWidth ? state : { zoom: newZoom, isZoomFitWidth };
|
|
274
275
|
});
|
|
275
276
|
},
|
|
276
277
|
zoomFitWidth: () => {
|
|
@@ -281,23 +282,20 @@ var PDFStore = createZustandContext(
|
|
|
281
282
|
viewports,
|
|
282
283
|
zoomOptions
|
|
283
284
|
);
|
|
284
|
-
|
|
285
|
-
zoom: clampedZoom,
|
|
286
|
-
isZoomFitWidth: true
|
|
287
|
-
});
|
|
285
|
+
get().updateZoom(clampedZoom, true);
|
|
288
286
|
return clampedZoom;
|
|
289
287
|
},
|
|
290
288
|
currentPage: 1,
|
|
291
289
|
setCurrentPage: (val) => {
|
|
292
|
-
set(
|
|
293
|
-
currentPage: val
|
|
294
|
-
|
|
290
|
+
set(
|
|
291
|
+
(state) => state.currentPage === val ? state : { currentPage: val }
|
|
292
|
+
);
|
|
295
293
|
},
|
|
296
294
|
isPinching: false,
|
|
297
295
|
setIsPinching: (val) => {
|
|
298
|
-
set(
|
|
299
|
-
isPinching: val
|
|
300
|
-
|
|
296
|
+
set(
|
|
297
|
+
(state) => state.isPinching === val ? state : { isPinching: val }
|
|
298
|
+
);
|
|
301
299
|
},
|
|
302
300
|
isResizing: false,
|
|
303
301
|
setIsResizing: (val) => {
|
|
@@ -307,9 +305,9 @@ var PDFStore = createZustandContext(
|
|
|
307
305
|
},
|
|
308
306
|
virtualizer: null,
|
|
309
307
|
setVirtualizer: (val) => {
|
|
310
|
-
set(
|
|
311
|
-
virtualizer: val
|
|
312
|
-
|
|
308
|
+
set(
|
|
309
|
+
(state) => state.virtualizer === val ? state : { virtualizer: val }
|
|
310
|
+
);
|
|
313
311
|
},
|
|
314
312
|
pageProxies: initialState.pageProxies,
|
|
315
313
|
getPdfPageProxy: (pageNumber) => {
|
|
@@ -319,21 +317,21 @@ var PDFStore = createZustandContext(
|
|
|
319
317
|
},
|
|
320
318
|
textContent: [],
|
|
321
319
|
setTextContent: (val) => {
|
|
322
|
-
set(
|
|
323
|
-
textContent: val
|
|
324
|
-
|
|
320
|
+
set(
|
|
321
|
+
(state) => state.textContent === val ? state : { textContent: val }
|
|
322
|
+
);
|
|
325
323
|
},
|
|
326
324
|
highlights: [],
|
|
327
325
|
setHighlight: (val) => {
|
|
328
|
-
set(
|
|
329
|
-
highlights: val
|
|
330
|
-
|
|
326
|
+
set(
|
|
327
|
+
(state) => state.highlights === val ? state : { highlights: val }
|
|
328
|
+
);
|
|
331
329
|
},
|
|
332
330
|
customSelectionRects: [],
|
|
333
331
|
setCustomSelectionRects: (val) => {
|
|
334
|
-
set(
|
|
335
|
-
customSelectionRects: val
|
|
336
|
-
|
|
332
|
+
set(
|
|
333
|
+
(state) => state.customSelectionRects === val ? state : { customSelectionRects: val }
|
|
334
|
+
);
|
|
337
335
|
},
|
|
338
336
|
coloredHighlights: [],
|
|
339
337
|
addColoredHighlight: (value) => set((prevState) => ({
|
|
@@ -700,10 +698,43 @@ var AnnotationsStoreProvider = ({
|
|
|
700
698
|
{ initialValue: void 0 },
|
|
701
699
|
children
|
|
702
700
|
);
|
|
703
|
-
var
|
|
701
|
+
var identity = (state) => state;
|
|
702
|
+
function useAnnotations(selector) {
|
|
704
703
|
const ctx = AnnotationsStore.useContext();
|
|
705
|
-
return useStore(
|
|
706
|
-
|
|
704
|
+
return useStore(
|
|
705
|
+
ctx ?? getFallbackStore(),
|
|
706
|
+
selector ?? identity
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
var emptyAnnotations = [];
|
|
710
|
+
var indexes = /* @__PURE__ */ new WeakMap();
|
|
711
|
+
function annotationsByPage(annotations) {
|
|
712
|
+
let index = indexes.get(annotations);
|
|
713
|
+
if (index) return index;
|
|
714
|
+
index = /* @__PURE__ */ new Map();
|
|
715
|
+
for (const annotation of annotations) {
|
|
716
|
+
const pages = /* @__PURE__ */ new Set([annotation.pageNumber]);
|
|
717
|
+
for (const rect of annotation.highlights) pages.add(rect.pageNumber);
|
|
718
|
+
for (const rect of annotation.underlines ?? []) pages.add(rect.pageNumber);
|
|
719
|
+
for (const page of pages) {
|
|
720
|
+
let values = index.get(page);
|
|
721
|
+
if (!values) {
|
|
722
|
+
values = [];
|
|
723
|
+
index.set(page, values);
|
|
724
|
+
}
|
|
725
|
+
values.push(annotation);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
indexes.set(annotations, index);
|
|
729
|
+
return index;
|
|
730
|
+
}
|
|
731
|
+
function usePageAnnotations(pageNumber) {
|
|
732
|
+
return useAnnotations(
|
|
733
|
+
useShallow(
|
|
734
|
+
(state) => annotationsByPage(state.annotations).get(pageNumber) ?? emptyAnnotations
|
|
735
|
+
)
|
|
736
|
+
);
|
|
737
|
+
}
|
|
707
738
|
var PDFPageNumberContext = createContext(0);
|
|
708
739
|
var usePDFPageNumber = () => {
|
|
709
740
|
return useContext(PDFPageNumberContext);
|
|
@@ -725,15 +756,9 @@ var AnnotationHighlightLayer = ({
|
|
|
725
756
|
onAnnotationTooltipClose,
|
|
726
757
|
hoverTooltipClassName
|
|
727
758
|
}) => {
|
|
728
|
-
const { annotations } = useAnnotations();
|
|
729
759
|
const pageNumber = usePDFPageNumber();
|
|
730
760
|
const isPageRendered = usePdf((state) => !!state.renderedPages[pageNumber]);
|
|
731
|
-
const pageAnnotations =
|
|
732
|
-
() => annotations.filter(
|
|
733
|
-
(a) => a.pageNumber === pageNumber || a.highlights.some((h) => h.pageNumber === pageNumber) || a.underlines?.some((u) => u.pageNumber === pageNumber)
|
|
734
|
-
),
|
|
735
|
-
[annotations, pageNumber]
|
|
736
|
-
);
|
|
761
|
+
const pageAnnotations = usePageAnnotations(pageNumber);
|
|
737
762
|
if (!isPageRendered) return null;
|
|
738
763
|
const getCommentIconPosition = (highlights) => {
|
|
739
764
|
if (!highlights.length) return { top: 0, right: 10 };
|
|
@@ -1220,20 +1245,42 @@ var PDFLinkServiceContext = createContext(defaultLinkService);
|
|
|
1220
1245
|
var usePDFLinkService = () => {
|
|
1221
1246
|
return useContext(PDFLinkServiceContext);
|
|
1222
1247
|
};
|
|
1248
|
+
var targets = /* @__PURE__ */ new Map();
|
|
1249
|
+
var observer;
|
|
1223
1250
|
var useVisibility = ({
|
|
1224
1251
|
elementRef
|
|
1225
1252
|
}) => {
|
|
1226
1253
|
const [visible, setVisible] = useState(false);
|
|
1227
1254
|
useEffect(() => {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1255
|
+
const element = elementRef.current;
|
|
1256
|
+
if (!element) return;
|
|
1257
|
+
observer ??= new IntersectionObserver((entries) => {
|
|
1258
|
+
for (const entry of entries) {
|
|
1259
|
+
const target2 = targets.get(entry.target);
|
|
1260
|
+
if (!target2) continue;
|
|
1261
|
+
target2.visible = entry.isIntersecting;
|
|
1262
|
+
for (const listener of target2.listeners) listener(entry.isIntersecting);
|
|
1263
|
+
}
|
|
1233
1264
|
});
|
|
1234
|
-
|
|
1265
|
+
let target = targets.get(element);
|
|
1266
|
+
if (!target) {
|
|
1267
|
+
target = { listeners: /* @__PURE__ */ new Set(), visible: false };
|
|
1268
|
+
targets.set(element, target);
|
|
1269
|
+
observer.observe(element);
|
|
1270
|
+
}
|
|
1271
|
+
const { listeners: listeners2 } = target;
|
|
1272
|
+
listeners2.add(setVisible);
|
|
1273
|
+
setVisible(target.visible);
|
|
1235
1274
|
return () => {
|
|
1236
|
-
|
|
1275
|
+
listeners2.delete(setVisible);
|
|
1276
|
+
if (listeners2.size === 0) {
|
|
1277
|
+
observer?.unobserve(element);
|
|
1278
|
+
targets.delete(element);
|
|
1279
|
+
}
|
|
1280
|
+
if (targets.size === 0) {
|
|
1281
|
+
observer?.disconnect();
|
|
1282
|
+
observer = void 0;
|
|
1283
|
+
}
|
|
1237
1284
|
};
|
|
1238
1285
|
}, [elementRef]);
|
|
1239
1286
|
return { visible };
|
|
@@ -2061,52 +2108,41 @@ function applyContextRecolor(ctx, map, options) {
|
|
|
2061
2108
|
function removeContextRecolor(ctx) {
|
|
2062
2109
|
ctx[RECOLOR_CLEANUP]?.();
|
|
2063
2110
|
}
|
|
2064
|
-
var
|
|
2065
|
-
var
|
|
2066
|
-
var
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2111
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
2112
|
+
var media;
|
|
2113
|
+
var getSnapshot = () => Math.min(window.devicePixelRatio || 1, 3);
|
|
2114
|
+
var getServerSnapshot = () => 1;
|
|
2115
|
+
function unlisten() {
|
|
2116
|
+
if (!media) return;
|
|
2117
|
+
if (typeof media.removeEventListener === "function")
|
|
2118
|
+
media.removeEventListener("change", onChange);
|
|
2119
|
+
else media.removeListener(onChange);
|
|
2120
|
+
}
|
|
2121
|
+
function watchResolution() {
|
|
2122
|
+
unlisten();
|
|
2123
|
+
media = window.matchMedia(
|
|
2124
|
+
`(resolution: ${window.devicePixelRatio || 1}dppx), (-webkit-device-pixel-ratio: ${window.devicePixelRatio || 1})`
|
|
2125
|
+
);
|
|
2126
|
+
if (typeof media.addEventListener === "function")
|
|
2127
|
+
media.addEventListener("change", onChange);
|
|
2128
|
+
else media.addListener(onChange);
|
|
2129
|
+
}
|
|
2130
|
+
function onChange() {
|
|
2131
|
+
watchResolution();
|
|
2132
|
+
for (const listener of listeners) listener();
|
|
2133
|
+
}
|
|
2134
|
+
function subscribe(listener) {
|
|
2135
|
+
listeners.add(listener);
|
|
2136
|
+
if (listeners.size === 1) watchResolution();
|
|
2137
|
+
return () => {
|
|
2138
|
+
listeners.delete(listener);
|
|
2139
|
+
if (listeners.size === 0) {
|
|
2140
|
+
unlisten();
|
|
2141
|
+
media = void 0;
|
|
2071
2142
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
if (cancelled) {
|
|
2076
|
-
return;
|
|
2077
|
-
}
|
|
2078
|
-
setDpr(readDpr());
|
|
2079
|
-
subscribe();
|
|
2080
|
-
};
|
|
2081
|
-
const unlisten = (mql) => {
|
|
2082
|
-
if (!mql) return;
|
|
2083
|
-
if (typeof mql.removeEventListener === "function") {
|
|
2084
|
-
mql.removeEventListener("change", handleChange);
|
|
2085
|
-
} else {
|
|
2086
|
-
mql.removeListener(handleChange);
|
|
2087
|
-
}
|
|
2088
|
-
};
|
|
2089
|
-
const subscribe = () => {
|
|
2090
|
-
unlisten(mediaQuery);
|
|
2091
|
-
const ratio = window.devicePixelRatio || 1;
|
|
2092
|
-
mediaQuery = window.matchMedia(
|
|
2093
|
-
`(resolution: ${ratio}dppx), (-webkit-device-pixel-ratio: ${ratio})`
|
|
2094
|
-
);
|
|
2095
|
-
if (typeof mediaQuery.addEventListener === "function") {
|
|
2096
|
-
mediaQuery.addEventListener("change", handleChange, { once: true });
|
|
2097
|
-
} else {
|
|
2098
|
-
mediaQuery.addListener(handleChange);
|
|
2099
|
-
}
|
|
2100
|
-
};
|
|
2101
|
-
setDpr(readDpr());
|
|
2102
|
-
subscribe();
|
|
2103
|
-
return () => {
|
|
2104
|
-
cancelled = true;
|
|
2105
|
-
unlisten(mediaQuery);
|
|
2106
|
-
};
|
|
2107
|
-
}, []);
|
|
2108
|
-
return dpr;
|
|
2109
|
-
};
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
var useDpr = () => useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
2110
2146
|
|
|
2111
2147
|
// src/hooks/layers/useCanvasLayer.tsx
|
|
2112
2148
|
var CACHE_MAX_ENTRIES = 60;
|
|
@@ -4492,9 +4528,9 @@ var useVisiblePage = ({
|
|
|
4492
4528
|
window.addEventListener("resize", measure);
|
|
4493
4529
|
return () => window.removeEventListener("resize", measure);
|
|
4494
4530
|
}
|
|
4495
|
-
const
|
|
4496
|
-
|
|
4497
|
-
return () =>
|
|
4531
|
+
const observer2 = new ResizeObserver(measure);
|
|
4532
|
+
observer2.observe(scrollElement);
|
|
4533
|
+
return () => observer2.disconnect();
|
|
4498
4534
|
}, [scrollElement]);
|
|
4499
4535
|
const calculateVisiblePageIndex = useCallback(
|
|
4500
4536
|
(virtualItems) => {
|
|
@@ -5051,7 +5087,7 @@ var CoordinatesEngine = class extends Engine {
|
|
|
5051
5087
|
}
|
|
5052
5088
|
}
|
|
5053
5089
|
};
|
|
5054
|
-
var
|
|
5090
|
+
var identity2 = (v) => v;
|
|
5055
5091
|
var DEFAULT_RUBBERBAND = 0.15;
|
|
5056
5092
|
var commonConfigResolver = {
|
|
5057
5093
|
enabled(value = true) {
|
|
@@ -5084,7 +5120,7 @@ var commonConfigResolver = {
|
|
|
5084
5120
|
const transform = value || config.shared.transform;
|
|
5085
5121
|
this.hasCustomTransform = !!transform;
|
|
5086
5122
|
if (process.env.NODE_ENV === "development") {
|
|
5087
|
-
const originalTransform = transform ||
|
|
5123
|
+
const originalTransform = transform || identity2;
|
|
5088
5124
|
return (v) => {
|
|
5089
5125
|
const r2 = originalTransform(v);
|
|
5090
5126
|
if (!isFinite(r2[0]) || !isFinite(r2[1])) {
|
|
@@ -5093,7 +5129,7 @@ var commonConfigResolver = {
|
|
|
5093
5129
|
return r2;
|
|
5094
5130
|
};
|
|
5095
5131
|
}
|
|
5096
|
-
return transform ||
|
|
5132
|
+
return transform || identity2;
|
|
5097
5133
|
},
|
|
5098
5134
|
threshold(value) {
|
|
5099
5135
|
return V.toVector(value, 0);
|
|
@@ -6167,16 +6203,16 @@ var EventStore = class {
|
|
|
6167
6203
|
this._gestureKey = gestureKey;
|
|
6168
6204
|
}
|
|
6169
6205
|
add(element, device, action, handler, options) {
|
|
6170
|
-
const
|
|
6206
|
+
const listeners2 = this._listeners;
|
|
6171
6207
|
const type = toDomEventType(device, action);
|
|
6172
6208
|
const _options = this._gestureKey ? this._ctrl.config[this._gestureKey].eventOptions : {};
|
|
6173
6209
|
const eventOptions = _objectSpread2(_objectSpread2({}, _options), options);
|
|
6174
6210
|
element.addEventListener(type, handler, eventOptions);
|
|
6175
6211
|
const remove = () => {
|
|
6176
6212
|
element.removeEventListener(type, handler, eventOptions);
|
|
6177
|
-
|
|
6213
|
+
listeners2.delete(remove);
|
|
6178
6214
|
};
|
|
6179
|
-
|
|
6215
|
+
listeners2.add(remove);
|
|
6180
6216
|
return remove;
|
|
6181
6217
|
}
|
|
6182
6218
|
clean() {
|
|
@@ -6661,7 +6697,6 @@ var useViewportContainer = ({
|
|
|
6661
6697
|
origin: originRef.current
|
|
6662
6698
|
};
|
|
6663
6699
|
};
|
|
6664
|
-
var selectLargestPageWidth = (state) => state.viewports.reduce((max, vp) => Math.max(max, vp.width), 0);
|
|
6665
6700
|
var DEFAULT_HEIGHT = 600;
|
|
6666
6701
|
var EXTRA_HEIGHT = 0;
|
|
6667
6702
|
var DEFAULT_VIRTUALIZER_OPTIONS = { overscan: 1 };
|
|
@@ -6776,7 +6811,10 @@ var Pages = ({
|
|
|
6776
6811
|
scrollOffset: virtualizer.scrollOffset ?? null
|
|
6777
6812
|
});
|
|
6778
6813
|
useFitWidth({ viewportRef: containerRef });
|
|
6779
|
-
const largestPageWidth =
|
|
6814
|
+
const largestPageWidth = useMemo(
|
|
6815
|
+
() => viewports.reduce((max, viewport) => Math.max(max, viewport.width), 0),
|
|
6816
|
+
[viewports]
|
|
6817
|
+
);
|
|
6780
6818
|
useEffect(() => {
|
|
6781
6819
|
virtualizer.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
|
|
6782
6820
|
const size = virtualizer.getSize();
|
|
@@ -6814,7 +6852,9 @@ var Pages = ({
|
|
|
6814
6852
|
{
|
|
6815
6853
|
ref: elementWrapperRef,
|
|
6816
6854
|
style: {
|
|
6817
|
-
width
|
|
6855
|
+
// The absolute page list contributes no intrinsic width. Reserve
|
|
6856
|
+
// its width before any zoom effect runs (including zoom === 1).
|
|
6857
|
+
width: largestPageWidth,
|
|
6818
6858
|
marginLeft: "auto",
|
|
6819
6859
|
marginRight: "auto",
|
|
6820
6860
|
flexShrink: 0
|
|
@@ -6855,6 +6895,36 @@ var Pages = ({
|
|
|
6855
6895
|
);
|
|
6856
6896
|
};
|
|
6857
6897
|
|
|
6898
|
+
// src/lib/map-concurrent.ts
|
|
6899
|
+
async function mapConcurrent(items, concurrency, map, isCancelled = () => false) {
|
|
6900
|
+
if (!Number.isFinite(concurrency) || concurrency < 1)
|
|
6901
|
+
throw new RangeError("Concurrency must be positive and finite");
|
|
6902
|
+
const results = new Array(items.length);
|
|
6903
|
+
let next = 0;
|
|
6904
|
+
let failed = false;
|
|
6905
|
+
const worker = async () => {
|
|
6906
|
+
while (next < items.length) {
|
|
6907
|
+
if (failed || isCancelled())
|
|
6908
|
+
throw new DOMException("Operation aborted", "AbortError");
|
|
6909
|
+
const index = next++;
|
|
6910
|
+
try {
|
|
6911
|
+
results[index] = await map(items[index], index);
|
|
6912
|
+
} catch (error) {
|
|
6913
|
+
failed = true;
|
|
6914
|
+
throw error;
|
|
6915
|
+
}
|
|
6916
|
+
}
|
|
6917
|
+
};
|
|
6918
|
+
await Promise.all(
|
|
6919
|
+
Array.from(
|
|
6920
|
+
{ length: Math.min(Math.max(1, Math.floor(concurrency)), items.length) },
|
|
6921
|
+
worker
|
|
6922
|
+
)
|
|
6923
|
+
);
|
|
6924
|
+
if (isCancelled()) throw new DOMException("Operation aborted", "AbortError");
|
|
6925
|
+
return results;
|
|
6926
|
+
}
|
|
6927
|
+
|
|
6858
6928
|
// src/lib/pdfjs.ts
|
|
6859
6929
|
var pdfJsPromise = null;
|
|
6860
6930
|
var loadPdfJs = () => {
|
|
@@ -6987,7 +7057,6 @@ var usePDFDocumentContext = ({
|
|
|
6987
7057
|
colorScheme,
|
|
6988
7058
|
darkModeColors
|
|
6989
7059
|
}) => {
|
|
6990
|
-
const [_, setProgress] = useState(0);
|
|
6991
7060
|
const [initialState, setInitialState] = useState();
|
|
6992
7061
|
const [rotation] = useState(initialRotation);
|
|
6993
7062
|
const [renderColorMapRef] = useState(() => ({
|
|
@@ -7002,30 +7071,30 @@ var usePDFDocumentContext = ({
|
|
|
7002
7071
|
const darkModeColorsRef = useRef(darkModeColors);
|
|
7003
7072
|
darkModeColorsRef.current = darkModeColors;
|
|
7004
7073
|
useEffect(() => {
|
|
7074
|
+
let isDisposed = false;
|
|
7005
7075
|
const generateViewports = async (pdf) => {
|
|
7006
7076
|
const pageProxies = [];
|
|
7007
|
-
const
|
|
7008
|
-
|
|
7009
|
-
|
|
7077
|
+
const viewports = await mapConcurrent(
|
|
7078
|
+
Array.from({ length: pdf.numPages }, (_, index) => index),
|
|
7079
|
+
16,
|
|
7080
|
+
async (index) => {
|
|
7010
7081
|
const page = await pdf.getPage(index + 1);
|
|
7011
7082
|
const deltaRotate = page.rotate || 0;
|
|
7012
7083
|
const viewport = page.getViewport({
|
|
7013
7084
|
scale: 1,
|
|
7014
7085
|
rotation: rotation + deltaRotate
|
|
7015
7086
|
});
|
|
7016
|
-
pageProxies
|
|
7017
|
-
rotations.push(page.rotate);
|
|
7087
|
+
pageProxies[index] = page;
|
|
7018
7088
|
return viewport;
|
|
7019
|
-
}
|
|
7020
|
-
|
|
7021
|
-
const sortedPageProxies = pageProxies.toSorted(
|
|
7022
|
-
(a, b) => a.pageNumber - b.pageNumber
|
|
7089
|
+
},
|
|
7090
|
+
() => isDisposed
|
|
7023
7091
|
);
|
|
7092
|
+
if (isDisposed) return;
|
|
7024
7093
|
setInitialState((prev) => ({
|
|
7025
7094
|
...prev,
|
|
7026
7095
|
isZoomFitWidth,
|
|
7027
7096
|
viewports,
|
|
7028
|
-
pageProxies
|
|
7097
|
+
pageProxies,
|
|
7029
7098
|
pdfDocumentProxy: pdf,
|
|
7030
7099
|
zoom,
|
|
7031
7100
|
zoomOptions,
|
|
@@ -7036,9 +7105,7 @@ var usePDFDocumentContext = ({
|
|
|
7036
7105
|
};
|
|
7037
7106
|
const loadDocument = () => {
|
|
7038
7107
|
setInitialState(null);
|
|
7039
|
-
setProgress(0);
|
|
7040
7108
|
let loadingTask = null;
|
|
7041
|
-
let isDisposed = false;
|
|
7042
7109
|
void loadPdfJs().then(({ getDocument, version }) => {
|
|
7043
7110
|
if (isDisposed) {
|
|
7044
7111
|
return;
|
|
@@ -7051,18 +7118,11 @@ var usePDFDocumentContext = ({
|
|
|
7051
7118
|
documentOptionsRef.current
|
|
7052
7119
|
)
|
|
7053
7120
|
);
|
|
7054
|
-
loadingTask.onProgress = (progressEvent) => {
|
|
7055
|
-
if (progressEvent.loaded === progressEvent.total) {
|
|
7056
|
-
return;
|
|
7057
|
-
}
|
|
7058
|
-
setProgress(progressEvent.loaded / progressEvent.total);
|
|
7059
|
-
};
|
|
7060
7121
|
return loadingTask.promise.then(async (proxy) => {
|
|
7061
7122
|
if (isDisposed || loadingTask?.destroyed) {
|
|
7062
7123
|
return;
|
|
7063
7124
|
}
|
|
7064
7125
|
onDocumentLoad?.({ proxy, source });
|
|
7065
|
-
setProgress(1);
|
|
7066
7126
|
try {
|
|
7067
7127
|
await generateViewports(proxy);
|
|
7068
7128
|
} catch (error) {
|
|
@@ -7191,31 +7251,122 @@ var Root = forwardRef(
|
|
|
7191
7251
|
}
|
|
7192
7252
|
);
|
|
7193
7253
|
Root.displayName = "Root";
|
|
7194
|
-
|
|
7195
|
-
|
|
7254
|
+
|
|
7255
|
+
// src/lib/document-text.ts
|
|
7256
|
+
var jobs = /* @__PURE__ */ new WeakMap();
|
|
7257
|
+
var CONCURRENCY = 4;
|
|
7258
|
+
function acquireDocumentText(pages) {
|
|
7259
|
+
let job = jobs.get(pages);
|
|
7260
|
+
if (!job) {
|
|
7261
|
+
const controller = new AbortController();
|
|
7262
|
+
const { signal } = controller;
|
|
7263
|
+
let next = 0;
|
|
7264
|
+
const result = new Array(pages.length);
|
|
7265
|
+
const worker = async () => {
|
|
7266
|
+
while (next < pages.length) {
|
|
7267
|
+
signal.throwIfAborted();
|
|
7268
|
+
const index = next++;
|
|
7269
|
+
const page = pages[index];
|
|
7270
|
+
const reader = page.streamTextContent().getReader();
|
|
7271
|
+
const chunks = [];
|
|
7272
|
+
let complete = false;
|
|
7273
|
+
try {
|
|
7274
|
+
while (true) {
|
|
7275
|
+
const { value, done } = await reader.read();
|
|
7276
|
+
signal.throwIfAborted();
|
|
7277
|
+
if (done) {
|
|
7278
|
+
complete = true;
|
|
7279
|
+
break;
|
|
7280
|
+
}
|
|
7281
|
+
chunks.push(
|
|
7282
|
+
value.items.map((item) => "str" in item ? item.str : "").join("")
|
|
7283
|
+
);
|
|
7284
|
+
}
|
|
7285
|
+
} finally {
|
|
7286
|
+
if (!complete)
|
|
7287
|
+
void reader.cancel(new Error("Text indexing stopped")).catch(() => {
|
|
7288
|
+
});
|
|
7289
|
+
reader.releaseLock();
|
|
7290
|
+
}
|
|
7291
|
+
result[index] = { pageNumber: page.pageNumber, text: chunks.join("") };
|
|
7292
|
+
}
|
|
7293
|
+
};
|
|
7294
|
+
const created = {
|
|
7295
|
+
controller,
|
|
7296
|
+
users: 0,
|
|
7297
|
+
settled: false,
|
|
7298
|
+
promise: Promise.resolve([])
|
|
7299
|
+
};
|
|
7300
|
+
created.promise = Promise.all(
|
|
7301
|
+
Array.from({ length: Math.min(CONCURRENCY, pages.length) }, worker)
|
|
7302
|
+
).then(() => result).catch((error) => {
|
|
7303
|
+
controller.abort();
|
|
7304
|
+
if (jobs.get(pages) === created) jobs.delete(pages);
|
|
7305
|
+
throw error;
|
|
7306
|
+
}).finally(() => {
|
|
7307
|
+
created.settled = true;
|
|
7308
|
+
});
|
|
7309
|
+
jobs.set(pages, created);
|
|
7310
|
+
job = created;
|
|
7311
|
+
}
|
|
7312
|
+
job.users++;
|
|
7313
|
+
const acquired = job;
|
|
7314
|
+
let released = false;
|
|
7315
|
+
return {
|
|
7316
|
+
promise: acquired.promise,
|
|
7317
|
+
release() {
|
|
7318
|
+
if (released) return;
|
|
7319
|
+
released = true;
|
|
7320
|
+
acquired.users--;
|
|
7321
|
+
queueMicrotask(() => {
|
|
7322
|
+
if (acquired.users || acquired.settled) return;
|
|
7323
|
+
if (jobs.get(pages) === acquired) jobs.delete(pages);
|
|
7324
|
+
acquired.controller.abort();
|
|
7325
|
+
});
|
|
7326
|
+
}
|
|
7327
|
+
};
|
|
7328
|
+
}
|
|
7329
|
+
var Search = ({
|
|
7330
|
+
children,
|
|
7331
|
+
loading = "Loading...",
|
|
7332
|
+
errorFallback
|
|
7333
|
+
}) => {
|
|
7334
|
+
const [result, setResult] = useState(null);
|
|
7335
|
+
const [attempt, setAttempt] = useState(0);
|
|
7196
7336
|
const proxies = usePdf((state) => state.pageProxies);
|
|
7197
7337
|
const setTextContent = usePdf((state) => state.setTextContent);
|
|
7198
|
-
const
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
const content = await proxy.getTextContent();
|
|
7203
|
-
const text2 = content.items.map((item) => item?.str || "").join("");
|
|
7204
|
-
return Promise.resolve({
|
|
7205
|
-
pageNumber: proxy.pageNumber,
|
|
7206
|
-
text: text2
|
|
7207
|
-
});
|
|
7208
|
-
});
|
|
7209
|
-
const text = await Promise.all(promises);
|
|
7210
|
-
setIsLoading(false);
|
|
7211
|
-
setTextContent(text);
|
|
7212
|
-
},
|
|
7213
|
-
[setTextContent]
|
|
7214
|
-
);
|
|
7338
|
+
const retry = useCallback(() => {
|
|
7339
|
+
setResult(null);
|
|
7340
|
+
setAttempt((value) => value + 1);
|
|
7341
|
+
}, []);
|
|
7215
7342
|
useEffect(() => {
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7343
|
+
let disposed = false;
|
|
7344
|
+
setResult(null);
|
|
7345
|
+
const task = acquireDocumentText(proxies);
|
|
7346
|
+
void task.promise.then((text) => {
|
|
7347
|
+
if (disposed) return;
|
|
7348
|
+
setTextContent(text);
|
|
7349
|
+
setResult({ pages: proxies, status: "ready" });
|
|
7350
|
+
}).catch((error) => {
|
|
7351
|
+
if (disposed) return;
|
|
7352
|
+
console.error("Error extracting PDF text", error);
|
|
7353
|
+
setResult({ pages: proxies, status: "error", error });
|
|
7354
|
+
});
|
|
7355
|
+
return () => {
|
|
7356
|
+
disposed = true;
|
|
7357
|
+
task.release();
|
|
7358
|
+
};
|
|
7359
|
+
}, [proxies, setTextContent, attempt]);
|
|
7360
|
+
if (!result || result.pages !== proxies) return loading;
|
|
7361
|
+
if (result.status === "error") {
|
|
7362
|
+
if (errorFallback) return errorFallback({ error: result.error, retry });
|
|
7363
|
+
return /* @__PURE__ */ jsxs("div", { role: "alert", children: [
|
|
7364
|
+
"Unable to index this document for search.",
|
|
7365
|
+
" ",
|
|
7366
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: retry, children: "Retry" })
|
|
7367
|
+
] });
|
|
7368
|
+
}
|
|
7369
|
+
return children;
|
|
7219
7370
|
};
|
|
7220
7371
|
var DEFAULT_CONFIG = {
|
|
7221
7372
|
maxHeight: 800,
|
|
@@ -7308,10 +7459,11 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
7308
7459
|
};
|
|
7309
7460
|
var Thumbnail = ({
|
|
7310
7461
|
pageNumber = 1,
|
|
7462
|
+
eager = false,
|
|
7311
7463
|
...props
|
|
7312
7464
|
}) => {
|
|
7313
7465
|
const { canvasRef, containerRef, isVisible } = useThumbnail(pageNumber, {
|
|
7314
|
-
isFirstPage: pageNumber < 5
|
|
7466
|
+
isFirstPage: eager || pageNumber < 5
|
|
7315
7467
|
});
|
|
7316
7468
|
const { jumpToPage } = usePdfJump();
|
|
7317
7469
|
return /* @__PURE__ */ jsx("div", { ref: containerRef, style: { minHeight: "150px", minWidth: "10px" }, children: isVisible && /* @__PURE__ */ jsx(
|
|
@@ -7338,16 +7490,107 @@ var Thumbnail = ({
|
|
|
7338
7490
|
}
|
|
7339
7491
|
) });
|
|
7340
7492
|
};
|
|
7493
|
+
function VirtualizedThumbnails({
|
|
7494
|
+
children,
|
|
7495
|
+
count,
|
|
7496
|
+
virtualize,
|
|
7497
|
+
style,
|
|
7498
|
+
onKeyDown,
|
|
7499
|
+
ref,
|
|
7500
|
+
...props
|
|
7501
|
+
}) {
|
|
7502
|
+
const containerRef = useRef(null);
|
|
7503
|
+
useImperativeHandle(ref, () => containerRef.current);
|
|
7504
|
+
const [focusPage, setFocusPage] = useState(null);
|
|
7505
|
+
const itemHeight = Number.isFinite(virtualize.itemHeight) ? Math.max(1, virtualize.itemHeight) : 150;
|
|
7506
|
+
const virtualizer = useVirtualizer({
|
|
7507
|
+
count,
|
|
7508
|
+
getScrollElement: () => containerRef.current,
|
|
7509
|
+
estimateSize: () => itemHeight,
|
|
7510
|
+
overscan: virtualize.overscan ?? 2
|
|
7511
|
+
});
|
|
7512
|
+
const items = virtualizer.getVirtualItems();
|
|
7513
|
+
useLayoutEffect(() => {
|
|
7514
|
+
if (focusPage === null) return;
|
|
7515
|
+
const canvas = containerRef.current?.querySelector(
|
|
7516
|
+
`[data-thumbnail-page="${focusPage}"] canvas`
|
|
7517
|
+
);
|
|
7518
|
+
if (canvas) {
|
|
7519
|
+
canvas.focus({ preventScroll: true });
|
|
7520
|
+
setFocusPage(null);
|
|
7521
|
+
}
|
|
7522
|
+
}, [focusPage, items]);
|
|
7523
|
+
return /* @__PURE__ */ jsx(
|
|
7524
|
+
Primitive.div,
|
|
7525
|
+
{
|
|
7526
|
+
...props,
|
|
7527
|
+
ref: containerRef,
|
|
7528
|
+
style: {
|
|
7529
|
+
height: "100%",
|
|
7530
|
+
overflow: "auto",
|
|
7531
|
+
position: "relative",
|
|
7532
|
+
...style
|
|
7533
|
+
},
|
|
7534
|
+
onKeyDown: (event) => {
|
|
7535
|
+
onKeyDown?.(event);
|
|
7536
|
+
if (event.defaultPrevented || !count) return;
|
|
7537
|
+
const row = event.target.closest(
|
|
7538
|
+
"[data-thumbnail-page]"
|
|
7539
|
+
);
|
|
7540
|
+
if (!row) return;
|
|
7541
|
+
const current = Number(row.dataset.thumbnailPage) - 1;
|
|
7542
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? count - 1 : event.key === "ArrowDown" ? Math.min(count - 1, current + 1) : event.key === "ArrowUp" ? Math.max(0, current - 1) : null;
|
|
7543
|
+
if (next === null) return;
|
|
7544
|
+
event.preventDefault();
|
|
7545
|
+
virtualizer.scrollToIndex(next, { align: "auto" });
|
|
7546
|
+
setFocusPage(next + 1);
|
|
7547
|
+
},
|
|
7548
|
+
children: /* @__PURE__ */ jsx("div", { style: { height: virtualizer.getTotalSize(), position: "relative" }, children: items.map((item) => /* @__PURE__ */ jsx(
|
|
7549
|
+
"div",
|
|
7550
|
+
{
|
|
7551
|
+
"data-thumbnail-page": item.index + 1,
|
|
7552
|
+
style: {
|
|
7553
|
+
position: "absolute",
|
|
7554
|
+
top: 0,
|
|
7555
|
+
left: 0,
|
|
7556
|
+
width: "100%",
|
|
7557
|
+
height: itemHeight,
|
|
7558
|
+
transform: `translateY(${item.start}px)`
|
|
7559
|
+
},
|
|
7560
|
+
children: cloneElement(
|
|
7561
|
+
children,
|
|
7562
|
+
{ pageNumber: item.index + 1, eager: true }
|
|
7563
|
+
)
|
|
7564
|
+
},
|
|
7565
|
+
item.key
|
|
7566
|
+
)) })
|
|
7567
|
+
}
|
|
7568
|
+
);
|
|
7569
|
+
}
|
|
7341
7570
|
var Thumbnails = ({
|
|
7342
7571
|
children,
|
|
7572
|
+
virtualize,
|
|
7343
7573
|
...props
|
|
7344
7574
|
}) => {
|
|
7345
7575
|
const pageCount = usePdf((state) => state.pdfDocumentProxy.numPages);
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7576
|
+
if (virtualize)
|
|
7577
|
+
return /* @__PURE__ */ jsx(
|
|
7578
|
+
VirtualizedThumbnails,
|
|
7579
|
+
{
|
|
7580
|
+
...props,
|
|
7581
|
+
virtualize,
|
|
7582
|
+
count: pageCount,
|
|
7583
|
+
children
|
|
7584
|
+
}
|
|
7585
|
+
);
|
|
7586
|
+
return /* @__PURE__ */ jsx(Primitive.div, { ...props, children: Array.from(
|
|
7587
|
+
{ length: pageCount },
|
|
7588
|
+
(_, index) => cloneElement(children, {
|
|
7589
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: a PDF page has a stable ordinal within this document
|
|
7590
|
+
key: index,
|
|
7591
|
+
pageNumber: index + 1
|
|
7592
|
+
})
|
|
7593
|
+
) });
|
|
7351
7594
|
};
|
|
7352
7595
|
var ZoomIn = ({ ...props }) => {
|
|
7353
7596
|
const setZoom = usePdf((state) => state.updateZoom);
|
|
@@ -7405,220 +7648,277 @@ var CurrentZoom = ({ ...props }) => {
|
|
|
7405
7648
|
}
|
|
7406
7649
|
);
|
|
7407
7650
|
};
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7651
|
+
|
|
7652
|
+
// src/lib/search.ts
|
|
7653
|
+
var normalizedPages = /* @__PURE__ */ new WeakMap();
|
|
7654
|
+
function normalize(page) {
|
|
7655
|
+
let cached = normalizedPages.get(page);
|
|
7656
|
+
if (!cached || cached.text !== page.text) {
|
|
7657
|
+
cached = { text: page.text, lower: page.text.toLowerCase() };
|
|
7658
|
+
normalizedPages.set(page, cached);
|
|
7659
|
+
}
|
|
7660
|
+
return cached.lower;
|
|
7661
|
+
}
|
|
7662
|
+
function createBoundedDistance(query, maxDistance) {
|
|
7663
|
+
const length = query.length;
|
|
7664
|
+
let previous = new Float64Array(length + 1);
|
|
7665
|
+
let current = new Float64Array(length + 1);
|
|
7666
|
+
const exceeded = maxDistance + 1;
|
|
7667
|
+
return (text, offset3) => {
|
|
7668
|
+
const size = Math.min(length, text.length - offset3);
|
|
7669
|
+
if (length - size > maxDistance) return exceeded;
|
|
7670
|
+
for (let j = 0; j <= length; j++) previous[j] = j;
|
|
7671
|
+
for (let i = 1; i <= size; i++) {
|
|
7672
|
+
const start = Math.max(1, i - maxDistance);
|
|
7673
|
+
const end = Math.min(length, i + maxDistance);
|
|
7674
|
+
current[0] = i;
|
|
7675
|
+
if (start > 1) current[start - 1] = exceeded;
|
|
7676
|
+
let minimum = exceeded;
|
|
7677
|
+
const code = text.charCodeAt(offset3 + i - 1);
|
|
7678
|
+
for (let j = start; j <= end; j++) {
|
|
7679
|
+
const value = Math.min(
|
|
7680
|
+
previous[j] + 1,
|
|
7681
|
+
current[j - 1] + 1,
|
|
7682
|
+
previous[j - 1] + (query.charCodeAt(j - 1) === code ? 0 : 1)
|
|
7425
7683
|
);
|
|
7684
|
+
current[j] = value;
|
|
7685
|
+
minimum = Math.min(minimum, value);
|
|
7426
7686
|
}
|
|
7687
|
+
if (end < length) current[end + 1] = exceeded;
|
|
7688
|
+
if (minimum > maxDistance) return exceeded;
|
|
7689
|
+
[previous, current] = [current, previous];
|
|
7690
|
+
}
|
|
7691
|
+
return previous[length];
|
|
7692
|
+
};
|
|
7693
|
+
}
|
|
7694
|
+
function insertMatch(matches, match, limit) {
|
|
7695
|
+
if (!limit || matches.length === limit && matches[limit - 1].score >= match.score)
|
|
7696
|
+
return;
|
|
7697
|
+
let low = 0;
|
|
7698
|
+
let high = matches.length;
|
|
7699
|
+
while (low < high) {
|
|
7700
|
+
const mid = low + high >>> 1;
|
|
7701
|
+
if (matches[mid].score >= match.score) low = mid + 1;
|
|
7702
|
+
else high = mid;
|
|
7703
|
+
}
|
|
7704
|
+
matches.splice(low, 0, match);
|
|
7705
|
+
if (matches.length > limit) matches.pop();
|
|
7706
|
+
}
|
|
7707
|
+
function* runSearch(pages, searchText, options = {}, cooperative = false, budget = 8) {
|
|
7708
|
+
const empty = { exactMatches: [], fuzzyMatches: [], hasMoreResults: false };
|
|
7709
|
+
if (!searchText.trim()) return empty;
|
|
7710
|
+
const threshold = Math.max(0, Math.min(1, options.threshold ?? 0.7));
|
|
7711
|
+
const limit = Number.isFinite(options.limit) ? Math.max(0, Math.floor(options.limit)) : 10;
|
|
7712
|
+
const textSize = options.textSize ?? 100;
|
|
7713
|
+
const query = searchText.toLowerCase();
|
|
7714
|
+
const maxDistance = Math.floor(query.length * (1 - threshold));
|
|
7715
|
+
const distance = maxDistance > 0 ? createBoundedDistance(query, maxDistance) : null;
|
|
7716
|
+
const exactMatches = [];
|
|
7717
|
+
const fuzzyMatches = [];
|
|
7718
|
+
let exactCount = 0;
|
|
7719
|
+
let fuzzyCount = 0;
|
|
7720
|
+
let steps = 0;
|
|
7721
|
+
let deadline = cooperative ? performance.now() + budget : 0;
|
|
7722
|
+
for (const page of pages) {
|
|
7723
|
+
const lower = normalize(page);
|
|
7724
|
+
const exactStarts = [];
|
|
7725
|
+
let index = 0;
|
|
7726
|
+
while (true) {
|
|
7727
|
+
if (cooperative && (++steps & 127) === 0 && performance.now() >= deadline) {
|
|
7728
|
+
yield;
|
|
7729
|
+
deadline = performance.now() + budget;
|
|
7730
|
+
}
|
|
7731
|
+
const matchIndex = lower.indexOf(query, index);
|
|
7732
|
+
if (matchIndex === -1) break;
|
|
7733
|
+
exactCount++;
|
|
7734
|
+
if (maxDistance > 0) exactStarts.push(matchIndex);
|
|
7735
|
+
if (exactMatches.length < limit) {
|
|
7736
|
+
exactMatches.push({
|
|
7737
|
+
pageNumber: page.pageNumber,
|
|
7738
|
+
text: page.text.substr(matchIndex, searchText.length + textSize),
|
|
7739
|
+
score: 1,
|
|
7740
|
+
matchIndex,
|
|
7741
|
+
isExactMatch: true,
|
|
7742
|
+
searchText
|
|
7743
|
+
});
|
|
7744
|
+
}
|
|
7745
|
+
index = matchIndex + searchText.length;
|
|
7746
|
+
}
|
|
7747
|
+
if (!(maxDistance > 0)) continue;
|
|
7748
|
+
index = 0;
|
|
7749
|
+
let exactIndex = 0;
|
|
7750
|
+
while (index < lower.length) {
|
|
7751
|
+
if (cooperative && (++steps & 127) === 0 && performance.now() >= deadline) {
|
|
7752
|
+
yield;
|
|
7753
|
+
deadline = performance.now() + budget;
|
|
7754
|
+
}
|
|
7755
|
+
while (exactIndex < exactStarts.length && exactStarts[exactIndex] + searchText.length <= index)
|
|
7756
|
+
exactIndex++;
|
|
7757
|
+
const exactStart = exactStarts[exactIndex];
|
|
7758
|
+
if (exactStart !== void 0 && index >= exactStart) {
|
|
7759
|
+
index = exactStart + searchText.length;
|
|
7760
|
+
continue;
|
|
7761
|
+
}
|
|
7762
|
+
const edits = distance(lower, index);
|
|
7763
|
+
if (edits > 0 && edits <= maxDistance) {
|
|
7764
|
+
fuzzyCount++;
|
|
7765
|
+
const score = 1 - edits / query.length;
|
|
7766
|
+
if (fuzzyMatches.length < limit || score > (fuzzyMatches[limit - 1]?.score ?? Infinity)) {
|
|
7767
|
+
insertMatch(
|
|
7768
|
+
fuzzyMatches,
|
|
7769
|
+
{
|
|
7770
|
+
pageNumber: page.pageNumber,
|
|
7771
|
+
text: page.text.substr(index, query.length + textSize),
|
|
7772
|
+
score,
|
|
7773
|
+
matchIndex: index,
|
|
7774
|
+
isExactMatch: false,
|
|
7775
|
+
searchText
|
|
7776
|
+
},
|
|
7777
|
+
limit
|
|
7778
|
+
);
|
|
7779
|
+
}
|
|
7780
|
+
index += query.length;
|
|
7781
|
+
} else index++;
|
|
7427
7782
|
}
|
|
7428
7783
|
}
|
|
7429
|
-
|
|
7784
|
+
const exactLimit = Math.min(
|
|
7785
|
+
exactCount,
|
|
7786
|
+
Math.max(Math.ceil(limit / 2), limit - fuzzyCount)
|
|
7787
|
+
);
|
|
7788
|
+
return {
|
|
7789
|
+
exactMatches: exactMatches.slice(0, exactLimit),
|
|
7790
|
+
fuzzyMatches: fuzzyMatches.slice(0, limit - exactLimit),
|
|
7791
|
+
hasMoreResults: exactCount + fuzzyCount > limit
|
|
7792
|
+
};
|
|
7793
|
+
}
|
|
7794
|
+
function searchDocument(pages, searchText, options = {}) {
|
|
7795
|
+
return runSearch(pages, searchText, options).next().value;
|
|
7796
|
+
}
|
|
7797
|
+
async function searchDocumentAsync(pages, searchText, options = {}) {
|
|
7798
|
+
const budget = Number.isFinite(options.timeSliceMs) ? Math.max(1, options.timeSliceMs) : 8;
|
|
7799
|
+
const iterator = runSearch(pages, searchText, options, true, budget);
|
|
7800
|
+
while (true) {
|
|
7801
|
+
if (options.signal?.aborted)
|
|
7802
|
+
throw new DOMException("Search aborted", "AbortError");
|
|
7803
|
+
const result = iterator.next();
|
|
7804
|
+
if (result.done) return result.value;
|
|
7805
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
7806
|
+
}
|
|
7430
7807
|
}
|
|
7808
|
+
|
|
7809
|
+
// src/hooks/search/useSearch.tsx
|
|
7431
7810
|
var useSearch = () => {
|
|
7432
7811
|
const textContent = usePdf((state) => state.textContent);
|
|
7812
|
+
const activeSearch = useRef(null);
|
|
7813
|
+
const [isSearching, setIsSearching] = useState(false);
|
|
7433
7814
|
const [keywords] = useState([]);
|
|
7434
7815
|
const [searchResults, setSearchResults] = useState({
|
|
7435
7816
|
exactMatches: [],
|
|
7436
7817
|
fuzzyMatches: [],
|
|
7437
7818
|
hasMoreResults: false
|
|
7438
7819
|
});
|
|
7439
|
-
|
|
7440
|
-
(
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7820
|
+
useEffect(() => {
|
|
7821
|
+
setIsSearching(false);
|
|
7822
|
+
return () => {
|
|
7823
|
+
activeSearch.current?.abort();
|
|
7824
|
+
activeSearch.current = null;
|
|
7825
|
+
};
|
|
7826
|
+
}, [textContent]);
|
|
7827
|
+
const cancelSearch = useCallback(() => {
|
|
7828
|
+
activeSearch.current?.abort();
|
|
7829
|
+
activeSearch.current = null;
|
|
7830
|
+
setIsSearching(false);
|
|
7831
|
+
}, []);
|
|
7832
|
+
const searchAsync = useCallback(
|
|
7833
|
+
async (searchText, options = {}) => {
|
|
7834
|
+
activeSearch.current?.abort();
|
|
7835
|
+
const controller = new AbortController();
|
|
7836
|
+
activeSearch.current = controller;
|
|
7837
|
+
const abort = () => controller.abort();
|
|
7838
|
+
if (options.signal?.aborted) abort();
|
|
7839
|
+
else options.signal?.addEventListener("abort", abort, { once: true });
|
|
7840
|
+
setIsSearching(true);
|
|
7841
|
+
try {
|
|
7842
|
+
const results = await searchDocumentAsync(textContent, searchText, {
|
|
7843
|
+
...options,
|
|
7844
|
+
signal: controller.signal
|
|
7455
7845
|
});
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
const textLower = text.toLowerCase();
|
|
7466
|
-
const searchLower = searchText.toLowerCase();
|
|
7467
|
-
let index = 0;
|
|
7468
|
-
while (index < textLower.length) {
|
|
7469
|
-
if (excludeIndices.has(index)) {
|
|
7470
|
-
index++;
|
|
7471
|
-
continue;
|
|
7472
|
-
}
|
|
7473
|
-
const chunk = textLower.substr(index, searchLower.length + 10);
|
|
7474
|
-
const distance = levenshteinDistance(
|
|
7475
|
-
searchLower,
|
|
7476
|
-
chunk.substr(0, searchLower.length)
|
|
7477
|
-
);
|
|
7478
|
-
const maxDistance = Math.floor(searchLower.length * (1 - threshold));
|
|
7479
|
-
if (distance <= maxDistance && distance > 0) {
|
|
7480
|
-
const score = 1 - distance / searchLower.length;
|
|
7481
|
-
results.push({
|
|
7482
|
-
pageNumber,
|
|
7483
|
-
text: text.substr(index, searchLower.length + textSize),
|
|
7484
|
-
score,
|
|
7485
|
-
matchIndex: index,
|
|
7486
|
-
isExactMatch: false,
|
|
7487
|
-
searchText
|
|
7488
|
-
});
|
|
7489
|
-
index += searchLower.length;
|
|
7490
|
-
} else {
|
|
7491
|
-
index++;
|
|
7846
|
+
if (controller.signal.aborted)
|
|
7847
|
+
throw new DOMException("Search aborted", "AbortError");
|
|
7848
|
+
if (activeSearch.current === controller) setSearchResults(results);
|
|
7849
|
+
return results;
|
|
7850
|
+
} finally {
|
|
7851
|
+
options.signal?.removeEventListener("abort", abort);
|
|
7852
|
+
if (activeSearch.current === controller) {
|
|
7853
|
+
activeSearch.current = null;
|
|
7854
|
+
setIsSearching(false);
|
|
7492
7855
|
}
|
|
7493
7856
|
}
|
|
7494
|
-
return results;
|
|
7495
7857
|
},
|
|
7496
|
-
[]
|
|
7858
|
+
[textContent]
|
|
7497
7859
|
);
|
|
7498
7860
|
const search = useCallback(
|
|
7499
7861
|
(searchText, options = {}) => {
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
fuzzyMatches: [],
|
|
7505
|
-
hasMoreResults: false
|
|
7506
|
-
};
|
|
7507
|
-
setSearchResults(emptyResults);
|
|
7508
|
-
return emptyResults;
|
|
7509
|
-
}
|
|
7510
|
-
let exactMatches = [];
|
|
7511
|
-
let fuzzyMatches = [];
|
|
7512
|
-
const exactMatchIndices = /* @__PURE__ */ new Set();
|
|
7513
|
-
textContent.forEach(({ pageNumber, text }) => {
|
|
7514
|
-
const matches = findExactMatches(
|
|
7515
|
-
searchText,
|
|
7516
|
-
text,
|
|
7517
|
-
pageNumber,
|
|
7518
|
-
textSize
|
|
7519
|
-
);
|
|
7520
|
-
exactMatches = [...exactMatches, ...matches];
|
|
7521
|
-
matches.forEach((match) => {
|
|
7522
|
-
for (let i = 0; i < searchText.length; i++) {
|
|
7523
|
-
exactMatchIndices.add(match.matchIndex + i);
|
|
7524
|
-
}
|
|
7525
|
-
});
|
|
7526
|
-
});
|
|
7527
|
-
textContent.forEach(({ pageNumber, text }) => {
|
|
7528
|
-
const matches = findFuzzyMatches(
|
|
7529
|
-
searchText,
|
|
7530
|
-
text,
|
|
7531
|
-
pageNumber,
|
|
7532
|
-
threshold,
|
|
7533
|
-
exactMatchIndices,
|
|
7534
|
-
textSize
|
|
7535
|
-
);
|
|
7536
|
-
fuzzyMatches = [...fuzzyMatches, ...matches];
|
|
7537
|
-
});
|
|
7538
|
-
exactMatches.sort((a, b) => b.score - a.score);
|
|
7539
|
-
fuzzyMatches.sort((a, b) => b.score - a.score);
|
|
7540
|
-
const exactLimit = Math.min(exactMatches.length, Math.ceil(limit / 2));
|
|
7541
|
-
const fuzzyLimit = Math.min(fuzzyMatches.length, limit - exactLimit);
|
|
7542
|
-
const limitedResults = {
|
|
7543
|
-
exactMatches: exactMatches.slice(0, exactLimit),
|
|
7544
|
-
fuzzyMatches: fuzzyMatches.slice(0, fuzzyLimit),
|
|
7545
|
-
hasMoreResults: exactMatches.length + fuzzyMatches.length > limit
|
|
7546
|
-
};
|
|
7547
|
-
setSearchResults(limitedResults);
|
|
7548
|
-
return limitedResults;
|
|
7862
|
+
cancelSearch();
|
|
7863
|
+
const results = searchDocument(textContent, searchText, options);
|
|
7864
|
+
setSearchResults(results);
|
|
7865
|
+
return results;
|
|
7549
7866
|
},
|
|
7550
|
-
[textContent,
|
|
7867
|
+
[textContent, cancelSearch]
|
|
7551
7868
|
);
|
|
7552
7869
|
return {
|
|
7553
7870
|
textContent,
|
|
7554
7871
|
keywords,
|
|
7555
7872
|
searchResults,
|
|
7556
|
-
search
|
|
7873
|
+
search,
|
|
7874
|
+
searchAsync,
|
|
7875
|
+
cancelSearch,
|
|
7876
|
+
isSearching
|
|
7557
7877
|
};
|
|
7558
7878
|
};
|
|
7559
7879
|
|
|
7560
7880
|
// src/hooks/search/useSearchPosition.tsx
|
|
7561
7881
|
async function calculateHighlightRects(pageProxy, textMatch) {
|
|
7882
|
+
const matchLength = textMatch.searchText ? textMatch.searchText.length : textMatch.text.length;
|
|
7883
|
+
if (matchLength === 0 || !Number.isFinite(textMatch.matchIndex) || textMatch.matchIndex < 0)
|
|
7884
|
+
return [];
|
|
7885
|
+
const matchEnd = textMatch.matchIndex + matchLength;
|
|
7562
7886
|
const reader = pageProxy.streamTextContent().getReader();
|
|
7563
|
-
const
|
|
7887
|
+
const matchRects = [];
|
|
7888
|
+
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
7889
|
+
let offset3 = 0;
|
|
7890
|
+
let foundEnd = false;
|
|
7564
7891
|
try {
|
|
7565
|
-
|
|
7892
|
+
read: while (true) {
|
|
7566
7893
|
const { value, done } = await reader.read();
|
|
7567
7894
|
if (done) break;
|
|
7568
|
-
items
|
|
7569
|
-
|
|
7570
|
-
|
|
7895
|
+
for (const item of value.items) {
|
|
7896
|
+
if (!("str" in item) || item.str.length === 0) continue;
|
|
7897
|
+
const length = item.str.length;
|
|
7898
|
+
const start = Math.max(0, textMatch.matchIndex - offset3);
|
|
7899
|
+
const end = Math.min(length, matchEnd - offset3);
|
|
7900
|
+
if (end > start) {
|
|
7901
|
+
matchRects.push({
|
|
7902
|
+
pageNumber: textMatch.pageNumber,
|
|
7903
|
+
left: item.transform[4] + start * (item.width / length),
|
|
7904
|
+
top: viewport.height - (item.transform[5] + item.height),
|
|
7905
|
+
width: (end - start) * (item.width / length),
|
|
7906
|
+
height: item.height
|
|
7907
|
+
});
|
|
7908
|
+
}
|
|
7909
|
+
offset3 += length;
|
|
7910
|
+
if (offset3 >= matchEnd) {
|
|
7911
|
+
foundEnd = true;
|
|
7912
|
+
break read;
|
|
7913
|
+
}
|
|
7914
|
+
}
|
|
7571
7915
|
}
|
|
7572
7916
|
} finally {
|
|
7917
|
+
if (foundEnd)
|
|
7918
|
+
void reader.cancel(new Error("Highlight range found")).catch(() => {
|
|
7919
|
+
});
|
|
7573
7920
|
reader.releaseLock();
|
|
7574
7921
|
}
|
|
7575
|
-
const matchLength = textMatch.searchText ? textMatch.searchText.length : textMatch.text.length;
|
|
7576
|
-
const matchRects = [];
|
|
7577
|
-
let currentIndex = 0;
|
|
7578
|
-
let remainingMatchLength = matchLength;
|
|
7579
|
-
let foundStart = false;
|
|
7580
|
-
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
7581
|
-
for (let i = 0; i < items.length; i++) {
|
|
7582
|
-
const item = items[i];
|
|
7583
|
-
if (!item) continue;
|
|
7584
|
-
const itemLength = item.str.length;
|
|
7585
|
-
if (!foundStart && currentIndex <= textMatch.matchIndex && textMatch.matchIndex < currentIndex + itemLength) {
|
|
7586
|
-
foundStart = true;
|
|
7587
|
-
const matchStartInItem = textMatch.matchIndex - currentIndex;
|
|
7588
|
-
const matchLengthInItem = Math.min(
|
|
7589
|
-
itemLength - matchStartInItem,
|
|
7590
|
-
remainingMatchLength
|
|
7591
|
-
);
|
|
7592
|
-
const transform = item.transform;
|
|
7593
|
-
const y = viewport.height - (transform[5] + item.height);
|
|
7594
|
-
const rect = {
|
|
7595
|
-
pageNumber: textMatch.pageNumber,
|
|
7596
|
-
left: transform[4] + matchStartInItem * (item.width / itemLength),
|
|
7597
|
-
top: y,
|
|
7598
|
-
width: matchLengthInItem * (item.width / itemLength),
|
|
7599
|
-
height: item.height
|
|
7600
|
-
};
|
|
7601
|
-
matchRects.push(rect);
|
|
7602
|
-
remainingMatchLength -= matchLengthInItem;
|
|
7603
|
-
} else if (foundStart && remainingMatchLength > 0) {
|
|
7604
|
-
const matchLengthInItem = Math.min(itemLength, remainingMatchLength);
|
|
7605
|
-
const transform = item.transform;
|
|
7606
|
-
const y = viewport.height - (transform[5] + item.height);
|
|
7607
|
-
const rect = {
|
|
7608
|
-
pageNumber: textMatch.pageNumber,
|
|
7609
|
-
left: transform[4],
|
|
7610
|
-
top: y,
|
|
7611
|
-
width: matchLengthInItem * (item.width / itemLength),
|
|
7612
|
-
height: item.height
|
|
7613
|
-
};
|
|
7614
|
-
matchRects.push(rect);
|
|
7615
|
-
remainingMatchLength -= matchLengthInItem;
|
|
7616
|
-
}
|
|
7617
|
-
if (remainingMatchLength <= 0 && foundStart) {
|
|
7618
|
-
break;
|
|
7619
|
-
}
|
|
7620
|
-
currentIndex += itemLength;
|
|
7621
|
-
}
|
|
7622
7922
|
return mergeAdjacentRects(matchRects);
|
|
7623
7923
|
}
|
|
7624
7924
|
function mergeAdjacentRects(rects) {
|
|
@@ -7646,4 +7946,4 @@ function mergeAdjacentRects(rects) {
|
|
|
7646
7946
|
// src/hooks/usePageRendered.ts
|
|
7647
7947
|
var usePageRendered = (pageNumber) => usePdf((state) => !!state.renderedPages[pageNumber]);
|
|
7648
7948
|
|
|
7649
|
-
export { AnnotationHighlightLayer, AnnotationLayer, AnnotationTooltip, AnnotationsStoreProvider, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, DEFAULT_DARK_MODE_COLORS, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, createDarkModeColorMap, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePageRendered, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|
|
7949
|
+
export { AnnotationHighlightLayer, AnnotationLayer, AnnotationTooltip, AnnotationsStoreProvider, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, DEFAULT_DARK_MODE_COLORS, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, createDarkModeColorMap, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePageAnnotations, usePageRendered, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|