@anaralabs/lector 3.14.10 → 3.14.11
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 +47 -70
- package/dist/_tsup-dts-rollup.d.ts +71 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.js +627 -327
- package/esm-only.cjs +8 -0
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -1,44 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
<p align="center">
|
|
3
|
-
<i>Simple primitives to compose powerful PDF viewing experiences.<br>powered by <code><a href="https://mozilla.github.io/pdf.js/">PDF.js</a></code> and <code><a href="https://reactjs.org/">React</a></code></i>
|
|
4
|
-
</p>
|
|
5
|
-
</p>
|
|
1
|
+
# Lector
|
|
6
2
|
|
|
7
|
-
|
|
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.
|
|
8
4
|
|
|
9
|
-
|
|
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)
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
## Get a PDF on screen
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
Use React 19+. The current source expects `pdfjs-dist` `^5.5.207`. These docs track `main`; check the [releases](https://github.com/anaralabs/lector/releases) against your installed package version.
|
|
15
10
|
|
|
16
11
|
```bash
|
|
17
|
-
npm install @anaralabs/lector pdfjs-dist
|
|
12
|
+
npm install @anaralabs/lector pdfjs-dist@^5.5.207
|
|
13
|
+
```
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
yarn add @anaralabs/lector pdfjs-dist
|
|
15
|
+
Copy the PDF.js worker from your app's installed dependency to its public assets. Recopy it whenever you update PDF.js so the runtime and worker versions match.
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
```bash
|
|
18
|
+
mkdir -p public/pdfjs
|
|
19
|
+
cp node_modules/pdfjs-dist/legacy/build/pdf.worker.min.mjs public/pdfjs/
|
|
24
20
|
```
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Here's a simple example of how to create a basic PDF viewer:
|
|
22
|
+
Put a PDF at `public/sample.pdf`, then add this browser-rendered component:
|
|
29
23
|
|
|
30
24
|
```tsx
|
|
31
25
|
import { CanvasLayer, Page, Pages, Root, TextLayer } from "@anaralabs/lector";
|
|
26
|
+
import { GlobalWorkerOptions } from "pdfjs-dist/legacy/build/pdf.mjs";
|
|
32
27
|
import "pdfjs-dist/web/pdf_viewer.css";
|
|
33
28
|
|
|
29
|
+
GlobalWorkerOptions.workerSrc = "/pdfjs/pdf.worker.min.mjs";
|
|
30
|
+
|
|
34
31
|
export default function PDFViewer() {
|
|
35
32
|
return (
|
|
36
33
|
<Root
|
|
37
34
|
source="/sample.pdf"
|
|
38
|
-
|
|
39
|
-
loader={<
|
|
35
|
+
style={{ height: 600 }}
|
|
36
|
+
loader={<p role="status">Loading PDF…</p>}
|
|
40
37
|
>
|
|
41
|
-
<Pages
|
|
38
|
+
<Pages>
|
|
42
39
|
<Page>
|
|
43
40
|
<CanvasLayer />
|
|
44
41
|
<TextLayer />
|
|
@@ -49,68 +46,48 @@ export default function PDFViewer() {
|
|
|
49
46
|
}
|
|
50
47
|
```
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
When you are using "pnpm link", you are bound to use pnpm on your consumer project when you are developing locally.
|
|
55
|
-
With yalc, we are decoupling the need for pnpm and now the package can be tested with any package managers. Any
|
|
56
|
-
changes should be automatically published to yalc on save, forcing a rebuilt and updating the consumer project.
|
|
57
|
-
|
|
58
|
-
Install yalc globally:
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
pnpm i yalc -g
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
From lector:
|
|
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.
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
# navigate to lector package folder and install dependencies
|
|
68
|
-
pnpm i
|
|
69
|
-
# when you first start development, make sure you publish the package locally
|
|
70
|
-
yalc publish
|
|
71
|
-
# and run the project in development mode to start a watcher that rebuilds the project and pushes the changes locally on save
|
|
72
|
-
pnpm dev
|
|
73
|
-
```
|
|
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.
|
|
74
52
|
|
|
75
|
-
|
|
76
|
-
(It doesn't really matter what package manager you are using)
|
|
53
|
+
## Build the reader your app needs
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
55
|
+
| Feature | Start here |
|
|
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) |
|
|
84
66
|
|
|
85
|
-
|
|
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.
|
|
86
68
|
|
|
87
|
-
|
|
88
|
-
- 🎨 Fully customizable UI components
|
|
89
|
-
- 🔍 Text selection and search functionality
|
|
90
|
-
- 📑 Page thumbnails and outline navigation
|
|
91
|
-
- 🌗 First-class dark mode support
|
|
92
|
-
- 🖱️ Pan and zoom controls
|
|
93
|
-
- 📝 Form filling support
|
|
94
|
-
- 🔗 Internal and external link handling
|
|
69
|
+
## Use with a coding assistant
|
|
95
70
|
|
|
96
|
-
|
|
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.
|
|
97
72
|
|
|
98
|
-
|
|
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.
|
|
99
74
|
|
|
100
|
-
|
|
101
|
-
2. Accessibility improvements
|
|
102
|
-
3. Mobile/touch interactions
|
|
103
|
-
4. Documentation and examples
|
|
75
|
+
## Work on Lector
|
|
104
76
|
|
|
77
|
+
From the repository root, with Node.js 22.13+ and pnpm 9.5.0:
|
|
105
78
|
|
|
79
|
+
```bash
|
|
80
|
+
pnpm install --frozen-lockfile
|
|
81
|
+
pnpm --filter @anaralabs/lector build
|
|
82
|
+
pnpm --filter docs dev
|
|
83
|
+
```
|
|
106
84
|
|
|
107
|
-
|
|
85
|
+
Open [localhost:3000/docs](http://localhost:3000/docs). The docs app uses the local workspace package. For watch mode, validation commands, and testing in another application, read [CONTRIBUTING.md](https://github.com/anaralabs/lector/blob/main/CONTRIBUTING.md).
|
|
108
86
|
|
|
109
|
-
|
|
87
|
+
## Acknowledgements
|
|
110
88
|
|
|
111
|
-
|
|
112
|
-
- [pdfreader](https://github.com/OnedocLabs/pdfreader)
|
|
89
|
+
Inspired by [react-pdf-headless](https://github.com/jkgenser/react-pdf-headless) and [pdfreader](https://github.com/OnedocLabs/pdfreader). Built on [PDF.js](https://mozilla.github.io/pdf.js/) and [React](https://react.dev/).
|
|
113
90
|
|
|
114
91
|
## License
|
|
115
92
|
|
|
116
|
-
MIT © [Anara](https://anara.com)
|
|
93
|
+
[MIT](https://github.com/anaralabs/lector/blob/main/LICENSE) © [Anara](https://anara.com)
|
|
@@ -6,6 +6,7 @@ import { FunctionComponent } from 'react';
|
|
|
6
6
|
import { HTMLProps } from 'react';
|
|
7
7
|
import { JSX } from 'react/jsx-runtime';
|
|
8
8
|
import { JSX as JSX_2 } from 'react';
|
|
9
|
+
import { JSXElementConstructor } from 'react';
|
|
9
10
|
import { MemoExoticComponent } from 'react';
|
|
10
11
|
import type { PageViewport } from 'pdfjs-dist';
|
|
11
12
|
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
|
@@ -14,6 +15,7 @@ import type { PDFPageProxy } from 'pdfjs-dist';
|
|
|
14
15
|
import { default as React_2 } from 'react';
|
|
15
16
|
import { ReactElement } from 'react';
|
|
16
17
|
import { ReactNode } from 'react';
|
|
18
|
+
import { ReactPortal } from 'react';
|
|
17
19
|
import { RefAttributes } from 'react';
|
|
18
20
|
import { StoreApi } from 'zustand';
|
|
19
21
|
import type { TypedArray } from 'pdfjs-dist/types/src/display/api';
|
|
@@ -21,6 +23,12 @@ import { useFloating } from '@floating-ui/react';
|
|
|
21
23
|
import { useInteractions } from '@floating-ui/react';
|
|
22
24
|
import type { Virtualizer } from '@tanstack/react-virtual';
|
|
23
25
|
|
|
26
|
+
/** Shared indexing with bounded PDF.js work; release when the consumer leaves. */
|
|
27
|
+
export declare function acquireDocumentText(pages: PDFPageProxy[]): {
|
|
28
|
+
promise: Promise<PageText[]>;
|
|
29
|
+
release(): void;
|
|
30
|
+
};
|
|
31
|
+
|
|
24
32
|
declare interface Annotation {
|
|
25
33
|
id: string;
|
|
26
34
|
pageNumber: number;
|
|
@@ -153,6 +161,15 @@ declare interface AnnotationTooltipProps {
|
|
|
153
161
|
*/
|
|
154
162
|
export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap, options?: RecolorContextOptions): (finalizeRender?: boolean) => void;
|
|
155
163
|
|
|
164
|
+
declare interface AsyncSearchOptions extends SearchOptions {
|
|
165
|
+
/** Cancelling rejects with AbortError and never publishes partial results. */
|
|
166
|
+
signal?: AbortSignal;
|
|
167
|
+
/** Approximate work budget between yields; defaults to 8 ms. */
|
|
168
|
+
timeSliceMs?: number;
|
|
169
|
+
}
|
|
170
|
+
export { AsyncSearchOptions }
|
|
171
|
+
export { AsyncSearchOptions as AsyncSearchOptions_alias_1 }
|
|
172
|
+
|
|
156
173
|
export declare function calculateHighlightRects(pageProxy: PDFPageProxy, textMatch: TextPosition): Promise<HighlightRect[]>;
|
|
157
174
|
|
|
158
175
|
export declare const cancellable: <T extends Promise<unknown>>(promise: T) => {
|
|
@@ -208,6 +225,9 @@ export declare function computeBaseScale(dpr: number, zoom: number, pageWidth: n
|
|
|
208
225
|
|
|
209
226
|
export declare function computeTargetScale(dpr: number, zoom: number): number;
|
|
210
227
|
|
|
228
|
+
/** Reuses two rows across candidate windows and only visits the edit band. */
|
|
229
|
+
export declare function createBoundedDistance(query: string, maxDistance: number): (text: string, offset: number) => number;
|
|
230
|
+
|
|
211
231
|
/**
|
|
212
232
|
* Builds a memoized color map that flips perceived lightness onto the
|
|
213
233
|
* background<->foreground ramp while preserving hue and chroma (OKLab).
|
|
@@ -357,6 +377,9 @@ export declare class LinkService {
|
|
|
357
377
|
|
|
358
378
|
export declare const loadPdfJs: () => Promise<pdfjsDist>;
|
|
359
379
|
|
|
380
|
+
/** Ordered results with bounded in-flight work and cooperative cancellation. */
|
|
381
|
+
export declare function mapConcurrent<T, R>(items: readonly T[], concurrency: number, map: (item: T, index: number) => Promise<R>, isCancelled?: () => boolean): Promise<R[]>;
|
|
382
|
+
|
|
360
383
|
export declare const MAX_CANVAS_DIMENSION = 32767;
|
|
361
384
|
|
|
362
385
|
export declare const MAX_CANVAS_PIXELS = 16777216;
|
|
@@ -399,6 +422,11 @@ export declare const Pages: ({ children, gap, virtualizerOptions, initialOffset,
|
|
|
399
422
|
onOffsetChange?: (offset: number) => void;
|
|
400
423
|
}) => JSX.Element;
|
|
401
424
|
|
|
425
|
+
declare type PageText = {
|
|
426
|
+
pageNumber: number;
|
|
427
|
+
text: string;
|
|
428
|
+
};
|
|
429
|
+
|
|
402
430
|
export declare interface PdfJsAssetUrls {
|
|
403
431
|
wasmUrl: string;
|
|
404
432
|
cMapUrl: string;
|
|
@@ -553,17 +581,33 @@ export declare interface ScanPaperClass {
|
|
|
553
581
|
inked: boolean;
|
|
554
582
|
}
|
|
555
583
|
|
|
556
|
-
export declare const Search: ({ children, loading }: SearchProps) => ReactNode;
|
|
584
|
+
export declare const Search: ({ children, loading, errorFallback, }: SearchProps) => string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | ReactPortal | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | JSX.Element | null | undefined;
|
|
585
|
+
|
|
586
|
+
export declare function searchDocument(pages: SearchPage[], searchText: string, options?: SearchOptions): SearchResults;
|
|
587
|
+
|
|
588
|
+
export declare function searchDocumentAsync(pages: SearchPage[], searchText: string, options?: AsyncSearchOptions): Promise<SearchResults>;
|
|
557
589
|
|
|
558
590
|
declare interface SearchOptions {
|
|
559
591
|
threshold?: number;
|
|
560
592
|
limit?: number;
|
|
561
593
|
textSize?: number;
|
|
562
594
|
}
|
|
595
|
+
export { SearchOptions }
|
|
596
|
+
export { SearchOptions as SearchOptions_alias_1 }
|
|
597
|
+
|
|
598
|
+
declare type SearchPage = {
|
|
599
|
+
pageNumber: number;
|
|
600
|
+
text: string;
|
|
601
|
+
};
|
|
563
602
|
|
|
564
603
|
declare interface SearchProps {
|
|
565
604
|
children: React.ReactNode;
|
|
566
605
|
loading?: React.ReactNode;
|
|
606
|
+
/** Replace the default indexing-error message and retry button. */
|
|
607
|
+
errorFallback?: (state: {
|
|
608
|
+
error: unknown;
|
|
609
|
+
retry: () => void;
|
|
610
|
+
}) => React.ReactNode;
|
|
567
611
|
}
|
|
568
612
|
|
|
569
613
|
export declare interface SearchResult {
|
|
@@ -617,19 +661,32 @@ declare interface TextPosition {
|
|
|
617
661
|
searchText?: string;
|
|
618
662
|
}
|
|
619
663
|
|
|
620
|
-
export declare const Thumbnail: ({ pageNumber, ...props }: HTMLProps<HTMLCanvasElement> & {
|
|
664
|
+
export declare const Thumbnail: ({ pageNumber, eager, ...props }: HTMLProps<HTMLCanvasElement> & {
|
|
621
665
|
pageNumber?: number;
|
|
666
|
+
eager?: boolean;
|
|
622
667
|
}) => JSX.Element;
|
|
623
668
|
|
|
624
|
-
export declare const Thumbnails: ({ children, ...props }:
|
|
625
|
-
|
|
626
|
-
|
|
669
|
+
export declare const Thumbnails: ({ children, virtualize, ...props }: ThumbnailsProps) => JSX.Element;
|
|
670
|
+
|
|
671
|
+
declare type ThumbnailsProps = HTMLProps<HTMLDivElement> & {
|
|
672
|
+
children: ReactElement;
|
|
673
|
+
/** Opt in to a bounded list. Give the container a height and each row content that fits itemHeight. */
|
|
674
|
+
virtualize?: ThumbnailVirtualization;
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
declare interface ThumbnailVirtualization {
|
|
678
|
+
/** Fixed row height in CSS pixels, including any desired space between items. */
|
|
679
|
+
itemHeight: number;
|
|
680
|
+
overscan?: number;
|
|
681
|
+
}
|
|
627
682
|
|
|
628
683
|
export declare const TotalPages: ({ ...props }: HTMLProps<HTMLDivElement>) => JSX.Element;
|
|
629
684
|
|
|
630
685
|
export declare const USE_LAYOUT_ZOOM: boolean;
|
|
631
686
|
|
|
632
|
-
declare
|
|
687
|
+
declare function useAnnotations<T>(selector: (state: AnnotationState) => T): T;
|
|
688
|
+
|
|
689
|
+
declare function useAnnotations(): AnnotationState;
|
|
633
690
|
export { useAnnotations }
|
|
634
691
|
export { useAnnotations as useAnnotations_alias_1 }
|
|
635
692
|
|
|
@@ -653,6 +710,10 @@ declare interface UseAnnotationTooltipReturn {
|
|
|
653
710
|
|
|
654
711
|
export declare const useCreatePDFLinkService: (pdfDocumentProxy: PDFDocumentProxy | null) => LinkService;
|
|
655
712
|
|
|
713
|
+
declare function usePageAnnotations(pageNumber: number): Annotation[];
|
|
714
|
+
export { usePageAnnotations }
|
|
715
|
+
export { usePageAnnotations as usePageAnnotations_alias_1 }
|
|
716
|
+
|
|
656
717
|
declare const usePageRendered: (pageNumber: number) => boolean;
|
|
657
718
|
export { usePageRendered }
|
|
658
719
|
export { usePageRendered as usePageRendered_alias_1 }
|
|
@@ -697,7 +758,7 @@ export declare interface usePDFDocumentParams {
|
|
|
697
758
|
/**
|
|
698
759
|
* Override or extend the PDF.js DocumentInitParameters passed to getDocument().
|
|
699
760
|
* These take highest precedence over both the source object and lector's defaults.
|
|
700
|
-
*
|
|
761
|
+
* Read when source changes; changing options alone does not reload the document.
|
|
701
762
|
*/
|
|
702
763
|
documentOptions?: Partial<DocumentInitParameters>;
|
|
703
764
|
/**
|
|
@@ -750,6 +811,9 @@ export declare const useSearch: () => {
|
|
|
750
811
|
keywords: string[];
|
|
751
812
|
searchResults: SearchResults;
|
|
752
813
|
search: (searchText: string, options?: SearchOptions) => SearchResults;
|
|
814
|
+
searchAsync: (searchText: string, options?: AsyncSearchOptions) => Promise<SearchResults>;
|
|
815
|
+
cancelSearch: () => void;
|
|
816
|
+
isSearching: boolean;
|
|
753
817
|
};
|
|
754
818
|
|
|
755
819
|
export declare const useSelectionDimensions: () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export { calculateHighlightRects } from './_tsup-dts-rollup.js';
|
|
|
32
32
|
export { Annotation } from './_tsup-dts-rollup.js';
|
|
33
33
|
export { AnnotationsStoreProvider } from './_tsup-dts-rollup.js';
|
|
34
34
|
export { useAnnotations } from './_tsup-dts-rollup.js';
|
|
35
|
+
export { usePageAnnotations } from './_tsup-dts-rollup.js';
|
|
35
36
|
export { usePageRendered } from './_tsup-dts-rollup.js';
|
|
36
37
|
export { LinkService } from './_tsup-dts-rollup.js';
|
|
37
38
|
export { PDFLinkServiceContext } from './_tsup-dts-rollup.js';
|
|
@@ -47,3 +48,5 @@ export { createDarkModeColorMap } from './_tsup-dts-rollup.js';
|
|
|
47
48
|
export { DarkModeColors } from './_tsup-dts-rollup.js';
|
|
48
49
|
export { DEFAULT_DARK_MODE_COLORS } from './_tsup-dts-rollup.js';
|
|
49
50
|
export { RenderColorMap } from './_tsup-dts-rollup.js';
|
|
51
|
+
export { AsyncSearchOptions } from './_tsup-dts-rollup.js';
|
|
52
|
+
export { SearchOptions } from './_tsup-dts-rollup.js';
|