@anaralabs/lector 3.14.7 → 3.14.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.
@@ -153,18 +153,6 @@ declare interface AnnotationTooltipProps {
153
153
  */
154
154
  export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap, options?: RecolorContextOptions): (finalizeRender?: boolean) => void;
155
155
 
156
- /**
157
- * Calculates the highlight rectangles for a given text match.
158
- *
159
- * @param pageProxy - The PDF page proxy object
160
- * @param textMatch - An object containing:
161
- * - pageNumber: The page number where the match is found
162
- * - text: The text content containing the match (usually a larger chunk of text)
163
- * - matchIndex: The index within the text where the match starts
164
- * - searchText: (Optional) The exact search term to highlight. If provided, only highlights
165
- * this exact term instead of the entire text. If not provided, highlights the full text.
166
- * @returns An array of HighlightRect objects representing the areas to highlight
167
- */
168
156
  export declare function calculateHighlightRects(pageProxy: PDFPageProxy, textMatch: TextPosition): Promise<HighlightRect[]>;
169
157
 
170
158
  export declare const cancellable: <T extends Promise<unknown>>(promise: T) => {
@@ -639,6 +627,8 @@ export declare const Thumbnails: ({ children, ...props }: HTMLProps<HTMLDivEleme
639
627
 
640
628
  export declare const TotalPages: ({ ...props }: HTMLProps<HTMLDivElement>) => JSX.Element;
641
629
 
630
+ export declare const USE_LAYOUT_ZOOM: boolean;
631
+
642
632
  declare const useAnnotations: () => AnnotationState;
643
633
  export { useAnnotations }
644
634
  export { useAnnotations as useAnnotations_alias_1 }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { createContext, memo, forwardRef, useMemo, cloneElement, useEffect, useRef, useState, useCallback, useContext, useLayoutEffect, createRef } from 'react';
4
4
  import { createPortal } from 'react-dom';
5
- import { useFloating, autoUpdate, offset, shift, useDismiss, useInteractions, inline, flip } from '@floating-ui/react';
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
8
  import { useDebounce } from 'use-debounce';
@@ -219,6 +219,7 @@ function createDarkModeColorMap(colors) {
219
219
  }
220
220
 
221
221
  // src/lib/zoom.ts
222
+ var USE_LAYOUT_ZOOM = typeof navigator !== "undefined" && /AppleWebKit/i.test(navigator.userAgent) && !/Chrome|Chromium|Android/i.test(navigator.userAgent);
222
223
  var getFitWidthZoom = (containerWidth, viewports, zoomOptions) => {
223
224
  const { minZoom, maxZoom } = zoomOptions;
224
225
  const maxPageWidth = Math.max(...viewports.map((viewport) => viewport.width));
@@ -2588,21 +2589,28 @@ var useDetailCanvasLayer = ({
2588
2589
  }
2589
2590
  const scrollX = scrollContainer.scrollLeft / zoom;
2590
2591
  const scrollY = scrollContainer.scrollTop / zoom;
2591
- const viewportWidth = scrollContainer.clientWidth / zoom;
2592
- const viewportHeight = scrollContainer.clientHeight / zoom;
2592
+ const containerStyle = getComputedStyle(scrollContainer);
2593
+ const padLeft = parseFloat(containerStyle.paddingLeft) || 0;
2594
+ const padRight = parseFloat(containerStyle.paddingRight) || 0;
2595
+ const padTop = parseFloat(containerStyle.paddingTop) || 0;
2596
+ const padBottom = parseFloat(containerStyle.paddingBottom) || 0;
2597
+ const viewportWidth = (scrollContainer.clientWidth - padLeft - padRight) / zoom;
2598
+ const viewportHeight = (scrollContainer.clientHeight - padTop - padBottom) / zoom;
2599
+ const viewX = scrollX + padLeft / zoom;
2600
+ const viewY = scrollY + padTop / zoom;
2593
2601
  const pageRect = pageContainer.getBoundingClientRect();
2594
2602
  const containerRect = scrollContainer.getBoundingClientRect();
2595
2603
  const pageLeft = (pageRect.left - containerRect.left) / zoom + scrollX;
2596
2604
  const pageTop = (pageRect.top - containerRect.top) / zoom + scrollY;
2597
- const visibleLeft = Math.max(0, scrollX - pageLeft);
2598
- const visibleTop = Math.max(0, scrollY - pageTop);
2605
+ const visibleLeft = Math.max(0, viewX - pageLeft);
2606
+ const visibleTop = Math.max(0, viewY - pageTop);
2599
2607
  const visibleRight = Math.min(
2600
2608
  pageWidth,
2601
- scrollX + viewportWidth - pageLeft
2609
+ viewX + viewportWidth - pageLeft
2602
2610
  );
2603
2611
  const visibleBottom = Math.min(
2604
2612
  pageHeight,
2605
- scrollY + viewportHeight - pageTop
2613
+ viewY + viewportHeight - pageTop
2606
2614
  );
2607
2615
  const visibleWidth = Math.max(0, visibleRight - visibleLeft);
2608
2616
  const visibleHeight = Math.max(0, visibleBottom - visibleTop);
@@ -2628,7 +2636,7 @@ var useDetailCanvasLayer = ({
2628
2636
  );
2629
2637
  const eps = 1 / effectiveScale;
2630
2638
  const painted = paintedRef.current;
2631
- if (painted && painted.proxy === pdfPageProxy && painted.key === contentKey && painted.scale === effectiveScale && visibleLeft >= painted.left - eps && visibleTop >= painted.top - eps && visibleRight <= painted.left + painted.width + eps && visibleBottom <= painted.top + painted.height + eps) {
2639
+ if (painted && painted.proxy === pdfPageProxy && painted.key === contentKey && painted.scale >= effectiveScale - 1e-3 && visibleLeft >= painted.left - eps && visibleTop >= painted.top - eps && visibleRight <= painted.left + painted.width + eps && visibleBottom <= painted.top + painted.height + eps) {
2632
2640
  renderingTask?.cancel();
2633
2641
  renderingTask = null;
2634
2642
  return;
@@ -3304,17 +3312,22 @@ var useSelectionDimensions = () => {
3304
3312
  var SelectionTooltip = ({ children }) => {
3305
3313
  const [isOpen, setIsOpen] = useState(false);
3306
3314
  const lastSelectionRef = useRef(null);
3315
+ const isPointerDownRef = useRef(false);
3307
3316
  const viewportRef = usePdf((state) => state.viewportRef);
3308
3317
  const { refs, floatingStyles, context } = useFloating({
3309
3318
  placement: "bottom",
3310
3319
  open: isOpen,
3311
3320
  onOpenChange: setIsOpen,
3312
3321
  whileElementsMounted: autoUpdate,
3313
- middleware: [offset(10), shift({ padding: 8 })]
3322
+ middleware: [offset(10), flip({ padding: 8 }), shift({ padding: 8 })]
3314
3323
  });
3315
3324
  const dismiss = useDismiss(context);
3316
3325
  const { getFloatingProps } = useInteractions([dismiss]);
3317
3326
  const updateTooltipPosition = useCallback(() => {
3327
+ if (isPointerDownRef.current) {
3328
+ setIsOpen(false);
3329
+ return;
3330
+ }
3318
3331
  const selection = document.getSelection();
3319
3332
  if (!selection || selection.isCollapsed) {
3320
3333
  setIsOpen(false);
@@ -3324,19 +3337,19 @@ var SelectionTooltip = ({ children }) => {
3324
3337
  const range = selection.getRangeAt(0);
3325
3338
  if (!range) return;
3326
3339
  const rects = range.getClientRects();
3340
+ const firstRect = rects[0];
3327
3341
  const lastRect = rects[rects.length - 1];
3328
3342
  lastSelectionRef.current = range;
3329
- if (lastRect) {
3343
+ if (firstRect && lastRect) {
3330
3344
  refs.setReference({
3331
3345
  getBoundingClientRect: () => ({
3332
3346
  width: lastRect.width,
3333
- height: lastRect.height,
3347
+ height: lastRect.bottom - firstRect.top,
3334
3348
  x: lastRect.left,
3335
- y: lastRect.bottom,
3336
- // Position below the last line of selection
3337
- top: lastRect.bottom,
3349
+ y: firstRect.top,
3350
+ top: firstRect.top,
3338
3351
  right: lastRect.right,
3339
- bottom: lastRect.bottom + lastRect.height,
3352
+ bottom: lastRect.bottom,
3340
3353
  left: lastRect.left
3341
3354
  }),
3342
3355
  getClientRects: () => [lastRect]
@@ -3346,6 +3359,31 @@ var SelectionTooltip = ({ children }) => {
3346
3359
  setIsOpen(false);
3347
3360
  }
3348
3361
  }, [refs]);
3362
+ useEffect(() => {
3363
+ const handlePointerDown = (event) => {
3364
+ if (event.button === 0 && viewportRef.current?.contains(event.target) && !refs.floating.current?.contains(event.target)) {
3365
+ isPointerDownRef.current = true;
3366
+ }
3367
+ };
3368
+ const handlePointerUp = () => {
3369
+ if (!isPointerDownRef.current) return;
3370
+ isPointerDownRef.current = false;
3371
+ requestAnimationFrame(updateTooltipPosition);
3372
+ };
3373
+ const handleBlur = () => {
3374
+ isPointerDownRef.current = false;
3375
+ };
3376
+ window.addEventListener("blur", handleBlur);
3377
+ document.addEventListener("pointerdown", handlePointerDown, true);
3378
+ document.addEventListener("pointerup", handlePointerUp, true);
3379
+ document.addEventListener("pointercancel", handlePointerUp, true);
3380
+ return () => {
3381
+ window.removeEventListener("blur", handleBlur);
3382
+ document.removeEventListener("pointerdown", handlePointerDown, true);
3383
+ document.removeEventListener("pointerup", handlePointerUp, true);
3384
+ document.removeEventListener("pointercancel", handlePointerUp, true);
3385
+ };
3386
+ }, [updateTooltipPosition, viewportRef, refs.floating]);
3349
3387
  useEffect(() => {
3350
3388
  const handleSelectionChange = () => {
3351
3389
  const selection = document.getSelection();
@@ -6380,6 +6418,9 @@ var useViewportContainer = ({
6380
6418
  elementWrapperRef,
6381
6419
  elementRef
6382
6420
  }) => {
6421
+ const store = PDFStore.useContext();
6422
+ const isPinching = usePdf((state) => state.isPinching);
6423
+ const gestureTransformAppliedRef = useRef(false);
6383
6424
  const originRef = useRef([0, 0]);
6384
6425
  const wheelInertia = useRef({
6385
6426
  active: false,
@@ -6410,7 +6451,23 @@ var useViewportContainer = ({
6410
6451
  const { zoom: zoom2, translateX, translateY } = transformations.current;
6411
6452
  const naturalWidth = parseFloat(elementRef.current.style.width) || elementRef.current.offsetWidth;
6412
6453
  const naturalHeight = parseFloat(elementRef.current.style.height) || elementRef.current.offsetHeight;
6413
- elementRef.current.style.transform = `scale3d(${zoom2}, ${zoom2}, 1)`;
6454
+ if (USE_LAYOUT_ZOOM) {
6455
+ const element = elementRef.current;
6456
+ if (store.getState().isPinching) {
6457
+ const layoutZoom = parseFloat(element.style.zoom) || 1;
6458
+ const gestureScale = zoom2 / layoutZoom;
6459
+ element.style.transform = `scale3d(${gestureScale}, ${gestureScale}, 1)`;
6460
+ element.style.willChange = "transform";
6461
+ gestureTransformAppliedRef.current = true;
6462
+ } else {
6463
+ element.style.zoom = String(zoom2);
6464
+ element.style.transform = "none";
6465
+ element.style.willChange = "auto";
6466
+ gestureTransformAppliedRef.current = false;
6467
+ }
6468
+ } else {
6469
+ elementRef.current.style.transform = `scale3d(${zoom2}, ${zoom2}, 1)`;
6470
+ }
6414
6471
  elementWrapperRef.current.style.width = `${naturalWidth * zoom2}px`;
6415
6472
  elementWrapperRef.current.style.height = `${naturalHeight * zoom2}px`;
6416
6473
  containerRef.current.scrollTop = translateY;
@@ -6424,8 +6481,12 @@ var useViewportContainer = ({
6424
6481
  });
6425
6482
  }
6426
6483
  },
6427
- [containerRef, elementRef, elementWrapperRef, updateZoom]
6484
+ [containerRef, elementRef, elementWrapperRef, updateZoom, store]
6428
6485
  );
6486
+ useEffect(() => {
6487
+ if (USE_LAYOUT_ZOOM && gestureTransformAppliedRef.current && !isPinching)
6488
+ updateTransform();
6489
+ }, [isPinching, updateTransform]);
6429
6490
  useEffect(() => {
6430
6491
  return () => {
6431
6492
  if (zoomRafRef.current !== null) {
@@ -6743,7 +6804,6 @@ var Pages = ({
6743
6804
  ...props,
6744
6805
  style: {
6745
6806
  display: "flex",
6746
- justifyContent: "center",
6747
6807
  height: "100%",
6748
6808
  position: "relative",
6749
6809
  overflow: "auto",
@@ -6754,7 +6814,10 @@ var Pages = ({
6754
6814
  {
6755
6815
  ref: elementWrapperRef,
6756
6816
  style: {
6757
- width: "max-content"
6817
+ width: "max-content",
6818
+ marginLeft: "auto",
6819
+ marginRight: "auto",
6820
+ flexShrink: 0
6758
6821
  },
6759
6822
  children: /* @__PURE__ */ jsx(
6760
6823
  "div",
@@ -6767,8 +6830,7 @@ var Pages = ({
6767
6830
  alignItems: "center",
6768
6831
  flexDirection: "column",
6769
6832
  transformOrigin: "0 0",
6770
- willChange: "transform",
6771
- // width: "max-content",
6833
+ willChange: USE_LAYOUT_ZOOM ? "auto" : "transform",
6772
6834
  width: largestPageWidth,
6773
6835
  margin: "0 auto"
6774
6836
  },
@@ -7497,8 +7559,19 @@ var useSearch = () => {
7497
7559
 
7498
7560
  // src/hooks/search/useSearchPosition.tsx
7499
7561
  async function calculateHighlightRects(pageProxy, textMatch) {
7500
- const textContent = await pageProxy.getTextContent();
7501
- const items = textContent.items;
7562
+ const reader = pageProxy.streamTextContent().getReader();
7563
+ const items = [];
7564
+ try {
7565
+ for (; ; ) {
7566
+ const { value, done } = await reader.read();
7567
+ if (done) break;
7568
+ items.push(
7569
+ ...value.items.filter((item) => "str" in item)
7570
+ );
7571
+ }
7572
+ } finally {
7573
+ reader.releaseLock();
7574
+ }
7502
7575
  const matchLength = textMatch.searchText ? textMatch.searchText.length : textMatch.text.length;
7503
7576
  const matchRects = [];
7504
7577
  let currentIndex = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaralabs/lector",
3
- "version": "3.14.7",
3
+ "version": "3.14.9",
4
4
  "description": "A headless PDF viewer for React",
5
5
  "author": "andrewdr (https://github.com/andrewdoro)",
6
6
  "license": "MIT",