@anaralabs/lector 3.12.4 → 3.12.6

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.
@@ -326,6 +326,8 @@ declare interface PDFState {
326
326
  zoomFitWidth: () => void;
327
327
  isPinching: boolean;
328
328
  setIsPinching: (isPinching: boolean) => void;
329
+ isResizing: boolean;
330
+ setIsResizing: (isResizing: boolean) => void;
329
331
  currentPage: number;
330
332
  setCurrentPage: (pageNumber: number) => void;
331
333
  viewports: Array<PageViewport>;
package/dist/index.js CHANGED
@@ -86,6 +86,12 @@ var PDFStore = createZustandContext(
86
86
  isPinching: val
87
87
  });
88
88
  },
89
+ isResizing: false,
90
+ setIsResizing: (val) => {
91
+ set({
92
+ isResizing: val
93
+ });
94
+ },
89
95
  virtualizer: null,
90
96
  setVirtualizer: (val) => {
91
97
  set({
@@ -1244,9 +1250,11 @@ function setCachedBitmap(docId, proxy, scale, background, bitmap) {
1244
1250
  }
1245
1251
  var useCanvasLayer = ({ background }) => {
1246
1252
  const canvasRef = useRef(null);
1253
+ const hasContentRef = useRef(false);
1247
1254
  const pageNumber = usePDFPageNumber();
1248
1255
  const dpr = useDpr();
1249
1256
  const bouncyZoom = usePdf((state) => state.zoom);
1257
+ const isResizing = usePdf((state) => state.isResizing);
1250
1258
  const docId = usePdf((state) => state.pdfDocumentProxy.fingerprints[0] ?? "");
1251
1259
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
1252
1260
  const markPageRendered = usePdf((state) => state.markPageRendered);
@@ -1258,6 +1266,13 @@ var useCanvasLayer = ({ background }) => {
1258
1266
  const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
1259
1267
  const pageWidth = baseViewport.width;
1260
1268
  const pageHeight = baseViewport.height;
1269
+ if (isResizing && hasContentRef.current) {
1270
+ baseCanvas.style.visibility = "";
1271
+ baseCanvas.style.width = `${pageWidth}px`;
1272
+ baseCanvas.style.height = `${pageHeight}px`;
1273
+ return;
1274
+ }
1275
+ hasContentRef.current = false;
1261
1276
  const targetBaseScale = dpr * Math.min(zoom, 1);
1262
1277
  const baseScale = clampScaleForPage(targetBaseScale, pageWidth, pageHeight);
1263
1278
  baseCanvas.width = Math.floor(pageWidth * baseScale);
@@ -1278,6 +1293,7 @@ var useCanvasLayer = ({ background }) => {
1278
1293
  if (cached) {
1279
1294
  context.drawImage(cached, 0, 0);
1280
1295
  markPageRendered(pageNumber);
1296
+ hasContentRef.current = true;
1281
1297
  return;
1282
1298
  }
1283
1299
  context.setTransform(1, 0, 0, 1, 0, 0);
@@ -1313,6 +1329,7 @@ var useCanvasLayer = ({ background }) => {
1313
1329
  }
1314
1330
  baseCanvas.style.visibility = "";
1315
1331
  markPageRendered(pageNumber);
1332
+ hasContentRef.current = true;
1316
1333
  if (typeof createImageBitmap !== "undefined") {
1317
1334
  createImageBitmap(renderCanvas).then((bitmap) => {
1318
1335
  if (cancelled) {
@@ -1347,6 +1364,7 @@ var useCanvasLayer = ({ background }) => {
1347
1364
  background,
1348
1365
  dpr,
1349
1366
  zoom,
1367
+ isResizing,
1350
1368
  pageNumber,
1351
1369
  markPageRendered,
1352
1370
  docId
@@ -1439,6 +1457,7 @@ var useDetailCanvasLayer = ({
1439
1457
  const detailCanvasRef = useRef(null);
1440
1458
  const pageNumber = usePDFPageNumber();
1441
1459
  const dpr = useDpr();
1460
+ const store = PDFStore.useContext();
1442
1461
  const bouncyZoom = usePdf((state) => state.zoom);
1443
1462
  const isPinching = usePdf((state) => state.isPinching);
1444
1463
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
@@ -1473,12 +1492,29 @@ var useDetailCanvasLayer = ({
1473
1492
  container.style.cssText = "";
1474
1493
  };
1475
1494
  const renderDetailCanvas = () => {
1495
+ const virtualizer = store.getState().virtualizer;
1496
+ if (virtualizer?.isScrolling) {
1497
+ scheduleRender(160);
1498
+ return;
1499
+ }
1476
1500
  const pageContainer = baseCanvasRef.current?.parentElement ?? null;
1477
1501
  if (!pageContainer) {
1478
1502
  hideDetailCanvas();
1479
1503
  return;
1480
1504
  }
1481
1505
  const { width: pageWidth, height: pageHeight } = pageDimsRef.current;
1506
+ const targetDetailScale = dpr * zoom * 1.3;
1507
+ const baseTargetScale = dpr * Math.min(zoom, 1);
1508
+ const baseScale = clampScaleForPage(
1509
+ baseTargetScale,
1510
+ pageWidth,
1511
+ pageHeight
1512
+ );
1513
+ const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
1514
+ if (isPinching || !needsDetail) {
1515
+ hideDetailCanvas();
1516
+ return;
1517
+ }
1482
1518
  const scrollX = scrollContainer.scrollLeft / zoom;
1483
1519
  const scrollY = scrollContainer.scrollTop / zoom;
1484
1520
  const viewportWidth = scrollContainer.clientWidth / zoom;
@@ -1499,15 +1535,7 @@ var useDetailCanvasLayer = ({
1499
1535
  );
1500
1536
  const visibleWidth = Math.max(0, visibleRight - visibleLeft);
1501
1537
  const visibleHeight = Math.max(0, visibleBottom - visibleTop);
1502
- const targetDetailScale = dpr * zoom * 1.3;
1503
- const baseTargetScale = dpr * Math.min(zoom, 1);
1504
- const baseScale = clampScaleForPage(
1505
- baseTargetScale,
1506
- pageWidth,
1507
- pageHeight
1508
- );
1509
- const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
1510
- if (isPinching || !needsDetail || visibleWidth <= 0 || visibleHeight <= 0) {
1538
+ if (visibleWidth <= 0 || visibleHeight <= 0) {
1511
1539
  hideDetailCanvas();
1512
1540
  return;
1513
1541
  }
@@ -1600,7 +1628,8 @@ var useDetailCanvasLayer = ({
1600
1628
  background,
1601
1629
  dpr,
1602
1630
  viewportRef,
1603
- baseCanvasRef
1631
+ baseCanvasRef,
1632
+ store
1604
1633
  ]);
1605
1634
  useEffect(() => {
1606
1635
  const canvas = detailCanvasRef.current;
@@ -2633,51 +2662,51 @@ var createTextSelectionManager = () => {
2633
2662
  return bindMouseEvents2;
2634
2663
  };
2635
2664
  var bindMouseEvents = createTextSelectionManager();
2665
+ var TEXT_BUILD_IDLE_MS = 300;
2636
2666
  var useTextLayer = () => {
2637
2667
  const textContainerRef = useRef(null);
2638
2668
  const textLayerRef = useRef(null);
2639
- const isRenderingRef = useRef(false);
2640
2669
  const pageNumber = usePDFPageNumber();
2641
2670
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
2642
2671
  useEffect(() => {
2643
2672
  const textContainer = textContainerRef.current;
2644
- if (!textContainer || isRenderingRef.current) {
2673
+ if (!textContainer) {
2645
2674
  return;
2646
2675
  }
2647
2676
  let isCancelled = false;
2648
- isRenderingRef.current = true;
2649
- textContainer.innerHTML = "";
2650
- if (textLayerRef.current) {
2651
- textLayerRef.current.cancel();
2652
- textLayerRef.current = null;
2653
- }
2654
- void import('pdfjs-dist/legacy/build/pdf.mjs').then(({ TextLayer: TextLayer2 }) => {
2677
+ const buildTimer = setTimeout(() => {
2655
2678
  if (isCancelled || textContainerRef.current !== textContainer) return;
2656
- const textLayer = new TextLayer2({
2657
- textContentSource: pdfPageProxy.streamTextContent(),
2658
- container: textContainer,
2659
- viewport: pdfPageProxy.getViewport({ scale: 1 })
2660
- });
2661
- textLayerRef.current = textLayer;
2662
- return textLayer.render();
2663
- }).then(() => {
2664
- if (isCancelled || textContainerRef.current !== textContainer) {
2665
- return;
2666
- }
2667
- const endOfContent = document.createElement("div");
2668
- endOfContent.className = "endOfContent";
2669
- textContainer.appendChild(endOfContent);
2670
- bindMouseEvents(textContainer, endOfContent);
2671
- }).catch((error) => {
2672
- if (error instanceof Error && error.name !== "AbortException") {
2673
- console.error("TextLayer rendering error:", error);
2679
+ textContainer.innerHTML = "";
2680
+ if (textLayerRef.current) {
2681
+ textLayerRef.current.cancel();
2682
+ textLayerRef.current = null;
2674
2683
  }
2675
- }).finally(() => {
2676
- isRenderingRef.current = false;
2677
- });
2684
+ void import('pdfjs-dist/legacy/build/pdf.mjs').then(({ TextLayer: TextLayer2 }) => {
2685
+ if (isCancelled || textContainerRef.current !== textContainer) return;
2686
+ const textLayer = new TextLayer2({
2687
+ textContentSource: pdfPageProxy.streamTextContent(),
2688
+ container: textContainer,
2689
+ viewport: pdfPageProxy.getViewport({ scale: 1 })
2690
+ });
2691
+ textLayerRef.current = textLayer;
2692
+ return textLayer.render();
2693
+ }).then(() => {
2694
+ if (isCancelled || textContainerRef.current !== textContainer) {
2695
+ return;
2696
+ }
2697
+ const endOfContent = document.createElement("div");
2698
+ endOfContent.className = "endOfContent";
2699
+ textContainer.appendChild(endOfContent);
2700
+ bindMouseEvents(textContainer, endOfContent);
2701
+ }).catch((error) => {
2702
+ if (error instanceof Error && error.name !== "AbortException") {
2703
+ console.error("TextLayer rendering error:", error);
2704
+ }
2705
+ });
2706
+ }, TEXT_BUILD_IDLE_MS);
2678
2707
  return () => {
2679
2708
  isCancelled = true;
2680
- isRenderingRef.current = false;
2709
+ clearTimeout(buildTimer);
2681
2710
  if (textLayerRef.current) {
2682
2711
  textLayerRef.current.cancel();
2683
2712
  textLayerRef.current = null;
@@ -2704,7 +2733,11 @@ var TextLayer = memo(
2704
2733
  ...style,
2705
2734
  position: "absolute",
2706
2735
  top: 0,
2707
- left: 0
2736
+ left: 0,
2737
+ // Isolate the span subtree's layout so a getBoundingClientRect on
2738
+ // an ancestor (e.g. renderDetailCanvas) doesn't reflow every text
2739
+ // span on this page during scroll.
2740
+ contain: "layout style"
2708
2741
  },
2709
2742
  ...props,
2710
2743
  ...{
@@ -2972,32 +3005,57 @@ var TotalPages = ({ ...props }) => {
2972
3005
  const pages = usePdf((state) => state.pdfDocumentProxy.numPages);
2973
3006
  return /* @__PURE__ */ jsx("div", { ...props, children: pages });
2974
3007
  };
3008
+ var RESIZE_QUIET_MS = 200;
2975
3009
  var useFitWidth = ({ viewportRef }) => {
2976
3010
  const viewports = usePdf((state) => state.viewports);
2977
3011
  const zoomOptions = usePdf((state) => state.zoomOptions);
2978
3012
  const updateZoom = usePdf((state) => state.updateZoom);
3013
+ const setIsResizing = usePdf((state) => state.setIsResizing);
2979
3014
  const store = PDFStore.useContext();
3015
+ const pendingFrameRef = useRef(null);
3016
+ const latestWidthRef = useRef(null);
2980
3017
  useLayoutEffect(() => {
2981
3018
  if (viewportRef.current === null) return;
3019
+ let quietTimer = null;
2982
3020
  const resizeObserver = new ResizeObserver((entries) => {
2983
3021
  for (const entry of entries) {
2984
- const isFitWidth = store.getState().isZoomFitWidth;
2985
- if (entry.target === viewportRef.current && isFitWidth) {
2986
- const containerWidth = entry.contentRect.width;
3022
+ if (entry.target !== viewportRef.current) continue;
3023
+ const prevWidth = latestWidthRef.current;
3024
+ latestWidthRef.current = entry.contentRect.width;
3025
+ if (store.getState().isZoomFitWidth && prevWidth !== null && prevWidth !== entry.contentRect.width) {
3026
+ if (!store.getState().isResizing) setIsResizing(true);
3027
+ if (quietTimer) clearTimeout(quietTimer);
3028
+ quietTimer = setTimeout(() => {
3029
+ setIsResizing(false);
3030
+ quietTimer = null;
3031
+ }, RESIZE_QUIET_MS);
3032
+ }
3033
+ if (pendingFrameRef.current !== null) continue;
3034
+ pendingFrameRef.current = requestAnimationFrame(() => {
3035
+ pendingFrameRef.current = null;
3036
+ const containerWidth = latestWidthRef.current;
3037
+ if (containerWidth === null) return;
3038
+ if (!store.getState().isZoomFitWidth) return;
2987
3039
  const newZoom = getFitWidthZoom(
2988
3040
  containerWidth,
2989
3041
  viewports,
2990
3042
  zoomOptions
2991
3043
  );
2992
3044
  updateZoom(newZoom, true);
2993
- }
3045
+ });
2994
3046
  }
2995
3047
  });
2996
3048
  resizeObserver.observe(viewportRef.current);
2997
3049
  return () => {
3050
+ if (quietTimer) clearTimeout(quietTimer);
2998
3051
  resizeObserver.disconnect();
3052
+ if (pendingFrameRef.current !== null) {
3053
+ cancelAnimationFrame(pendingFrameRef.current);
3054
+ pendingFrameRef.current = null;
3055
+ }
3056
+ if (store.getState().isResizing) setIsResizing(false);
2999
3057
  };
3000
- }, [store, updateZoom, viewportRef, viewports, zoomOptions]);
3058
+ }, [store, updateZoom, setIsResizing, viewportRef, viewports, zoomOptions]);
3001
3059
  return null;
3002
3060
  };
3003
3061
  var supportsScrollend = typeof window === "undefined" ? true : "onscrollend" in window;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaralabs/lector",
3
- "version": "3.12.4",
3
+ "version": "3.12.6",
4
4
  "description": "A headless PDF viewer for React",
5
5
  "author": "andrewdr (https://github.com/andrewdoro)",
6
6
  "license": "MIT",