@anaralabs/lector 3.8.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -102,6 +102,8 @@ We welcome contributions! Key areas we're focusing on:
102
102
  3. Mobile/touch interactions
103
103
  4. Documentation and examples
104
104
 
105
+
106
+
105
107
  ## Thanks
106
108
 
107
109
  Special thanks to these open-source projects that provided inspiration:
@@ -4,11 +4,12 @@ import type { DocumentInitParameters } from 'pdfjs-dist/types/src/display/api';
4
4
  import { ForwardRefExoticComponent } from 'react';
5
5
  import { FunctionComponent } from 'react';
6
6
  import { HTMLProps } from 'react';
7
- import type { IPDFLinkService } from 'pdfjs-dist/types/web/interfaces';
8
7
  import { JSX } from 'react/jsx-runtime';
9
- import type { JSX as JSX_2 } from 'react';
8
+ import { JSX as JSX_2 } from 'react';
9
+ import { MemoExoticComponent } from 'react';
10
10
  import type { PageViewport } from 'pdfjs-dist';
11
- import { PDFDocumentProxy } from 'pdfjs-dist';
11
+ import type { PDFDocumentProxy } from 'pdfjs-dist';
12
+ import { pdfjsDist } from 'pdfjs-dist';
12
13
  import type { PDFPageProxy } from 'pdfjs-dist';
13
14
  import type { default as React_2 } from 'react';
14
15
  import { ReactElement } from 'react';
