├── .github └── workflows │ └── release.yaml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── LICENSE ├── README.md ├── examples └── basic │ ├── .gitignore │ ├── README.md │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ ├── sample.pdf │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.tsx │ ├── assets │ │ └── react.svg │ ├── index.css │ ├── main.tsx │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── package.json ├── packages ├── docs │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── (home) │ │ │ ├── _components │ │ │ │ ├── anara.tsx │ │ │ │ ├── annotations.tsx │ │ │ │ ├── default-annotation-tooltip.tsx │ │ │ │ ├── document-menu.tsx │ │ │ │ ├── footer.tsx │ │ │ │ ├── github-stars-button.tsx │ │ │ │ ├── page-navigation.tsx │ │ │ │ ├── useAnnotationActions.ts │ │ │ │ └── zoom-menu.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ └── search │ │ │ │ └── route.ts │ │ ├── docs │ │ │ ├── [[...slug]] │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── global.css │ │ ├── icon.png │ │ ├── layout.config.tsx │ │ └── layout.tsx │ ├── components.json │ ├── components │ │ ├── basic-text-layer.tsx │ │ ├── basic.tsx │ │ ├── custom-search.tsx │ │ ├── custom-select.tsx │ │ ├── highlight-layer.tsx │ │ ├── highlight-select.tsx │ │ ├── page-navigation-example.tsx │ │ ├── pdf-form-layer.tsx │ │ ├── search-control.tsx │ │ ├── thumbnails.tsx │ │ ├── ui │ │ │ ├── button.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── feature-card.tsx │ │ │ ├── icon.tsx │ │ │ ├── page-navigation-buttons.tsx │ │ │ └── popover.tsx │ │ └── zoom-control.tsx │ ├── content │ │ └── docs │ │ │ ├── basic-usage.mdx │ │ │ ├── code │ │ │ ├── basic.mdx │ │ │ ├── highlight.mdx │ │ │ ├── page-navigation.mdx │ │ │ ├── pdf-form.mdx │ │ │ ├── search.mdx │ │ │ ├── select.mdx │ │ │ ├── thumbnails.mdx │ │ │ └── zoom-control.mdx │ │ │ ├── dark-mode.mdx │ │ │ ├── installation.mdx │ │ │ └── meta.json │ ├── lib │ │ ├── metadata.ts │ │ ├── setup.ts │ │ ├── shiki.ts │ │ ├── source.ts │ │ └── utils.ts │ ├── next.config.mjs │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.js │ ├── public │ │ ├── banner.png │ │ ├── favicon.ico │ │ ├── logo.png │ │ └── pdf │ │ │ ├── brochure.pdf │ │ │ ├── expensive.pdf │ │ │ ├── form.pdf │ │ │ ├── large.pdf │ │ │ └── pathways.pdf │ ├── source.config.ts │ ├── tailwind.config.js │ └── tsconfig.json └── lector │ ├── .gitignore │ ├── .npmignore │ ├── .prettierrc │ ├── .tsbuildinfo │ ├── banner.png │ ├── capture.png │ ├── eslint.config.mjs │ ├── esm-only.cjs │ ├── image.jpeg │ ├── package.json │ ├── scripts │ └── prepack.sh │ ├── size.json │ ├── src │ ├── components │ │ ├── annotation-tooltip.tsx │ │ ├── layers │ │ │ ├── annotation-highlight-layer.tsx │ │ │ ├── annotation-layer.tsx │ │ │ ├── canvas-layer.tsx │ │ │ ├── colored-highlight │ │ │ │ ├── color-selection-tool.tsx │ │ │ │ ├── colored-highlight-layer.tsx │ │ │ │ └── colored-highlight.tsx │ │ │ ├── custom-layer.tsx │ │ │ ├── highlight-layer.tsx │ │ │ └── text-layer.tsx │ │ ├── outline.tsx │ │ ├── page-number.tsx │ │ ├── page.tsx │ │ ├── pages.tsx │ │ ├── primitive.tsx │ │ ├── root.tsx │ │ ├── search.tsx │ │ ├── selection-tooltip.tsx │ │ ├── selection │ │ │ ├── custom-selection-trigger.tsx │ │ │ └── custom-selection.tsx │ │ ├── thumbnails.tsx │ │ └── zoom.tsx │ ├── hooks │ │ ├── document │ │ │ └── document.ts │ │ ├── layers │ │ │ ├── useAnnotationLayer.tsx │ │ │ ├── useCanvasLayer.tsx │ │ │ └── useTextLayer.tsx │ │ ├── pages │ │ │ ├── useFitWidth.tsx │ │ │ ├── useObserveElement.tsx │ │ │ ├── usePdfJump.tsx │ │ │ ├── useScrollFn.tsx │ │ │ ├── useVirtualizerVelocity.tsx │ │ │ └── useVisiblePage.tsx │ │ ├── search │ │ │ ├── useSearch.tsx │ │ │ └── useSearchPosition.tsx │ │ ├── useAnnotationTooltip.ts │ │ ├── useAnnotations.ts │ │ ├── useDpr.tsx │ │ ├── usePDFLinkService.tsx │ │ ├── usePdfOutline.tsx │ │ ├── usePdfPageNumber.tsx │ │ ├── useSelectionDimensions.tsx │ │ ├── useThumbnail.tsx │ │ ├── useVisibility.tsx │ │ └── viewport │ │ │ └── useViewportContainer.tsx │ ├── index.ts │ ├── internal.ts │ ├── lib │ │ ├── cancellable.ts │ │ ├── clamp.ts │ │ ├── memo.ts │ │ ├── zoom.ts │ │ └── zustand.tsx │ ├── static │ │ └── form.pdf │ ├── tests │ │ └── basic.test.tsx │ └── utils │ │ └── selectionUtils.ts │ ├── tailwind.config.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vitest.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── turbo.json /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: CI/CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [opened, reopened, synchronize] 9 | workflow_dispatch: 10 | 11 | env: 12 | FORCE_COLOR: 3 13 | 14 | jobs: 15 | lint: 16 | name: Linting 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/create-github-app-token@v1 20 | id: app-token 21 | with: 22 | app-id: ${{ secrets.APP_ID }} 23 | private-key: ${{ secrets.APP_PRIVATE_KEY }} 24 | 25 | - uses: actions/checkout@v4 26 | with: 27 | token: ${{ steps.app-token.outputs.token }} 28 | fetch-depth: 0 29 | 30 | - uses: pnpm/action-setup@v2 31 | with: 32 | version: latest 33 | 34 | - uses: actions/setup-node@v4 35 | with: 36 | node-version: 20 37 | cache: pnpm 38 | registry-url: "https://registry.npmjs.org" 39 | cache-dependency-path: ./pnpm-lock.yaml 40 | 41 | - name: Install dependencies 42 | run: pnpm install 43 | 44 | - name: Run lint 45 | run: pnpm lint 46 | 47 | test: 48 | name: Testing 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/create-github-app-token@v1 52 | id: app-token 53 | with: 54 | app-id: ${{ secrets.APP_ID }} 55 | private-key: ${{ secrets.APP_PRIVATE_KEY }} 56 | 57 | - uses: actions/checkout@v4 58 | with: 59 | token: ${{ steps.app-token.outputs.token }} 60 | fetch-depth: 0 61 | 62 | - uses: pnpm/action-setup@v2 63 | with: 64 | version: latest 65 | 66 | - uses: actions/setup-node@v4 67 | with: 68 | node-version: 20 69 | cache: pnpm 70 | registry-url: "https://registry.npmjs.org" 71 | cache-dependency-path: ./pnpm-lock.yaml 72 | 73 | - name: Install dependencies 74 | run: pnpm install 75 | 76 | - name: Run tests 77 | run: pnpm test 78 | 79 | - name: Build package 80 | run: pnpm build 81 | 82 | release: 83 | name: Release 84 | needs: [lint, test] 85 | if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' 86 | runs-on: ubuntu-latest 87 | permissions: 88 | contents: write 89 | pages: write 90 | id-token: write 91 | issues: write 92 | pull-requests: write 93 | 94 | steps: 95 | - uses: actions/create-github-app-token@v1 96 | id: app-token 97 | with: 98 | app-id: ${{ secrets.APP_ID }} 99 | private-key: ${{ secrets.APP_PRIVATE_KEY }} 100 | 101 | - uses: actions/checkout@v4 102 | with: 103 | token: ${{ steps.app-token.outputs.token }} 104 | fetch-depth: 0 105 | 106 | - uses: pnpm/action-setup@v2 107 | with: 108 | version: latest 109 | 110 | - uses: actions/setup-node@v4 111 | with: 112 | node-version: 20 113 | cache: pnpm 114 | registry-url: "https://registry.npmjs.org" 115 | cache-dependency-path: ./pnpm-lock.yaml 116 | 117 | - name: Install dependencies 118 | run: pnpm install --ignore-scripts --frozen-lockfile --filter lector... 119 | 120 | - name: Build package 121 | run: pnpm build --filter @anaralabs/lector 122 | 123 | - name: Set execute permissions 124 | run: chmod +x ./packages/lector/scripts/prepack.sh 125 | 126 | - name: Semantic Release 127 | run: ../../node_modules/.bin/semantic-release 128 | working-directory: packages/lector 129 | env: 130 | GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} 131 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 132 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Packages 2 | node_modules 3 | 4 | # Log files 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Env 15 | .env 16 | 17 | # Dist 18 | dist 19 | dist-docs 20 | 21 | # Miscellaneous 22 | .tmp 23 | .vscode/* 24 | !.vscode/extensions.json 25 | !.vscode/settings.json 26 | .idea 27 | .DS_Store 28 | .turbo 29 | tsconfig.tsbuildinfo 30 | coverage 31 | out 32 | package.tgz 33 | tsup.config.bundled* 34 | vitest.config.ts.timestamp* 35 | 36 | # Deno 37 | deno.lock 38 | 39 | # Bun 40 | bun.lockb 41 | 42 | # yarn 43 | .pnp.* 44 | .yarn/* 45 | !.yarn/patches 46 | !.yarn/plugins 47 | !.yarn/releases 48 | !.yarn/sdks 49 | !.yarn/versions 50 | 51 | # Cache 52 | .prettiercache 53 | .eslintcache 54 | .vercel 55 | 56 | # Mac 57 | .DS_Store 58 | 59 | # Cursor 60 | .cursorrules 61 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | pnpm commitlint --edit $1 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anaralabs/lector/3124c69bec10d75e06022f517e0ecbd74b1b7de4/.husky/pre-commit -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Anara 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | Simple primitives to compose powerful PDF viewing experiences.
powered by PDF.js and React
4 |

