@anaralabs/lector 3.12.7 → 3.12.9

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.
Files changed (2) hide show
  1. package/dist/index.js +118 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2668,26 +2668,36 @@ var createTextSelectionManager = () => {
2668
2668
  };
2669
2669
  var bindMouseEvents = createTextSelectionManager();
2670
2670
  var TEXT_BUILD_IDLE_MS = 300;
2671
+ var TEXT_SCROLL_SETTLE_MS = 160;
2672
+ var VIEWPORT_ATTACH_MAX_FRAMES = 300;
2671
2673
  var useTextLayer = () => {
2672
2674
  const textContainerRef = useRef(null);
2673
2675
  const textLayerRef = useRef(null);
2674
2676
  const pageNumber = usePDFPageNumber();
2675
2677
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
2678
+ const viewportRef = usePdf((state) => state.viewportRef);
2676
2679
  useEffect(() => {
2677
2680
  const textContainer = textContainerRef.current;
2678
2681
  if (!textContainer) {
2679
2682
  return;
2680
2683
  }
2681
2684
  let isCancelled = false;
2682
- const buildTimer = setTimeout(() => {
2683
- if (isCancelled || textContainerRef.current !== textContainer) return;
2685
+ let buildTimer = null;
2686
+ let unsubscribe = null;
2687
+ let built = false;
2688
+ let building = false;
2689
+ let buildGen = 0;
2690
+ const build = () => {
2691
+ building = true;
2692
+ const gen = ++buildGen;
2684
2693
  textContainer.innerHTML = "";
2685
2694
  if (textLayerRef.current) {
2686
2695
  textLayerRef.current.cancel();
2687
2696
  textLayerRef.current = null;
2688
2697
  }
2689
2698
  void import('pdfjs-dist/legacy/build/pdf.mjs').then(({ TextLayer: TextLayer2 }) => {
2690
- if (isCancelled || textContainerRef.current !== textContainer) return;
2699
+ if (isCancelled || gen !== buildGen || textContainerRef.current !== textContainer)
2700
+ return;
2691
2701
  const textLayer = new TextLayer2({
2692
2702
  textContentSource: pdfPageProxy.streamTextContent(),
2693
2703
  container: textContainer,
@@ -2696,22 +2706,61 @@ var useTextLayer = () => {
2696
2706
  textLayerRef.current = textLayer;
2697
2707
  return textLayer.render();
2698
2708
  }).then(() => {
2699
- if (isCancelled || textContainerRef.current !== textContainer) {
2709
+ if (isCancelled || gen !== buildGen || textContainerRef.current !== textContainer) {
2700
2710
  return;
2701
2711
  }
2702
2712
  const endOfContent = document.createElement("div");
2703
2713
  endOfContent.className = "endOfContent";
2704
2714
  textContainer.appendChild(endOfContent);
2705
2715
  bindMouseEvents(textContainer, endOfContent);
2716
+ built = true;
2717
+ building = false;
2718
+ unsubscribe?.();
2719
+ unsubscribe = null;
2706
2720
  }).catch((error) => {
2721
+ if (gen === buildGen) building = false;
2707
2722
  if (error instanceof Error && error.name !== "AbortException") {
2708
2723
  console.error("TextLayer rendering error:", error);
2709
2724
  }
2710
2725
  });
2711
- }, TEXT_BUILD_IDLE_MS);
2726
+ };
2727
+ const schedule = () => {
2728
+ if (built) return;
2729
+ if (building) {
2730
+ buildGen++;
2731
+ building = false;
2732
+ if (textLayerRef.current) {
2733
+ textLayerRef.current.cancel();
2734
+ textLayerRef.current = null;
2735
+ }
2736
+ textContainer.innerHTML = "";
2737
+ }
2738
+ if (buildTimer) clearTimeout(buildTimer);
2739
+ buildTimer = setTimeout(() => {
2740
+ buildTimer = null;
2741
+ if (isCancelled || textContainerRef.current !== textContainer) return;
2742
+ build();
2743
+ }, TEXT_BUILD_IDLE_MS);
2744
+ };
2745
+ let attachRaf = null;
2746
+ let attachAttempts = 0;
2747
+ const attachScrollSubscription = () => {
2748
+ attachRaf = null;
2749
+ const scrollViewport = viewportRef?.current;
2750
+ if (!scrollViewport) {
2751
+ if (++attachAttempts > VIEWPORT_ATTACH_MAX_FRAMES) return;
2752
+ attachRaf = requestAnimationFrame(attachScrollSubscription);
2753
+ return;
2754
+ }
2755
+ unsubscribe = subscribeToViewportInvalidation(scrollViewport, schedule);
2756
+ };
2757
+ attachScrollSubscription();
2758
+ schedule();
2712
2759
  return () => {
2713
2760
  isCancelled = true;
2714
- clearTimeout(buildTimer);
2761
+ if (buildTimer) clearTimeout(buildTimer);
2762
+ if (attachRaf !== null) cancelAnimationFrame(attachRaf);
2763
+ unsubscribe?.();
2715
2764
  if (textLayerRef.current) {
2716
2765
  textLayerRef.current.cancel();
2717
2766
  textLayerRef.current = null;
@@ -2721,7 +2770,69 @@ var useTextLayer = () => {
2721
2770
  delete textContainer._cleanupTextSelection;
2722
2771
  }
2723
2772
  };
2724
- }, [pdfPageProxy.streamTextContent, pdfPageProxy.getViewport]);
2773
+ }, [pdfPageProxy.streamTextContent, pdfPageProxy.getViewport, viewportRef]);
2774
+ useEffect(() => {
2775
+ const textContainer = textContainerRef.current;
2776
+ if (!textContainer) return;
2777
+ let settleTimer = null;
2778
+ let pointerDown = false;
2779
+ let selecting = false;
2780
+ const show = () => {
2781
+ textContainer.style.visibility = "";
2782
+ };
2783
+ const onScroll = () => {
2784
+ if (selecting) return;
2785
+ textContainer.style.visibility = "hidden";
2786
+ if (settleTimer) clearTimeout(settleTimer);
2787
+ settleTimer = setTimeout(show, TEXT_SCROLL_SETTLE_MS);
2788
+ };
2789
+ const onPointerDown = () => {
2790
+ pointerDown = true;
2791
+ selecting = false;
2792
+ if (settleTimer) {
2793
+ clearTimeout(settleTimer);
2794
+ settleTimer = null;
2795
+ }
2796
+ show();
2797
+ };
2798
+ const onSelectionChange = () => {
2799
+ if (pointerDown) selecting = true;
2800
+ };
2801
+ const clearPointer = () => {
2802
+ pointerDown = false;
2803
+ selecting = false;
2804
+ };
2805
+ let unsubscribe = null;
2806
+ let attachRaf = null;
2807
+ let attachAttempts = 0;
2808
+ const attachScrollSubscription = () => {
2809
+ attachRaf = null;
2810
+ const scrollViewport = viewportRef?.current;
2811
+ if (!scrollViewport) {
2812
+ if (++attachAttempts > VIEWPORT_ATTACH_MAX_FRAMES) return;
2813
+ attachRaf = requestAnimationFrame(attachScrollSubscription);
2814
+ return;
2815
+ }
2816
+ unsubscribe = subscribeToViewportInvalidation(scrollViewport, onScroll);
2817
+ };
2818
+ attachScrollSubscription();
2819
+ document.addEventListener("pointerdown", onPointerDown, true);
2820
+ document.addEventListener("pointerup", clearPointer, true);
2821
+ document.addEventListener("pointercancel", clearPointer, true);
2822
+ document.addEventListener("selectionchange", onSelectionChange);
2823
+ window.addEventListener("blur", clearPointer);
2824
+ return () => {
2825
+ if (attachRaf !== null) cancelAnimationFrame(attachRaf);
2826
+ unsubscribe?.();
2827
+ document.removeEventListener("pointerdown", onPointerDown, true);
2828
+ document.removeEventListener("pointerup", clearPointer, true);
2829
+ document.removeEventListener("pointercancel", clearPointer, true);
2830
+ document.removeEventListener("selectionchange", onSelectionChange);
2831
+ window.removeEventListener("blur", clearPointer);
2832
+ if (settleTimer) clearTimeout(settleTimer);
2833
+ show();
2834
+ };
2835
+ }, [viewportRef]);
2725
2836
  return {
2726
2837
  textContainerRef,
2727
2838
  pageNumber: pdfPageProxy.pageNumber
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaralabs/lector",
3
- "version": "3.12.7",
3
+ "version": "3.12.9",
4
4
  "description": "A headless PDF viewer for React",
5
5
  "author": "andrewdr (https://github.com/andrewdoro)",
6
6
  "license": "MIT",