@@ -68,7 +69,7 @@ declare interface AnnotationHighlightLayerProps {
68
69
  * @param jumpOptions - Options for page navigation behavior when clicking internal links.
69
70
  * See `usePdfJump` hook for available options.
70
71
  */
71
- export declare const AnnotationLayer: ({ renderForms, externalLinksEnabled, jumpOptions, className, style, ...props }: AnnotationLayerParams & HTMLProps<HTMLDivElement>) => JSX.Element;
72
+ export declare const AnnotationLayer: MemoExoticComponent<({ renderForms, externalLinksEnabled, jumpOptions, className, style, ...props }: AnnotationLayerParams & HTMLProps<HTMLDivElement>) => JSX.Element>;
72
73
 
73
74
  declare interface AnnotationLayerParams {
74
75
  /**
@@ -142,9 +143,9 @@ export declare const cancellable: <T extends Promise<unknown>>(promise: T) => {
142
143
  cancel: () => void;
143
144
  };
144
145
 
145
- export declare const CanvasLayer: ({ style, background, ...props }: HTMLProps<HTMLCanvasElement> & {
146
- background?: string;
147
- }) => JSX.Element;
146
+ export declare const CanvasLayer: MemoExoticComponent<({ style, background, ...props }: HTMLProps<HTMLCanvasElement> & {
147
+ background?: string;
148
+ }) => JSX.Element>;
148
149
 
149
150
  export declare const clamp: (value: number, minimum: number, maximum: number) => number;
150
151
 
@@ -175,9 +176,11 @@ export declare const CurrentPage: ({ ...props }: HTMLProps<HTMLInputElement>) =>
175
176
 
176
177
  export declare const CurrentZoom: ({ ...props }: HTMLProps<HTMLInputElement>) => JSX.Element;
177
178
 
178
- export declare const CustomLayer: ({ children, }: {
179
- children: (pageNumber: number) => JSX_2.Element;
180
- }) => JSX_2.Element;
179
+ export declare const CustomLayer: MemoExoticComponent<({ children }: {
180
+ children: (pageNumber: number) => JSX_2.Element;
181
+ }) => JSX_2.Element>;
182
+
183
+ export declare const ensureAnnotationLayerStyles: () => void;
181
184
 
182
185
  export declare const firstMemo: <T>(first: boolean, memo: unknown, initializer: () => T) => T;
183
186
 
@@ -225,7 +228,7 @@ export declare interface InitialPDFState {
225
228
  zoomOptions?: ZoomOptions;
226
229
  }
227
230
 
228
- export declare class LinkService implements IPDFLinkService {
231
+ export declare class LinkService {
229
232
  _pdfDocumentProxy?: PDFDocumentProxy;
230
233
  externalLinkEnabled: boolean;
231
234
  isInPresentationMode: boolean;
@@ -252,6 +255,8 @@ export declare class LinkService implements IPDFLinkService {
252
255
  unregisterPageNavigationCallback(): void;
253
256
  }
254
257
 
258
+ export declare const loadPdfJs: () => Promise<pdfjsDist>;
259
+
255
260
  export declare const NextPage: () => void;
256
261
 
257
262
  export declare const Outline: ({ children, ...props }: HTMLProps<HTMLUListElement> & {
@@ -273,10 +278,10 @@ declare interface OutlineItemProps extends HTMLProps<HTMLDivElement> {
273
278
 
274
279
  declare type OutlineItemType = NonNullable<ReturnType<typeof usePDFOutline>>[number];
275
280
 
276
- export declare const Page: ({ children, pageNumber, style, ...props }: HTMLProps<HTMLDivElement> & {
277
- children: ReactNode;
278
- pageNumber?: number;
279
- }) => JSX.Element;
281
+ export declare const Page: MemoExoticComponent<({ children, pageNumber, style, ...props }: HTMLProps<HTMLDivElement> & {
282
+ children: ReactNode;
283
+ pageNumber?: number;
284
+ }) => JSX.Element>;
280
285
 
281
286
  export declare const Pages: ({ children, gap, virtualizerOptions, initialOffset, onOffsetChange, ...props }: HTMLProps<HTMLDivElement> & {
282
287
  virtualizerOptions?: {
@@ -371,12 +376,14 @@ declare interface SelectionTooltipProps {
371
376
 
372
377
  export declare type Source = string | URL | TypedArray | ArrayBuffer | DocumentInitParameters;
373
378
 
379
+ export declare const subscribeToViewportInvalidation: (viewport: HTMLDivElement, callback: ViewportCallback) => () => void;
380
+
374
381
  declare type TextContent = {
375
382
  pageNumber: number;
376
383
  text: string;
377
384
  };
378
385
 
379
- export declare const TextLayer: ({ className, style, ...props }: HTMLProps<HTMLDivElement>) => JSX.Element;
386
+ export declare const TextLayer: MemoExoticComponent<({ className, style, ...props }: HTMLProps<HTMLDivElement>) => JSX.Element>;
380
387
 
381
388
  declare interface TextPosition {
382
389
  pageNumber: number;
@@ -494,6 +501,8 @@ export declare const useSelectionDimensions: () => {
494
501
  } | undefined;
495
502
  };
496
503
 
504
+ declare type ViewportCallback = () => void;
505
+
497
506
  export declare const ZoomIn: ({ ...props }: HTMLProps<HTMLButtonElement>) => JSX.Element;
498
507
 
499
508
  export declare interface ZoomOptions {
package/dist/index.js CHANGED
@@ -1,18 +1,15 @@
1
1
  'use client';
2
2
 
3
- import React, { createContext, forwardRef, useRef, useState, useCallback, useContext, useEffect, cloneElement, createRef, useMemo, useLayoutEffect } from 'react';
3
+ import React, { createContext, memo, forwardRef, cloneElement, useRef, useState, useCallback, useContext, useEffect, useMemo, useLayoutEffect, createRef } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
5
  import { useFloating, autoUpdate, offset, shift, useDismiss, useInteractions, inline, flip } from '@floating-ui/react';
6
6
  import { create, useStore, createStore } from 'zustand';
7
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
8
- import { AnnotationLayer, TextLayer, getDocument } from 'pdfjs-dist';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
8
  import { useDebounce } from 'use-debounce';
10
9
  import { v4 } from 'uuid';
11
10
  import { Slot } from '@radix-ui/react-slot';
12
11
  import { useVirtualizer, elementScroll, debounce } from '@tanstack/react-virtual';
13
12
 
14
- // src/components/annotation-tooltip.tsx
15
-
16
13
  // src/lib/clamp.ts
17
14
  var clamp = (value, minimum, maximum) => {
18
15
  return Math.min(Math.max(value, minimum), maximum);
@@ -615,6 +612,56 @@ function clsx() {
615
612
  }
616
613
  var clsx_default = clsx;
617
614
 
615
+ // src/lib/annotation-layer-styles.ts
616
+ var ANNOTATION_LAYER_STYLE_ID = "lector-annotation-layer-styles";
617
+ var annotationLayerStyles = `
618
+ .annotationLayer {
619
+ position: absolute;
620
+ left: 0;
621
+ top: 0;
622
+ right: 0;
623
+ bottom: 0;
624
+ overflow: hidden;
625
+ opacity: 1;
626
+ z-index: 3;
627
+ }
628
+
629
+ .annotationLayer section {
630
+ position: absolute;
631
+ }
632
+
633
+ .annotationLayer .linkAnnotation > a,
634
+ .annotationLayer .buttonWidgetAnnotation.pushButton > a {
635
+ position: absolute;
636
+ font-size: 1em;
637
+ top: 0;
638
+ left: 0;
639
+ width: 100%;
640
+ height: 100%;
641
+ background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
642
+ cursor: pointer;
643
+ }
644
+
645
+ .annotationLayer .linkAnnotation > a:hover,
646
+ .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
647
+ opacity: 0.2;
648
+ background: rgba(255, 255, 0, 1);
649
+ box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
650
+ }
651
+ `;
652
+ var ensureAnnotationLayerStyles = () => {
653
+ if (typeof document === "undefined") {
654
+ return;
655
+ }
656
+ if (document.getElementById(ANNOTATION_LAYER_STYLE_ID)) {
657
+ return;
658
+ }
659
+ const style = document.createElement("style");
660
+ style.id = ANNOTATION_LAYER_STYLE_ID;
661
+ style.textContent = annotationLayerStyles;
662
+ document.head.appendChild(style);
663
+ };
664
+
618
665
  // src/lib/cancellable.ts
619
666
  var cancellable = (promise) => {
620
667
  let isCancelled = false;
@@ -639,6 +686,15 @@ var cancellable = (promise) => {
639
686
  }
640
687
  };
641
688
  };
689
+
690
+ // src/lib/pdfjs.ts
691
+ var pdfJsPromise = null;
692
+ var loadPdfJs = () => {
693
+ if (!pdfJsPromise) {
694
+ pdfJsPromise = import('pdfjs-dist');
695
+ }
696
+ return pdfJsPromise;
697
+ };
642
698
  var usePdfJump = () => {
643
699
  const virtualizer = usePdf((state) => state.virtualizer);
644
700
  const setHighlight = usePdf((state) => state.setHighlight);
@@ -931,46 +987,7 @@ var useAnnotationLayer = (params) => {
931
987
  };
932
988
  }, [jumpToPage, linkService, mergedParams.jumpOptions]);
933
989
  useEffect(() => {
934
- const style = document.createElement("style");
935
- style.textContent = `
936
- .annotationLayer {
937
- position: absolute;
938
- left: 0;
939
- top: 0;
940
- right: 0;
941
- bottom: 0;
942
- overflow: hidden;
943
- opacity: 1;
944
- z-index: 3;
945
- }
946
-
947
- .annotationLayer section {
948
- position: absolute;
949
- }
950
-
951
- .annotationLayer .linkAnnotation > a,
952
- .annotationLayer .buttonWidgetAnnotation.pushButton > a {
953
- position: absolute;
954
- font-size: 1em;
955
- top: 0;
956
- left: 0;
957
- width: 100%;
958
- height: 100%;
959
- background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
960
- cursor: pointer;
961
- }
962
-
963
- .annotationLayer .linkAnnotation > a:hover,
964
- .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
965
- opacity: 0.2;
966
- background: rgba(255, 255, 0, 1);
967
- box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
968
- }
969
- `;
970
- document.head.appendChild(style);
971
- return () => {
972
- document.head.removeChild(style);
973
- };
990
+ ensureAnnotationLayerStyles();
974
991
  }, []);
975
992
  useEffect(() => {
976
993
  if (!annotationLayerRef.current) return;
@@ -1011,22 +1028,27 @@ var useAnnotationLayer = (params) => {
1011
1028
  }
1012
1029
  annotationLayerRef.current.innerHTML = "";
1013
1030
  annotationLayerRef.current.className = "annotationLayer";
1014
- const viewport = pdfPageProxy.getViewport({ scale: 1 });
1031
+ const viewport = pdfPageProxy.getViewport({ scale: 1 }).clone({
1032
+ dontFlip: true
1033
+ });
1015
1034
  const annotationLayerConfig = {
1016
1035
  div: annotationLayerRef.current,
1017
1036
  viewport,
1018
1037
  page: pdfPageProxy,
1019
1038
  linkService,
1039
+ annotationStorage: void 0,
1020
1040
  accessibilityManager: void 0,
1021
1041
  annotationCanvasMap: void 0,
1022
1042
  annotationEditorUIManager: void 0,
1023
- structTreeLayer: void 0
1043
+ structTreeLayer: void 0,
1044
+ commentManager: void 0
1024
1045
  };
1025
- const annotationLayer = new AnnotationLayer(annotationLayerConfig);
1026
- annotationLayerObjectRef.current = annotationLayer;
1027
1046
  const { cancel } = cancellable(
1028
1047
  (async () => {
1029
1048
  try {
1049
+ const { AnnotationLayer: AnnotationLayer2 } = await loadPdfJs();
1050
+ const annotationLayer = new AnnotationLayer2(annotationLayerConfig);
1051
+ annotationLayerObjectRef.current = annotationLayer;
1030
1052
  const annotations = await pdfPageProxy.getAnnotations();
1031
1053
  await annotationLayer.render({
1032
1054
  ...annotationLayerConfig,
@@ -1046,44 +1068,47 @@ var useAnnotationLayer = (params) => {
1046
1068
  annotationLayerRef
1047
1069
  };
1048
1070
  };
1049
- var AnnotationLayer2 = ({
1050
- renderForms = true,
1051
- externalLinksEnabled = true,
1052
- jumpOptions = { behavior: "smooth", align: "start" },
1053
- className,
1054
- style,
1055
- ...props
1056
- }) => {
1057
- const { annotationLayerRef } = useAnnotationLayer({
1058
- renderForms,
1059
- externalLinksEnabled,
1060
- jumpOptions
1061
- });
1062
- return /* @__PURE__ */ jsx(
1063
- "div",
1064
- {
1065
- className: clsx_default("annotationLayer", className),
1066
- style: {
1067
- ...style,
1068
- position: "absolute",
1069
- top: 0,
1070
- left: 0
1071
- },
1072
- ...props,
1073
- ref: annotationLayerRef
1074
- }
1075
- );
1076
- };
1071
+ var AnnotationLayer = memo(
1072
+ ({
1073
+ renderForms = true,
1074
+ externalLinksEnabled = true,
1075
+ jumpOptions = { behavior: "smooth", align: "start" },
1076
+ className,
1077
+ style,
1078
+ ...props
1079
+ }) => {
1080
+ const { annotationLayerRef } = useAnnotationLayer({
1081
+ renderForms,
1082
+ externalLinksEnabled,
1083
+ jumpOptions
1084
+ });
1085
+ return /* @__PURE__ */ jsx(
1086
+ "div",
1087
+ {
1088
+ className: clsx_default("annotationLayer", className),
1089
+ style: {
1090
+ ...style,
1091
+ position: "absolute",
1092
+ top: 0,
1093
+ left: 0
1094
+ },
1095
+ ...props,
1096
+ ref: annotationLayerRef
1097
+ }
1098
+ );
1099
+ }
1100
+ );
1101
+ AnnotationLayer.displayName = "AnnotationLayer";
1077
1102
  var useDpr = () => {
1078
1103
  const [dpr, setDPR] = useState(
1079
- !window ? 1 : Math.min(window.devicePixelRatio, 2)
1104
+ typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
1080
1105
  );
1081
1106
  useEffect(() => {
1082
- if (!window) {
1107
+ if (typeof window === "undefined") {
1083
1108
  return;
1084
1109
  }
1085
1110
  const handleDPRChange = () => {
1086
- setDPR(window.devicePixelRatio);
1111
+ setDPR(Math.min(window.devicePixelRatio, 2));
1087
1112
  };
1088
1113
  const windowMatch = window.matchMedia(
1089
1114
  `screen and (min-resolution: ${dpr}dppx)`
@@ -1146,6 +1171,7 @@ var useCanvasLayer = ({ background }) => {
1146
1171
  baseCanvas.style.transform = "translate(0px, 0px)";
1147
1172
  baseCanvas.style.zIndex = "0";
1148
1173
  baseCanvas.style.pointerEvents = "none";
1174
+ baseCanvas.style.backgroundColor = background ?? "white";
1149
1175
  const context = baseCanvas.getContext("2d");
1150
1176
  if (!context) {
1151
1177
  return;
@@ -1154,6 +1180,7 @@ var useCanvasLayer = ({ background }) => {
1154
1180
  context.clearRect(0, 0, baseCanvas.width, baseCanvas.height);
1155
1181
  const viewport = pdfPageProxy.getViewport({ scale: baseScale });
1156
1182
  const renderingTask = pdfPageProxy.render({
1183
+ canvas: baseCanvas,
1157
1184
  canvasContext: context,
1158
1185
  viewport,
1159
1186
  background
@@ -1172,8 +1199,74 @@ var useCanvasLayer = ({ background }) => {
1172
1199
  canvasRef
1173
1200
  };
1174
1201
  };
1202
+
1203
+ // src/lib/viewport-invalidation.ts
1204
+ var viewportInvalidationRegistry = /* @__PURE__ */ new WeakMap();
1205
+ var notifyCallbacks = (entry) => {
1206
+ entry.rafId = null;
1207
+ entry.callbacks.forEach((callback) => {
1208
+ callback();
1209
+ });
1210
+ };
1211
+ var scheduleInvalidation = (entry) => {
1212
+ if (entry.rafId !== null) {
1213
+ return;
1214
+ }
1215
+ entry.rafId = requestAnimationFrame(() => {
1216
+ notifyCallbacks(entry);
1217
+ });
1218
+ };
1219
+ var subscribeToViewportInvalidation = (viewport, callback) => {
1220
+ let entry = viewportInvalidationRegistry.get(viewport);
1221
+ if (!entry) {
1222
+ const callbacks = /* @__PURE__ */ new Set();
1223
+ const schedule = () => {
1224
+ const currentEntry = viewportInvalidationRegistry.get(viewport);
1225
+ if (!currentEntry) {
1226
+ return;
1227
+ }
1228
+ scheduleInvalidation(currentEntry);
1229
+ };
1230
+ entry = {
1231
+ callbacks,
1232
+ scrollListener: schedule,
1233
+ windowResizeListener: schedule,
1234
+ resizeObserver: typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule),
1235
+ rafId: null
1236
+ };
1237
+ viewport.addEventListener("scroll", entry.scrollListener, {
1238
+ passive: true
1239
+ });
1240
+ window.addEventListener("resize", entry.windowResizeListener, {
1241
+ passive: true
1242
+ });
1243
+ entry.resizeObserver?.observe(viewport);
1244
+ viewportInvalidationRegistry.set(viewport, entry);
1245
+ }
1246
+ entry.callbacks.add(callback);
1247
+ return () => {
1248
+ const currentEntry = viewportInvalidationRegistry.get(viewport);
1249
+ if (!currentEntry) {
1250
+ return;
1251
+ }
1252
+ currentEntry.callbacks.delete(callback);
1253
+ if (currentEntry.callbacks.size > 0) {
1254
+ return;
1255
+ }
1256
+ if (currentEntry.rafId !== null) {
1257
+ cancelAnimationFrame(currentEntry.rafId);
1258
+ }
1259
+ viewport.removeEventListener("scroll", currentEntry.scrollListener);
1260
+ window.removeEventListener("resize", currentEntry.windowResizeListener);
1261
+ currentEntry.resizeObserver?.disconnect();
1262
+ viewportInvalidationRegistry.delete(viewport);
1263
+ };
1264
+ };
1265
+
1266
+ // src/hooks/layers/useDetailCanvasLayer.tsx
1175
1267
  var MAX_CANVAS_PIXELS2 = 16777216;
1176
1268
  var MAX_CANVAS_DIMENSION2 = 32767;
1269
+ var DETAIL_RENDER_IDLE_MS = 80;
1177
1270
  var useDetailCanvasLayer = ({
1178
1271
  background,
1179
1272
  baseCanvasRef
@@ -1183,29 +1276,23 @@ var useDetailCanvasLayer = ({
1183
1276
  const pageNumber = usePDFPageNumber();
1184
1277
  const dpr = useDpr();
1185
1278
  const bouncyZoom = usePdf((state) => state.zoom);
1279
+ const isPinching = usePdf((state) => state.isPinching);
1186
1280
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
1187
1281
  const viewportRef = usePdf((state) => state.viewportRef);
1188
1282
  const [zoom] = useDebounce(bouncyZoom, 200);
1189
- const [scrollTick, setScrollTick] = useState(0);
1190
- const [debouncedScrollTick] = useDebounce(scrollTick, 20);
1191
- const ensureDetailCanvas = useCallback(() => {
1192
- let detailCanvas = detailCanvasRef.current;
1283
+ const getDetailCanvas = useCallback(() => {
1284
+ const detailCanvas = detailCanvasRef.current;
1193
1285
  if (!detailCanvas) {
1194
- const parent = baseCanvasRef.current?.parentElement;
1195
- if (!parent) {
1196
- return null;
1197
- }
1198
- detailCanvas = document.createElement("canvas");
1199
- parent.appendChild(detailCanvas);
1200
- detailCanvasRef.current = detailCanvas;
1286
+ return null;
1201
1287
  }
1202
1288
  detailCanvas.style.position = "absolute";
1203
1289
  detailCanvas.style.top = "0";
1204
1290
  detailCanvas.style.left = "0";
1205
1291
  detailCanvas.style.pointerEvents = "none";
1206
- detailCanvas.style.zIndex = "0";
1292
+ detailCanvas.style.zIndex = "1";
1293
+ detailCanvas.style.backgroundColor = background ?? "white";
1207
1294
  return detailCanvas;
1208
- }, [baseCanvasRef]);
1295
+ }, [background]);
1209
1296
  const clampScaleForPage = useCallback(
1210
1297
  (targetScale, pageWidth, pageHeight) => {
1211
1298
  if (!targetScale) {
@@ -1228,151 +1315,183 @@ var useDetailCanvasLayer = ({
1228
1315
  );
1229
1316
  useLayoutEffect(() => {
1230
1317
  const scrollContainer = viewportRef?.current;
1231
- if (!scrollContainer) return;
1232
- const handleScroll = () => {
1233
- setScrollTick((prev) => prev + 1);
1234
- };
1235
- scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
1236
- return () => {
1237
- scrollContainer.removeEventListener("scroll", handleScroll);
1238
- };
1239
- }, [viewportRef?.current]);
1240
- useLayoutEffect(() => {
1241
- if (!viewportRef?.current) {
1318
+ if (!scrollContainer) {
1242
1319
  return;
1243
1320
  }
1244
- const detailCanvas = ensureDetailCanvas();
1321
+ const detailCanvas = getDetailCanvas();
1245
1322
  const container = containerRef.current;
1246
1323
  if (!detailCanvas || !container) {
1247
1324
  return;
1248
1325
  }
1249
- const scrollContainer = viewportRef.current;
1250
- const pageContainer = baseCanvasRef.current?.parentElement ?? null;
1251
- if (!pageContainer) {
1252
- detailCanvas.style.display = "none";
1253
- detailCanvas.width = 0;
1254
- detailCanvas.height = 0;
1255
- return;
1256
- }
1257
- const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
1258
- const pageWidth = baseViewport.width;
1259
- const pageHeight = baseViewport.height;
1260
- const scrollX = scrollContainer.scrollLeft / zoom;
1261
- const scrollY = scrollContainer.scrollTop / zoom;
1262
- const viewportWidth = scrollContainer.clientWidth / zoom;
1263
- const viewportHeight = scrollContainer.clientHeight / zoom;
1264
- const pageRect = pageContainer.getBoundingClientRect();
1265
- const containerRect = scrollContainer.getBoundingClientRect();
1266
- const pageLeft = (pageRect.left - containerRect.left) / zoom + scrollX;
1267
- const pageTop = (pageRect.top - containerRect.top) / zoom + scrollY;
1268
- const visibleLeft = Math.max(0, scrollX - pageLeft);
1269
- const visibleTop = Math.max(0, scrollY - pageTop);
1270
- const visibleRight = Math.min(
1271
- pageWidth,
1272
- scrollX + viewportWidth - pageLeft
1273
- );
1274
- const visibleBottom = Math.min(
1275
- pageHeight,
1276
- scrollY + viewportHeight - pageTop
1277
- );
1278
- const visibleWidth = Math.max(0, visibleRight - visibleLeft);
1279
- const visibleHeight = Math.max(0, visibleBottom - visibleTop);
1280
- const targetDetailScale = dpr * zoom * 1.3;
1281
- const baseTargetScale = dpr * Math.min(zoom, 1);
1282
- const baseScale = clampScaleForPage(baseTargetScale, pageWidth, pageHeight);
1283
- const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
1284
- if (!needsDetail || visibleWidth <= 0 || visibleHeight <= 0) {
1285
- detailCanvas.style.display = "none";
1286
- detailCanvas.width = 0;
1287
- detailCanvas.height = 0;
1288
- return;
1289
- }
1290
- detailCanvas.style.display = "block";
1291
- const pdfOffsetX = visibleLeft;
1292
- const pdfOffsetY = visibleTop;
1293
- const pdfWidth = visibleWidth * targetDetailScale;
1294
- const pdfHeight = visibleHeight * targetDetailScale;
1295
- const effectiveScale = targetDetailScale;
1296
- const actualWidth = pdfWidth;
1297
- const actualHeight = pdfHeight;
1298
- detailCanvas.width = actualWidth;
1299
- detailCanvas.height = actualHeight;
1300
- const scaledWidth = visibleWidth * zoom;
1301
- const scaledHeight = visibleHeight * zoom;
1302
- detailCanvas.style.width = `${scaledWidth}px`;
1303
- detailCanvas.style.height = `${scaledHeight}px`;
1304
- detailCanvas.style.transformOrigin = "center center";
1305
- detailCanvas.style.transform = `translate(${visibleLeft * zoom}px, ${visibleTop * zoom}px) `;
1306
- container.style.transform = `scale3d(${1 / zoom}, ${1 / zoom}, 1)`;
1307
- container.style.transformOrigin = `0 0`;
1308
- const context = detailCanvas.getContext("2d");
1309
- if (!context) {
1310
- return;
1311
- }
1312
- context.setTransform(1, 0, 0, 1, 0, 0);
1313
- context.clearRect(0, 0, detailCanvas.width, detailCanvas.height);
1314
- const transform = [
1315
- 1,
1316
- 0,
1317
- 0,
1318
- 1,
1319
- -pdfOffsetX * effectiveScale,
1320
- -pdfOffsetY * effectiveScale
1321
- ];
1322
- const detailViewport = pdfPageProxy.getViewport({ scale: effectiveScale });
1323
- const renderingTask = pdfPageProxy.render({
1324
- canvasContext: context,
1325
- viewport: detailViewport,
1326
- background,
1327
- transform
1328
- });
1329
- renderingTask.promise.catch((error) => {
1330
- if (error.name === "RenderingCancelledException") {
1326
+ let renderingTask = null;
1327
+ let renderTimeoutId = null;
1328
+ const hideDetailCanvas = () => {
1329
+ renderingTask?.cancel();
1330
+ detailCanvas.style.display = "block";
1331
+ detailCanvas.style.opacity = "0";
1332
+ container.style.transform = "";
1333
+ container.style.transformOrigin = "";
1334
+ };
1335
+ const renderDetailCanvas = () => {
1336
+ const pageContainer = baseCanvasRef.current?.parentElement ?? null;
1337
+ if (!pageContainer) {
1338
+ hideDetailCanvas();
1331
1339
  return;
1332
1340
  }
1333
- throw error;
1334
- });
1341
+ const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
1342
+ const pageWidth = baseViewport.width;
1343
+ const pageHeight = baseViewport.height;
1344
+ const scrollX = scrollContainer.scrollLeft / zoom;
1345
+ const scrollY = scrollContainer.scrollTop / zoom;
1346
+ const viewportWidth = scrollContainer.clientWidth / zoom;
1347
+ const viewportHeight = scrollContainer.clientHeight / zoom;
1348
+ const pageRect = pageContainer.getBoundingClientRect();
1349
+ const containerRect = scrollContainer.getBoundingClientRect();
1350
+ const pageLeft = (pageRect.left - containerRect.left) / zoom + scrollX;
1351
+ const pageTop = (pageRect.top - containerRect.top) / zoom + scrollY;
1352
+ const visibleLeft = Math.max(0, scrollX - pageLeft);
1353
+ const visibleTop = Math.max(0, scrollY - pageTop);
1354
+ const visibleRight = Math.min(
1355
+ pageWidth,
1356
+ scrollX + viewportWidth - pageLeft
1357
+ );
1358
+ const visibleBottom = Math.min(
1359
+ pageHeight,
1360
+ scrollY + viewportHeight - pageTop
1361
+ );
1362
+ const visibleWidth = Math.max(0, visibleRight - visibleLeft);
1363
+ const visibleHeight = Math.max(0, visibleBottom - visibleTop);
1364
+ const targetDetailScale = dpr * zoom * 1.3;
1365
+ const baseTargetScale = dpr * Math.min(zoom, 1);
1366
+ const baseScale = clampScaleForPage(
1367
+ baseTargetScale,
1368
+ pageWidth,
1369
+ pageHeight
1370
+ );
1371
+ const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
1372
+ if (isPinching || !needsDetail || visibleWidth <= 0 || visibleHeight <= 0) {
1373
+ hideDetailCanvas();
1374
+ return;
1375
+ }
1376
+ renderingTask?.cancel();
1377
+ detailCanvas.style.display = "block";
1378
+ detailCanvas.style.opacity = "0";
1379
+ const pdfOffsetX = visibleLeft;
1380
+ const pdfOffsetY = visibleTop;
1381
+ const effectiveScale = targetDetailScale;
1382
+ const actualWidth = visibleWidth * effectiveScale;
1383
+ const actualHeight = visibleHeight * effectiveScale;
1384
+ detailCanvas.width = actualWidth;
1385
+ detailCanvas.height = actualHeight;
1386
+ detailCanvas.style.width = `${visibleWidth * zoom}px`;
1387
+ detailCanvas.style.height = `${visibleHeight * zoom}px`;
1388
+ detailCanvas.style.transformOrigin = "center center";
1389
+ detailCanvas.style.transform = `translate(${visibleLeft * zoom}px, ${visibleTop * zoom}px)`;
1390
+ container.style.transform = `scale3d(${1 / zoom}, ${1 / zoom}, 1)`;
1391
+ container.style.transformOrigin = "0 0";
1392
+ const context = detailCanvas.getContext("2d");
1393
+ if (!context) {
1394
+ return;
1395
+ }
1396
+ context.setTransform(1, 0, 0, 1, 0, 0);
1397
+ context.clearRect(0, 0, detailCanvas.width, detailCanvas.height);
1398
+ const transform = [
1399
+ 1,
1400
+ 0,
1401
+ 0,
1402
+ 1,
1403
+ -pdfOffsetX * effectiveScale,
1404
+ -pdfOffsetY * effectiveScale
1405
+ ];
1406
+ const detailViewport = pdfPageProxy.getViewport({
1407
+ scale: effectiveScale
1408
+ });
1409
+ const currentRenderingTask = pdfPageProxy.render({
1410
+ canvas: detailCanvas,
1411
+ canvasContext: context,
1412
+ viewport: detailViewport,
1413
+ background,
1414
+ transform
1415
+ });
1416
+ renderingTask = currentRenderingTask;
1417
+ currentRenderingTask.promise.then(() => {
1418
+ if (renderingTask === currentRenderingTask) {
1419
+ detailCanvas.style.opacity = "1";
1420
+ }
1421
+ }).catch((error) => {
1422
+ if (error.name === "RenderingCancelledException") {
1423
+ return;
1424
+ }
1425
+ throw error;
1426
+ }).finally(() => {
1427
+ if (renderingTask === currentRenderingTask) {
1428
+ renderingTask = null;
1429
+ }
1430
+ });
1431
+ };
1432
+ const scheduleRender = (delay = DETAIL_RENDER_IDLE_MS) => {
1433
+ if (renderTimeoutId !== null) {
1434
+ clearTimeout(renderTimeoutId);
1435
+ }
1436
+ hideDetailCanvas();
1437
+ renderTimeoutId = setTimeout(() => {
1438
+ renderTimeoutId = null;
1439
+ renderDetailCanvas();
1440
+ }, delay);
1441
+ };
1442
+ const unsubscribe = subscribeToViewportInvalidation(
1443
+ scrollContainer,
1444
+ scheduleRender
1445
+ );
1446
+ scheduleRender(isPinching ? DETAIL_RENDER_IDLE_MS * 2 : 0);
1335
1447
  return () => {
1336
- void renderingTask.cancel();
1448
+ unsubscribe();
1449
+ if (renderTimeoutId !== null) {
1450
+ clearTimeout(renderTimeoutId);
1451
+ }
1452
+ void renderingTask?.cancel();
1337
1453
  };
1338
1454
  }, [
1339
1455
  pdfPageProxy,
1340
1456
  zoom,
1457
+ isPinching,
1341
1458
  background,
1342
1459
  dpr,
1343
1460
  viewportRef,
1344
- ensureDetailCanvas,
1461
+ getDetailCanvas,
1345
1462
  clampScaleForPage,
1346
- baseCanvasRef,
1347
- debouncedScrollTick
1463
+ baseCanvasRef
1348
1464
  ]);
1349
1465
  return {
1350
1466
  detailCanvasRef,
1351
1467
  containerRef
1352
1468
  };
1353
1469
  };
1354
- var CanvasLayer = ({
1355
- style,
1356
- background,
1357
- ...props
1358
- }) => {
1359
- const { canvasRef } = useCanvasLayer({ background });
1360
- const { detailCanvasRef, containerRef } = useDetailCanvasLayer({
1470
+ var CanvasLayer = memo(
1471
+ ({
1472
+ style,
1361
1473
  background,
1362
- baseCanvasRef: canvasRef
1363
- });
1364
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1365
- /* @__PURE__ */ jsx("canvas", { ...props, ref: canvasRef, style }),
1366
- /* @__PURE__ */ jsx(
1367
- "div",
1368
- {
1369
- ref: containerRef,
1370
- className: "absolute top-0 left-0 w-full h-full flex items-center justify-center",
1371
- children: /* @__PURE__ */ jsx("canvas", { ref: detailCanvasRef })
1372
- }
1373
- )
1374
- ] });
1375
- };
1474
+ ...props
1475
+ }) => {
1476
+ const { canvasRef } = useCanvasLayer({ background });
1477
+ const { detailCanvasRef, containerRef } = useDetailCanvasLayer({
1478
+ background,
1479
+ baseCanvasRef: canvasRef
1480
+ });
1481
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1482
+ /* @__PURE__ */ jsx("canvas", { ...props, ref: canvasRef, style }),
1483
+ /* @__PURE__ */ jsx(
1484
+ "div",
1485
+ {
1486
+ ref: containerRef,
1487
+ className: "absolute top-0 left-0 w-full h-full flex items-center justify-center",
1488
+ children: /* @__PURE__ */ jsx("canvas", { ref: detailCanvasRef })
1489
+ }
1490
+ )
1491
+ ] });
1492
+ }
1493
+ );
1494
+ CanvasLayer.displayName = "CanvasLayer";
1376
1495
 
1377
1496
  // src/hooks/useSelectionDimensions.tsx
1378
1497
  var MERGE_THRESHOLD = 2;
@@ -2118,14 +2237,13 @@ var ColoredHighlightLayer = ({
2118
2237
  )
2119
2238
  ] });
2120
2239
  };
2121
-
2122
- // src/components/layers/custom-layer.tsx
2123
- var CustomLayer = ({
2124
- children
2125
- }) => {
2126
- const pageNumber = usePDFPageNumber();
2127
- return children(pageNumber);
2128
- };
2240
+ var CustomLayer = memo(
2241
+ ({ children }) => {
2242
+ const pageNumber = usePDFPageNumber();
2243
+ return children(pageNumber);
2244
+ }
2245
+ );
2246
+ CustomLayer.displayName = "CustomLayer";
2129
2247
  var convertToPercentString = (rect) => {
2130
2248
  return {
2131
2249
  top: `${rect.top}%`,
@@ -2319,25 +2437,33 @@ var useTextLayer = () => {
2319
2437
  if (!textContainer || isRenderingRef.current) {
2320
2438
  return;
2321
2439
  }
2440
+ let isCancelled = false;
2322
2441
  isRenderingRef.current = true;
2323
2442
  textContainer.innerHTML = "";
2324
2443
  if (textLayerRef.current) {
2325
2444
  textLayerRef.current.cancel();
2326
2445
  textLayerRef.current = null;
2327
2446
  }
2328
- const textLayer = new TextLayer({
2329
- textContentSource: pdfPageProxy.streamTextContent(),
2330
- container: textContainer,
2331
- viewport: pdfPageProxy.getViewport({ scale: 1 })
2332
- });
2333
- textLayerRef.current = textLayer;
2334
- textLayer.render().then(() => {
2335
- if (textLayerRef.current === textLayer && textContainer) {
2336
- const endOfContent = document.createElement("div");
2337
- endOfContent.className = "endOfContent";
2338
- textContainer.appendChild(endOfContent);
2339
- bindMouseEvents(textContainer, endOfContent);
2447
+ void loadPdfJs().then(({ TextLayer: TextLayer2 }) => {
2448
+ if (isCancelled || textContainerRef.current !== textContainer) {
2449
+ return;
2450
+ }
2451
+ const textLayer = new TextLayer2({
2452
+ textContentSource: pdfPageProxy.streamTextContent(),
2453
+ container: textContainer,
2454
+ viewport: pdfPageProxy.getViewport({ scale: 1 })
2455
+ });
2456
+ textLayerRef.current = textLayer;
2457
+ return textLayer.render();
2458
+ }).then(() => {
2459
+ const textLayer = textLayerRef.current;
2460
+ if (!textLayer || isCancelled || textContainerRef.current !== textContainer) {
2461
+ return;
2340
2462
  }
2463
+ const endOfContent = document.createElement("div");
2464
+ endOfContent.className = "endOfContent";
2465
+ textContainer.appendChild(endOfContent);
2466
+ bindMouseEvents(textContainer, endOfContent);
2341
2467
  }).catch((error) => {
2342
2468
  if (error.name !== "AbortException") {
2343
2469
  console.error("TextLayer rendering error:", error);
@@ -2346,6 +2472,7 @@ var useTextLayer = () => {
2346
2472
  isRenderingRef.current = false;
2347
2473
  });
2348
2474
  return () => {
2475
+ isCancelled = true;
2349
2476
  isRenderingRef.current = false;
2350
2477
  if (textLayerRef.current) {
2351
2478
  textLayerRef.current.cancel();
@@ -2362,30 +2489,29 @@ var useTextLayer = () => {
2362
2489
  pageNumber: pdfPageProxy.pageNumber
2363
2490
  };
2364
2491
  };
2365
- var TextLayer2 = ({
2366
- className,
2367
- style,
2368
- ...props
2369
- }) => {
2370
- const { textContainerRef, pageNumber } = useTextLayer();
2371
- return /* @__PURE__ */ jsx(
2372
- "div",
2373
- {
2374
- className: clsx_default("textLayer", className),
2375
- style: {
2376
- ...style,
2377
- position: "absolute",
2378
- top: 0,
2379
- left: 0
2380
- },
2381
- ...props,
2382
- ...{
2383
- "data-page-number": pageNumber
2384
- },
2385
- ref: textContainerRef
2386
- }
2387
- );
2388
- };
2492
+ var TextLayer = memo(
2493
+ ({ className, style, ...props }) => {
2494
+ const { textContainerRef, pageNumber } = useTextLayer();
2495
+ return /* @__PURE__ */ jsx(
2496
+ "div",
2497
+ {
2498
+ className: clsx_default("textLayer", className),
2499
+ style: {
2500
+ ...style,
2501
+ position: "absolute",
2502
+ top: 0,
2503
+ left: 0
2504
+ },
2505
+ ...props,
2506
+ ...{
2507
+ "data-page-number": pageNumber
2508
+ },
2509
+ ref: textContainerRef
2510
+ }
2511
+ );
2512
+ }
2513
+ );
2514
+ TextLayer.displayName = "TextLayer";
2389
2515
  var usePDFOutline = () => {
2390
2516
  const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
2391
2517
  const [outline, setOutline] = useState();
@@ -2523,39 +2649,44 @@ var Outline = ({
2523
2649
  });
2524
2650
  }) });
2525
2651
  };
2526
- var Page = ({
2527
- children,
2528
- pageNumber = 1,
2529
- style,
2530
- ...props
2531
- }) => {
2532
- const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
2533
- const width = (pdfPageProxy.view[2] ?? 0) - (pdfPageProxy.view[0] ?? 0);
2534
- const height = (pdfPageProxy.view[3] ?? 0) - (pdfPageProxy.view[1] ?? 0);
2535
- return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value: pdfPageProxy.pageNumber, children: /* @__PURE__ */ jsx(
2536
- Primitive.div,
2537
- {
2538
- style: {
2539
- display: "block"
2540
- },
2541
- children: /* @__PURE__ */ jsx(
2542
- "div",
2543
- {
2544
- style: {
2545
- ...style,
2546
- "--scale-factor": 1,
2547
- "--total-scale-factor": 1,
2548
- position: "relative",
2549
- width,
2550
- height
2551
- },
2552
- ...props,
2553
- children
2554
- }
2555
- )
2556
- }
2557
- ) });
2558
- };
2652
+ var Page = memo(
2653
+ ({
2654
+ children,
2655
+ pageNumber = 1,
2656
+ style,
2657
+ ...props
2658
+ }) => {
2659
+ const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
2660
+ const viewport = usePdf((state) => state.viewports[pageNumber - 1]);
2661
+ const width = viewport?.width ?? (pdfPageProxy.view[2] ?? 0) - (pdfPageProxy.view[0] ?? 0);
2662
+ const height = viewport?.height ?? (pdfPageProxy.view[3] ?? 0) - (pdfPageProxy.view[1] ?? 0);
2663
+ return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value: pdfPageProxy.pageNumber, children: /* @__PURE__ */ jsx(
2664
+ Primitive.div,
2665
+ {
2666
+ style: {
2667
+ display: "block"
2668
+ },
2669
+ children: /* @__PURE__ */ jsx(
2670
+ "div",
2671
+ {
2672
+ style: {
2673
+ "--scale-factor": 1,
2674
+ "--total-scale-factor": 1,
2675
+ backgroundColor: "white",
2676
+ position: "relative",
2677
+ width,
2678
+ height,
2679
+ ...style
2680
+ },
2681
+ ...props,
2682
+ children
2683
+ }
2684
+ )
2685
+ }
2686
+ ) });
2687
+ }
2688
+ );
2689
+ Page.displayName = "Page";
2559
2690
  var NextPage = () => {
2560
2691
  };
2561
2692
  var PreviousPage = () => {
@@ -3176,10 +3307,10 @@ var Engine = class {
3176
3307
  const config = this.config;
3177
3308
  if (!state._active) this.clean();
3178
3309
  if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return;
3179
- const memo = this.handler(_objectSpread2(_objectSpread2(_objectSpread2({}, shared), state), {}, {
3310
+ const memo7 = this.handler(_objectSpread2(_objectSpread2(_objectSpread2({}, shared), state), {}, {
3180
3311
  [this.aliasKey]: state.values
3181
3312
  }));
3182
- if (memo !== void 0) state.memo = memo;
3313
+ if (memo7 !== void 0) state.memo = memo7;
3183
3314
  }
3184
3315
  clean() {
3185
3316
  this.eventStore.clean();
@@ -4528,11 +4659,11 @@ Please add \`${key}Action\` when creating your handler.`);
4528
4659
  const startKey = handlerKey + "Start";
4529
4660
  const endKey = handlerKey + "End";
4530
4661
  const fn = (state) => {
4531
- let memo = void 0;
4662
+ let memo7 = void 0;
4532
4663
  if (state.first && startKey in handlers) handlers[startKey](state);
4533
- if (handlerKey in handlers) memo = handlers[handlerKey](state);
4664
+ if (handlerKey in handlers) memo7 = handlers[handlerKey](state);
4534
4665
  if (state.last && endKey in handlers) handlers[endKey](state);
4535
- return memo;
4666
+ return memo7;
4536
4667
  };
4537
4668
  internalHandlers[key] = fn;
4538
4669
  config[key] = config[key] || {};
@@ -4584,8 +4715,8 @@ function useGesture(handlers, config) {
4584
4715
  }
4585
4716
 
4586
4717
  // src/lib/memo.ts
4587
- var firstMemo = (first, memo, initializer) => {
4588
- if (!first && !memo) {
4718
+ var firstMemo = (first, memo7, initializer) => {
4719
+ if (!first && !memo7) {
4589
4720
  throw new Error(
4590
4721
  "Missing memo initialization. You likely forgot to return the result of `firstMemo` in the event function"
4591
4722
  );
@@ -4593,7 +4724,7 @@ var firstMemo = (first, memo, initializer) => {
4593
4724
  if (first) {
4594
4725
  return initializer();
4595
4726
  }
4596
- return memo;
4727
+ return memo7;
4597
4728
  };
4598
4729
 
4599
4730
  // src/hooks/viewport/useViewportContainer.tsx
@@ -4725,13 +4856,13 @@ var useViewportContainer = ({
4725
4856
  useGesture(
4726
4857
  {
4727
4858
  onPinch: (state) => {
4728
- const { origin: origin2, first, movement, delta, event, memo } = state;
4859
+ const { origin: origin2, first, movement, delta, event, memo: memo7 } = state;
4729
4860
  const [ms] = movement;
4730
4861
  const [deltaScale] = delta;
4731
4862
  const currentElement = elementRef.current;
4732
4863
  const currentContainer = containerRef.current;
4733
4864
  if (!currentElement || !currentContainer) return;
4734
- const newMemo = firstMemo(first, memo, () => {
4865
+ const newMemo = firstMemo(first, memo7, () => {
4735
4866
  const elementRect = currentElement.getBoundingClientRect();
4736
4867
  const containerRect = currentContainer.getBoundingClientRect();
4737
4868
  const currentZoom = transformations.current.zoom;
@@ -4800,10 +4931,39 @@ var useViewportContainer = ({
4800
4931
  };
4801
4932
  var DEFAULT_HEIGHT = 600;
4802
4933
  var EXTRA_HEIGHT = 0;
4934
+ var VirtualizedPageItem = memo(
4935
+ ({ child, virtualItem, innerBoxWidth }) => {
4936
+ return /* @__PURE__ */ jsx(
4937
+ "div",
4938
+ {
4939
+ style: {
4940
+ width: innerBoxWidth,
4941
+ height: "0px"
4942
+ },
4943
+ children: /* @__PURE__ */ jsx(
4944
+ "div",
4945
+ {
4946
+ style: {
4947
+ height: `${virtualItem.size}px`,
4948
+ transform: `translateY(${virtualItem.start}px)`
4949
+ },
4950
+ children: cloneElement(child, {
4951
+ key: virtualItem.key,
4952
+ //@ts-expect-error pageNumber is not a valid react key
4953
+ pageNumber: virtualItem.index + 1
4954
+ })
4955
+ }
4956
+ )
4957
+ }
4958
+ );
4959
+ },
4960
+ (prev, next) => prev.child === next.child && prev.innerBoxWidth === next.innerBoxWidth && prev.virtualItem.key === next.virtualItem.key && prev.virtualItem.index === next.virtualItem.index && prev.virtualItem.size === next.virtualItem.size && prev.virtualItem.start === next.virtualItem.start
4961
+ );
4962
+ VirtualizedPageItem.displayName = "VirtualizedPageItem";
4803
4963
  var Pages = ({
4804
4964
  children,
4805
4965
  gap = 10,
4806
- virtualizerOptions = { overscan: 2 },
4966
+ virtualizerOptions = { overscan: 1 },
4807
4967
  initialOffset,
4808
4968
  onOffsetChange,
4809
4969
  ...props
@@ -4869,7 +5029,9 @@ var Pages = ({
4869
5029
  });
4870
5030
  useFitWidth({ viewportRef: containerRef });
4871
5031
  const largestPageWidth = usePdf(
4872
- (state) => Math.max(...state.viewports.map((v) => v.width))
5032
+ (state) => state.viewports.reduce((largestWidth, viewport) => {
5033
+ return Math.max(largestWidth, viewport.width);
5034
+ }, 0)
4873
5035
  );
4874
5036
  useEffect(() => {
4875
5037
  virtualizer.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
@@ -4930,26 +5092,11 @@ var Pages = ({
4930
5092
  const innerBoxWidth = viewports?.[virtualItem.index] ? viewports[virtualItem.index]?.width : 0;
4931
5093
  if (!innerBoxWidth) return null;
4932
5094
  return /* @__PURE__ */ jsx(
4933
- "div",
5095
+ VirtualizedPageItem,
4934
5096
  {
4935
- style: {
4936
- width: innerBoxWidth,
4937
- height: `0px`
4938
- },
4939
- children: /* @__PURE__ */ jsx(
4940
- "div",
4941
- {
4942
- style: {
4943
- height: `${virtualItem.size}px`,
4944
- transform: `translateY(${virtualItem.start}px)`
4945
- },
4946
- children: cloneElement(children, {
4947
- key: virtualItem.key,
4948
- //@ts-expect-error pageNumber is not a valid react key
4949
- pageNumber: virtualItem.index + 1
4950
- })
4951
- }
4952
- )
5097
+ child: children,
5098
+ virtualItem,
5099
+ innerBoxWidth
4953
5100
  },
4954
5101
  virtualItem.key
4955
5102
  );
@@ -5005,28 +5152,44 @@ var usePDFDocumentContext = ({
5005
5152
  const loadDocument = () => {
5006
5153
  setInitialState(null);
5007
5154
  setProgress(0);
5008
- const loadingTask = getDocument(source);
5009
- loadingTask.onProgress = (progressEvent) => {
5010
- if (progressEvent.loaded === progressEvent.total) {
5155
+ let loadingTask = null;
5156
+ let isDisposed = false;
5157
+ void loadPdfJs().then(({ getDocument }) => {
5158
+ if (isDisposed) {
5011
5159
  return;
5012
5160
  }
5013
- setProgress(progressEvent.loaded / progressEvent.total);
5014
- };
5015
- const loadingPromise = loadingTask.promise.then((proxy) => {
5016
- onDocumentLoad?.({ proxy, source });
5017
- setProgress(1);
5018
- generateViewports(proxy);
5161
+ loadingTask = getDocument(source);
5162
+ loadingTask.onProgress = (progressEvent) => {
5163
+ if (progressEvent.loaded === progressEvent.total) {
5164
+ return;
5165
+ }
5166
+ setProgress(progressEvent.loaded / progressEvent.total);
5167
+ };
5168
+ return loadingTask.promise.then((proxy) => {
5169
+ if (isDisposed || loadingTask?.destroyed) {
5170
+ return;
5171
+ }
5172
+ onDocumentLoad?.({ proxy, source });
5173
+ setProgress(1);
5174
+ return generateViewports(proxy);
5175
+ }).catch((error) => {
5176
+ if (isDisposed || loadingTask?.destroyed) {
5177
+ return;
5178
+ }
5179
+ console.error("Error loading PDF document", error);
5180
+ });
5019
5181
  }).catch((error) => {
5020
- if (loadingTask.destroyed) {
5182
+ if (isDisposed) {
5021
5183
  return;
5022
5184
  }
5023
- console.error("Error loading PDF document", error);
5185
+ console.error("Error loading PDF.js", error);
5024
5186
  });
5025
5187
  return () => {
5026
- loadingPromise.finally(() => loadingTask.destroy());
5188
+ isDisposed = true;
5189
+ void loadingTask?.destroy();
5027
5190
  };
5028
5191
  };
5029
- loadDocument();
5192
+ return loadDocument();
5030
5193
  }, [source]);
5031
5194
  return {
5032
5195
  initialState
@@ -5117,6 +5280,7 @@ var useThumbnail = (pageNumber, config = {}) => {
5117
5280
  const context = canvas.getContext("2d");
5118
5281
  if (!context) return;
5119
5282
  const renderTask = pageProxy.render({
5283
+ canvas,
5120
5284
  canvasContext: context,
5121
5285
  viewport: scaledViewport
5122
5286
  });
@@ -5467,4 +5631,4 @@ function mergeAdjacentRects(rects) {
5467
5631
  return merged;
5468
5632
  }
5469
5633
 
5470
- export { AnnotationHighlightLayer, AnnotationLayer2 as AnnotationLayer, AnnotationTooltip, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer2 as TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePdf, usePdfJump, useSearch, useSelectionDimensions };
5634
+ export { AnnotationHighlightLayer, AnnotationLayer, AnnotationTooltip, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePdf, usePdfJump, useSearch, useSelectionDimensions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaralabs/lector",
3
- "version": "3.8.0",
3
+ "version": "3.10.0",
4
4
  "description": "Headless PDF viewer for React",
5
5
  "author": "andrewdr (https://github.com/andrewdoro)",
6
6
  "license": "MIT",
@@ -44,7 +44,7 @@
44
44
  "@vitejs/plugin-react": "^4.3.4",
45
45
  "@vitest/browser": "^2.0.4",
46
46
  "clsx": "^2.1.1",
47
- "pdfjs-dist": "^4.10.38",
47
+ "pdfjs-dist": "^5.5.207",
48
48
  "playwright": "^1.45.3",
49
49
  "react": "^19.1.1",
50
50
  "size-limit": "^11.1.6",
@@ -57,7 +57,7 @@
57
57
  "webdriverio": "^8.39.1"
58
58
  },
59
59
  "peerDependencies": {
60
- "pdfjs-dist": "^4.9",
60
+ "pdfjs-dist": "^5.5.207",
61
61
  "react": ">=19"
62
62
  },
63
63
  "dependencies": {
@@ -84,7 +84,7 @@
84
84
  "repository": {
85
85
  "type": "git",
86
86
  "url": "git+https://github.com/anaralabs/lector.git",
87
- "directory": "packages/nuqs"
87
+ "directory": "packages/lector"
88
88
  },
89
89
  "bugs": {
90
90
  "url": "https://github.com/anaralabs/lector/issues"