5 |

6 | 7 | # `lector` 8 | 9 | A composable, headless PDF viewer toolkit for React applications, powered by `PDF.js`. Build feature-rich PDF viewing experiences with full control over the UI and functionality. 10 | 11 | [![npm version](https://badge.fury.io/js/@anaralabs%2Flector.svg)](https://www.npmjs.com/package/@anaralabs/lector) 12 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 13 | 14 | ## Installation 15 | 16 | ```bash 17 | npm install @anaralabs/lector pdfjs-dist 18 | 19 | # or with yarn 20 | yarn add @anaralabs/lector pdfjs-dist 21 | 22 | # or with pnpm 23 | pnpm add @anaralabs/lector pdfjs-dist 24 | ``` 25 | 26 | ## Basic Usage 27 | 28 | Here's a simple example of how to create a basic PDF viewer: 29 | 30 | ```tsx 31 | import { CanvasLayer, Page, Pages, Root, TextLayer } from "@anaralabs/lector"; 32 | import "pdfjs-dist/web/pdf_viewer.css"; 33 | 34 | export default function PDFViewer() { 35 | return ( 36 | Loading...} 40 | > 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ); 49 | } 50 | ``` 51 | 52 | ## Local Development using PNPM and Yalc 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: 65 | 66 | ```bash 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 | ``` 74 | 75 | From consumer project: 76 | (It doesn't really matter what package manager you are using) 77 | 78 | ```bash 79 | # add local package to your package.json of the consumer project using yalc 80 | yalc add @anaralabs/lector 81 | # or if you don't want to add the yalc package in your package.json 82 | yalc link @anaralabs/lector 83 | ``` 84 | 85 | ## Features 86 | 87 | - 📱 Responsive and mobile-friendly 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 95 | 96 | ## Contributing 97 | 98 | We welcome contributions! Key areas we're focusing on: 99 | 100 | 1. Performance optimizations 101 | 2. Accessibility improvements 102 | 3. Mobile/touch interactions 103 | 4. Documentation and examples 104 | 105 | ## Thanks 106 | 107 | Special thanks to these open-source projects that provided inspiration: 108 | 109 | - [react-pdf-headless](https://github.com/jkgenser/react-pdf-headless) 110 | - [pdfreader](https://github.com/OnedocLabs/pdfreader) 111 | 112 | ## License 113 | 114 | MIT © [Anara](https://anara.com) 115 | -------------------------------------------------------------------------------- /examples/basic/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /examples/basic/README.md: -------------------------------------------------------------------------------- 1 | # Lector Basic Example 2 | 3 | This is a basic example of using Lector, a headless PDF viewer for React. 4 | 5 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/anaralabs/lector/tree/main/examples/basic) 6 | 7 | ## Features 8 | 9 | - PDF rendering with canvas layer 10 | - Text selection and copying 11 | - Responsive layout 12 | - Dark mode support 13 | - Loading state handling 14 | 15 | ## Getting Started 16 | 17 | 1. Clone the repository: 18 | 19 | ```bash 20 | git clone https://github.com/anaralabs/lector.git 21 | cd lector/examples/basic 22 | ``` 23 | 24 | 2. Install dependencies: 25 | 26 | ```bash 27 | pnpm install 28 | ``` 29 | 30 | 3. Start the development server: 31 | 32 | ```bash 33 | pnpm dev 34 | ``` 35 | 36 | 4. Open [http://localhost:5173](http://localhost:5173) in your browser. 37 | 38 | ## Learn More 39 | 40 | - [Lector Documentation](https://lector-weld.vercel.app/) 41 | - [GitHub Repository](https://github.com/anaralabs/lector) 42 | -------------------------------------------------------------------------------- /examples/basic/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import reactHooks from 'eslint-plugin-react-hooks' 4 | import reactRefresh from 'eslint-plugin-react-refresh' 5 | import tseslint from 'typescript-eslint' 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': [ 23 | 'warn', 24 | { allowConstantExport: true }, 25 | ], 26 | }, 27 | }, 28 | ) 29 | -------------------------------------------------------------------------------- /examples/basic/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@anaralabs/lector": "latest", 14 | "pdfjs-dist": "^4.9.155", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.2.43", 20 | "@types/react-dom": "^18.2.17", 21 | "@typescript-eslint/eslint-plugin": "^6.14.0", 22 | "@typescript-eslint/parser": "^6.14.0", 23 | "@vitejs/plugin-react": "^4.2.1", 24 | "autoprefixer": "^10.4.16", 25 | "eslint": "^8.55.0", 26 | "eslint-plugin-react-hooks": "^4.6.0", 27 | "eslint-plugin-react-refresh": "^0.4.5", 28 | "postcss": "^8.4.32", 29 | "tailwindcss": "^3.4.0", 30 | "typescript": "^5.2.2", 31 | "vite": "^5.0.8" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/basic/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /examples/basic/public/sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anaralabs/lector/3124c69bec10d75e06022f517e0ecbd74b1b7de4/examples/basic/public/sample.pdf -------------------------------------------------------------------------------- /examples/basic/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/basic/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /examples/basic/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { CanvasLayer, Page, Pages, Root, TextLayer } from "@anaralabs/lector"; 2 | import { GlobalWorkerOptions } from "pdfjs-dist"; 3 | import "pdfjs-dist/web/pdf_viewer.css"; 4 | 5 | // Configure PDF.js worker 6 | GlobalWorkerOptions.workerSrc = new URL( 7 | "pdfjs-dist/build/pdf.worker.mjs", 8 | import.meta.url 9 | ).toString(); 10 | 11 | export default function App() { 12 | return ( 13 |
14 |
15 |
16 |

