@anaralabs/lector 3.14.11 → 3.14.13

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  **A headless PDF viewer for React.** Compose pages, text selection, search, and annotations into your own reading experience. Lector handles PDF.js rendering and page virtualization; you control the layout and UI.
4
4
 
5
- [Documentation](https://lector-weld.vercel.app/docs) · [Live demo](https://lector-weld.vercel.app) · [npm](https://www.npmjs.com/package/@anaralabs/lector) · [Contributing](https://github.com/anaralabs/lector/blob/main/CONTRIBUTING.md)
5
+ [Documentation](https://anara.com/lector/docs) · [Live demo](https://anara.com/lector) · [npm](https://www.npmjs.com/package/@anaralabs/lector) · [Contributing](https://github.com/anaralabs/lector/blob/main/CONTRIBUTING.md)
6
6
 
7
7
  ## Get a PDF on screen
8
8
 
@@ -46,7 +46,7 @@ export default function PDFViewer() {
46
46
  }
47
47
  ```
48
48
 
49
- For **Next.js**, load the viewer through a Client Component using `dynamic(..., { ssr: false })`; keep PDF.js setup inside the dynamically loaded module. See [installation](https://lector-weld.vercel.app/docs/installation) for the full wrapper, Vite worker setup, and deployments below a path prefix.
49
+ For **Next.js**, load the viewer through a Client Component using `dynamic(..., { ssr: false })`; keep PDF.js setup inside the dynamically loaded module. See [installation](https://anara.com/lector/docs/installation) for the full wrapper, Vite worker setup, and deployments below a path prefix.
50
50
 
51
51
  `Root` loads the document and provides its state. `Pages` owns scrolling and clones one `Page` template for visible pages. `CanvasLayer` paints the PDF; `TextLayer` adds selectable text. Give the viewer a definite height and import the PDF.js stylesheet so its layers align.
52
52
 
@@ -54,23 +54,23 @@ For **Next.js**, load the viewer through a Client Component using `dynamic(...,
54
54
 
55
55
  | Feature | Start here |
56
56
  | --- | --- |
57
- | Toolbar, page input, and layout | [Your first viewer](https://lector-weld.vercel.app/docs/basic-usage) |
58
- | Authenticated URLs, local files, errors, and self-hosted assets | [Loading documents](https://lector-weld.vercel.app/docs/document-loading) |
59
- | Page navigation and fit width | [Navigation](https://lector-weld.vercel.app/docs/code/page-navigation), [zoom](https://lector-weld.vercel.app/docs/code/zoom-control) |
60
- | Page previews | [Thumbnails](https://lector-weld.vercel.app/docs/code/thumbnails) |
61
- | Text search and highlighted results | [Search](https://lector-weld.vercel.app/docs/code/search) |
62
- | Selection and citation regions | [Selection](https://lector-weld.vercel.app/docs/code/select), [highlights](https://lector-weld.vercel.app/docs/code/highlight) |
63
- | PDF links and editable form fields | [Links](https://lector-weld.vercel.app/docs/code/links), [forms](https://lector-weld.vercel.app/docs/code/pdf-form) |
64
- | Dark page rendering | [Dark mode](https://lector-weld.vercel.app/docs/dark-mode) |
65
- | Props, hooks, and defaults | [API reference](https://lector-weld.vercel.app/docs/api) |
66
-
67
- Lector is a toolkit rather than a finished toolbar or a PDF editor. Your app supplies accessible controls, error UI, and storage for user annotations. Search requires embedded text; it does not perform OCR. Custom highlight overlays do not automatically modify the PDF file. See [troubleshooting](https://lector-weld.vercel.app/docs/troubleshooting) for worker errors, blank pages, and layout issues.
57
+ | Toolbar, page input, and layout | [Your first viewer](https://anara.com/lector/docs/basic-usage) |
58
+ | Authenticated URLs, local files, errors, and self-hosted assets | [Loading documents](https://anara.com/lector/docs/document-loading) |
59
+ | Page navigation and fit width | [Navigation](https://anara.com/lector/docs/code/page-navigation), [zoom](https://anara.com/lector/docs/code/zoom-control) |
60
+ | Page previews | [Thumbnails](https://anara.com/lector/docs/code/thumbnails) |
61
+ | Text search and highlighted results | [Search](https://anara.com/lector/docs/code/search) |
62
+ | Selection and citation regions | [Selection](https://anara.com/lector/docs/code/select), [highlights](https://anara.com/lector/docs/code/highlight) |
63
+ | PDF links and editable form fields | [Links](https://anara.com/lector/docs/code/links), [forms](https://anara.com/lector/docs/code/pdf-form) |
64
+ | Dark page rendering | [Dark mode](https://anara.com/lector/docs/dark-mode) |
65
+ | Props, hooks, and defaults | [API reference](https://anara.com/lector/docs/api) |
66
+
67
+ Lector is a toolkit rather than a finished toolbar or a PDF editor. Your app supplies accessible controls, error UI, and storage for user annotations. Search requires embedded text; it does not perform OCR. Custom highlight overlays do not automatically modify the PDF file. See [troubleshooting](https://anara.com/lector/docs/troubleshooting) for worker errors, blank pages, and layout issues.
68
68
 
69
69
  ## Use with a coding assistant
70
70
 
71
- Connect an MCP client to `https://lector-weld.vercel.app/mcp` using Streamable HTTP, with no API key. It can search and read every guide through tools and resources. See [AI agents and MCP](https://lector-weld.vercel.app/docs/agents) for setup and example requests.
71
+ Connect an MCP client to `https://anara.com/lector/mcp` using Streamable HTTP, with no API key. It can search and read every guide through tools and resources. See [AI agents and MCP](https://anara.com/lector/docs/agents) for setup and example requests.
72
72
 
73
- For direct fetching, start with [llms.txt](https://lector-weld.vercel.app/llms.txt), read [individual Markdown guides](https://lector-weld.vercel.app/docs/installation.md), or use the [complete documentation](https://lector-weld.vercel.app/llms-full.txt). These exports are generated from the same source as the website.
73
+ For direct fetching, start with [llms.txt](https://anara.com/lector/llms.txt), read [individual Markdown guides](https://anara.com/lector/docs/installation.md), or use the [complete documentation](https://anara.com/lector/llms-full.txt). These exports are generated from the same source as the website.
74
74
 
75
75
  ## Work on Lector
76
76
 
@@ -9,9 +9,11 @@ import { JSX as JSX_2 } from 'react';
9
9
  import { JSXElementConstructor } from 'react';
10
10
  import { MemoExoticComponent } from 'react';
11
11
  import type { PageViewport } from 'pdfjs-dist';
12
+ import { PageViewport as PageViewport_2 } from 'pdfjs-dist/types/src/display/display_utils';
12
13
  import type { PDFDocumentProxy } from 'pdfjs-dist';
13
14
  import { pdfjsDist } from 'pdfjs-dist';
14
15
  import type { PDFPageProxy } from 'pdfjs-dist';
16
+ import { PDFPageProxy as PDFPageProxy_2 } from 'pdfjs-dist/types/src/display/api';
15
17
  import { default as React_2 } from 'react';
16
18
  import { ReactElement } from 'react';
17
19
  import { ReactNode } from 'react';
@@ -161,6 +163,11 @@ declare interface AnnotationTooltipProps {
161
163
  */
162
164
  export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap, options?: RecolorContextOptions): (finalizeRender?: boolean) => void;
163
165
 
166
+ /** Keep viewport geometry and zoom subscribers in the same coordinate space. */
167
+ export declare function applyViewportZoom(viewport: HTMLDivElement | null, zoom: number): void;
168
+
169
+ declare type ApplyZoom = (zoom: number) => void;
170
+
164
171
  declare interface AsyncSearchOptions extends SearchOptions {
165
172
  /** Cancelling rejects with AbortError and never publishes partial results. */
166
173
  signal?: AbortSignal;
@@ -267,6 +274,13 @@ export declare function createRecolorCanvasFactory(mapRef: RenderColorMapRef): {
267
274
  };
268
275
  };
269
276
 
277
+ /** Paint native selection once per page; overlapping PDF spans must not
278
+ * composite their translucent backgrounds on top of each other. */
279
+ export declare const createSelectionBackground: () => {
280
+ update: (selection: Selection | null, layers: Iterable<HTMLElement>) => void;
281
+ clear: (layer: HTMLElement) => void;
282
+ };
283
+
270
284
  export declare const CurrentPage: ({ ...props }: HTMLProps<HTMLInputElement>) => JSX.Element;
271
285
 
272
286
  export declare const CurrentZoom: ({ ...props }: HTMLProps<HTMLInputElement>) => JSX.Element;
@@ -305,6 +319,13 @@ export declare const getFitWidthZoom: (containerWidth: number, viewports: PageVi
305
319
 
306
320
  export declare const getMidHeightOfHighlightLine: (selection: ColoredHighlight) => number;
307
321
 
322
+ /**
323
+ * Per-text-node client rects for a selection. Avoids the block-level rects
324
+ * that `range.getClientRects()` returns for `.textLayer` / page wrappers,
325
+ * which would render as full-page highlights on multi-page selections.
326
+ */
327
+ export declare const getTextNodeClientRects: (range: Range) => TextNodeRect[];
328
+
308
329
  export declare const HighlightLayer: ForwardRefExoticComponent<HighlightLayerProps & RefAttributes<HTMLDivElement>>;
309
330
 
310
331
  declare interface HighlightLayerProps extends ComponentPropsWithoutRef<"div"> {
@@ -332,6 +353,8 @@ export declare interface HighlightRect_alias_2 {
332
353
  }
333
354
 
334
355
  export declare interface InitialPDFState {
356
+ pageResources?: PageResources;
357
+ initialPage?: number;
335
358
  pdfDocumentProxy: PDFDocumentProxy;
336
359
  pageProxies: PDFPageProxy[];
337
360
  viewports: Array<PageViewport>;
@@ -384,6 +407,8 @@ export declare const MAX_CANVAS_DIMENSION = 32767;
384
407
 
385
408
  export declare const MAX_CANVAS_PIXELS = 16777216;
386
409
 
410
+ export declare const mergeTextRuns: <T extends Rect>(rects: T[]) => T[];
411
+
387
412
  export declare const NextPage: () => void;
388
413
 
389
414
  export declare const Outline: ({ children, ...props }: HTMLProps<HTMLUListElement> & {
@@ -412,6 +437,41 @@ pageNumber?: number;
412
437
 
413
438
  declare type PageNavigationCallback = (pageNumber: number, explicitDest?: unknown[]) => void;
414
439
 
440
+ /** Owns a document's bounded, deduplicated page acquisition queue. */
441
+ export declare class PageResources {
442
+ private document;
443
+ private rotation;
444
+ private onError;
445
+ private concurrency;
446
+ private pages;
447
+ private requests;
448
+ private queue;
449
+ private active;
450
+ private disposed;
451
+ private listeners;
452
+ private notification;
453
+ private viewports;
454
+ private completePages;
455
+ private viewportUpdates;
456
+ constructor(document: PDFDocumentProxy, firstPage: PDFPageProxy, rotation: number, onError: (error: unknown) => void, concurrency?: number);
457
+ private viewportFor;
458
+ get(pageNumber: number): PDFPageProxy_2 | undefined;
459
+ getSnapshot: () => {
460
+ viewports: PageViewport_2[];
461
+ pageProxies: PDFPageProxy_2[] | undefined;
462
+ };
463
+ subscribe: (listener: () => void) => () => void;
464
+ /** Visible pages move ahead of the background queue; in-flight requests are shared. */
465
+ load: (pageNumber: number) => Promise<PDFPageProxy>;
466
+ private createRequest;
467
+ /** Start after the reader mounts, so unrelated page metadata cannot delay its first render. */
468
+ start: () => void;
469
+ loadAll: () => Promise<PDFPageProxy[]>;
470
+ private pump;
471
+ private notify;
472
+ dispose: () => void;
473
+ }
474
+
415
475
  export declare const Pages: ({ children, gap, virtualizerOptions, initialOffset, onOffsetChange, ...props }: HTMLProps<HTMLDivElement> & {
416
476
  virtualizerOptions?: {
417
477
  overscan?: number;
@@ -458,7 +518,14 @@ declare interface PDFState {
458
518
  setVirtualizer: (virtualizer: PDFVirtualizer) => void;
459
519
  highlights: HighlightRect[];
460
520
  setHighlight: (higlights: HighlightRect[]) => void;
521
+ /** Synchronous access; progressive consumers must load an unresolved page first. */
461
522
  getPdfPageProxy: (pageNumber: number) => PDFPageProxy;
523
+ loadPdfPageProxy: (pageNumber: number) => Promise<PDFPageProxy>;
524
+ loadPdfPageProxies: () => Promise<PDFPageProxy[]>;
525
+ isPageLoaded: (pageNumber: number) => boolean;
526
+ /** False until the ordered pageProxies array is complete in progressive mode. */
527
+ pagesLoaded: boolean;
528
+ initialPage: number;
462
529
  customSelectionRects: HighlightRect[];
463
530
  setCustomSelectionRects: (rects: HighlightRect[]) => void;
464
531
  coloredHighlights: ColoredHighlight[];
@@ -502,6 +569,15 @@ export declare interface RecolorContextOptions {
502
569
  pageArea?: number;
503
570
  }
504
571
 
572
+ declare type Rect = {
573
+ left: number;
574
+ top: number;
575
+ width: number;
576
+ height: number;
577
+ };
578
+
579
+ export declare function registerViewportZoom(viewport: HTMLDivElement, apply: ApplyZoom): () => void;
580
+
505
581
  /**
506
582
  * Removes any recolor wrapper installed on the context. For long-lived
507
583
  * contexts (the visible canvas in the no-buffer fallback, thumbnails) that
@@ -615,6 +691,8 @@ export declare interface SearchResult {
615
691
  text: string;
616
692
  score: number;
617
693
  matchIndex: number;
694
+ /** Matched UTF-16 span in the original PDF text, when different from searchText.length. */
695
+ matchLength?: number;
618
696
  isExactMatch: boolean;
619
697
  searchText?: string;
620
698
  }
@@ -654,14 +732,21 @@ declare type TextContent = {
654
732
 
655
733
  export declare const TextLayer: MemoExoticComponent<({ className, style, ...props }: HTMLProps<HTMLDivElement>) => JSX.Element>;
656
734
 
735
+ declare type TextNodeRect = {
736
+ rect: DOMRect;
737
+ element: Element | null;
738
+ };
739
+
657
740
  declare interface TextPosition {
658
741
  pageNumber: number;
659
742
  text: string;
660
743
  matchIndex: number;
744
+ matchLength?: number;
661
745
  searchText?: string;
662
746
  }
663
747
 
664
- export declare const Thumbnail: ({ pageNumber, eager, ...props }: HTMLProps<HTMLCanvasElement> & {
748
+ /** An unresolved page occupies a stable row while its resource is acquired. */
749
+ export declare const Thumbnail: (props: HTMLProps<HTMLCanvasElement> & {
665
750
  pageNumber?: number;
666
751
  eager?: boolean;
667
752
  }) => JSX.Element;
@@ -722,7 +807,7 @@ declare const usePdf: <T>(selector: (state: PDFState) => T) => T;
722
807
  export { usePdf }
723
808
  export { usePdf as usePdf_alias_1 }
724
809
 
725
- export declare const usePDFDocumentContext: ({ onDocumentLoad, onError, source, initialRotation, isZoomFitWidth, zoom, zoomOptions, documentOptions, colorScheme, darkModeColors, }: usePDFDocumentParams) => {
810
+ export declare const usePDFDocumentContext: ({ onDocumentLoad, onError, source, initialRotation, progressive, initialPage, isZoomFitWidth, zoom, zoomOptions, documentOptions, colorScheme, darkModeColors, }: usePDFDocumentParams) => {
726
811
  initialState: InitialPDFState | null | undefined;
727
812
  };
728
813
 
@@ -752,6 +837,10 @@ export declare interface usePDFDocumentParams {
752
837
  source: Source;
753
838
  }) => void;
754
839
  initialRotation?: number;
840
+ /** Render the initial page before acquiring the rest. Unloaded page sizes are estimates. */
841
+ progressive?: boolean;
842
+ /** One-based page to load and display first. Read when source changes. */
843
+ initialPage?: number;
755
844
  isZoomFitWidth?: boolean;
756
845
  zoom?: number;
757
846
  zoomOptions?: ZoomOptions;