Lector PDF Viewer

17 | 23 | View on GitHub → 24 | 25 |
26 |
27 | Loading...
} 31 | > 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |
41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /examples/basic/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/basic/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /examples/basic/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import './index.css' 4 | import App from './App.tsx' 5 | 6 | createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /examples/basic/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/basic/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /examples/basic/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "isolatedModules": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noUncheckedSideEffectImports": true 24 | }, 25 | "include": ["src"] 26 | } 27 | -------------------------------------------------------------------------------- /examples/basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /examples/basic/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedSideEffectImports": true 22 | }, 23 | "include": ["vite.config.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /examples/basic/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | server: { 8 | port: 5173, 9 | host: true, 10 | }, 11 | optimizeDeps: { 12 | include: ["@anaralabs/lector", "pdfjs-dist"], 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lector-monorepo", 3 | "version": "0.0.0", 4 | "description": "Headless PDF viewer for React", 5 | "author": "andrewdr (https://github.com/andrewdoro)", 6 | "type": "module", 7 | "private": "true", 8 | "scripts": { 9 | "build": "turbo run build ", 10 | "lint": "turbo run lint", 11 | "format": "turbo run format", 12 | "prepare": "husky", 13 | "test": "turbo run test", 14 | "dev": "turbo run dev" 15 | }, 16 | "devDependencies": { 17 | "@commitlint/config-conventional": "^19.6.0", 18 | "commitlint": "^19.6.1", 19 | "turbo": "^2.3.3" 20 | }, 21 | "keywords": [ 22 | "pdf", 23 | "react", 24 | "headless", 25 | "viewer", 26 | "react-pdf", 27 | "anaralabs", 28 | "typescript" 29 | ], 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/anaralabs/lector.git" 33 | }, 34 | "bugs": { 35 | "url": "https://github.com/anaralabs/lector/issues" 36 | }, 37 | "homepage": "https://github.com/anaralabs/lector", 38 | "packageManager": "pnpm@9.5.0", 39 | "dependencies": { 40 | "husky": "^9.1.7", 41 | "semantic-release": "^24.2.0" 42 | }, 43 | "commitlint": { 44 | "extends": [ 45 | "@commitlint/config-conventional" 46 | ], 47 | "rules": { 48 | "type-enum": [ 49 | 2, 50 | "always", 51 | [ 52 | "build", 53 | "chore", 54 | "ci", 55 | "clean", 56 | "doc", 57 | "feat", 58 | "fix", 59 | "perf", 60 | "ref", 61 | "revert", 62 | "style", 63 | "test" 64 | ] 65 | ], 66 | "subject-case": [ 67 | 0, 68 | "always", 69 | "sentence-case" 70 | ], 71 | "body-leading-blank": [ 72 | 2, 73 | "always", 74 | true 75 | ] 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/docs/.gitignore: -------------------------------------------------------------------------------- 1 | # deps 2 | /node_modules 3 | 4 | # generated content 5 | .contentlayer 6 | .content-collections 7 | .source 8 | 9 | # test & build 10 | /coverage 11 | /.next/ 12 | /out/ 13 | /build 14 | *.tsbuildinfo 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | /.pnp 20 | .pnp.js 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # others 26 | .env*.local 27 | .vercel 28 | next-env.d.ts -------------------------------------------------------------------------------- /packages/docs/README.md: -------------------------------------------------------------------------------- 1 | # docs 2 | 3 | This is a Next.js application generated with 4 | [Create Fumadocs](https://github.com/fuma-nama/fumadocs). 5 | 6 | Run development server: 7 | 8 | ```bash 9 | npm run dev 10 | # or 11 | pnpm dev 12 | # or 13 | yarn dev 14 | ``` 15 | 16 | Open http://localhost:3000 with your browser to see the result. 17 | 18 | ## Learn More 19 | 20 | To learn more about Next.js and Fumadocs, take a look at the following 21 | resources: 22 | 23 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js 24 | features and API. 25 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 26 | - [Fumadocs](https://fumadocs.vercel.app) - learn about Fumadocs 27 | -------------------------------------------------------------------------------- /packages/docs/app/(home)/_components/default-annotation-tooltip.tsx: -------------------------------------------------------------------------------- 1 | import { useAnnotationActions } from "./useAnnotationActions"; 2 | import type { Annotation } from "@anaralabs/lector"; 3 | 4 | interface DefaultAnnotationTooltipContentProps { 5 | annotation: Annotation; 6 | onClose?: () => void; 7 | } 8 | 9 | export const DefaultAnnotationTooltipContent = ({ 10 | annotation, 11 | onClose, 12 | }: DefaultAnnotationTooltipContentProps) => { 13 | const { 14 | comment, 15 | isEditing, 16 | setComment, 17 | setIsEditing, 18 | handleSaveComment, 19 | handleColorChange, 20 | handleCancelEdit, 21 | colors, 22 | } = useAnnotationActions({ 23 | annotation, 24 | onClose, 25 | }); 26 | 27 | return ( 28 |
29 | {/* Color picker */} 30 |
31 | {colors.map((color) => ( 32 |
40 | 41 | {/* Comment section */} 42 | {isEditing ? ( 43 |
44 |