Appearance
PDF Library
A reading list with an in-app PDF reader and annotations
Overview
PDF Library is a reading list with a built-in PDF reader. You collect PDFs — by pasting a URL or uploading a file — track which ones you have read, and open any of them in a smooth, continuous reader where you can search the text and mark it up with colored highlights and notes. It is part of the kimtos new-tab app suite.
Everything is fully on-device. There is no backend, no account, and no network round-trips while you read: every book, the PDF file itself, your reading progress, and your annotations live in your browser's IndexedDB and never leave the machine.
Highlights
- Build a reading list — import a PDF by URL or upload one from your computer. The file is downloaded/stored locally, so it opens instantly later.
- Track progress — a read checkbox per book, plus All / Unread / Read filters. Unread books sort first; read ones fall below a divider.
- Dock badge — the app's dock icon shows how many books are still unread.
- Continue reading — a home-screen widget lists the books you read most recently, so you can jump straight back in.
- Continuous reader — every page stacked in one scroll, rendered on demand, with manual zoom and a live page indicator.
- Remembers where you were — your last page and zoom level are saved per book and restored when you reopen it.
- Search the whole book — find a word or phrase across every page, with matches fully highlighted even when they break across the PDF's text chunks.
- Highlights, notes & annotations — select text to highlight it in one of four colors, attach a comment, and browse everything in a notes sidebar.
Privacy model
PDF Library writes only to local browser storage:
- Books (title, source, read state, last page, zoom) are records in IndexedDB.
- The PDF files themselves are stored as binary blobs, one per book.
- Annotations (highlights and comments) are separate records linked to a book.
When you import by URL, the PDF is fetched once and saved locally; from then on it is read from your own storage and rendered in the browser. Nothing is uploaded, and there are no analytics or third-party calls.
Getting Started
Open PDF Library from the dock. You land on your reading list — empty at first, with a form at the top for adding your first book.
Adding books
There are two ways to add a PDF, both from the top of the list:
- Import by URL — paste a link to a PDF into the "Paste a PDF URL to import" field and click Import. The file is downloaded and stored locally.
- Upload a file — click Upload a PDF from your computer and pick a file.
A Title field above both is optional. If you leave it blank, the title is taken from the file name (or the last part of the URL). Either way, as soon as the book is added it opens straight into the reader. Files are capped at 50 MB, and non-PDF files are rejected.
Managing the list
- Filters — the All / Unread / Read tabs narrow the list. In All, unread books come first and a "Read" divider marks where the read ones begin.
- Mark read / unread — tick the checkbox on a book's row. The dock badge (the unread count) updates accordingly.
- Open — click a book to open it in the reader. Each row shows its source (the host for URL imports, or "Uploaded PDF") and, if you have read past the first page, the page you left off on.
- Remove one — the trash button on a row deletes that book (with a confirm).
- Remove several — tap Select (or drag across rows), tick the books, and delete them in one batch.
Reading
The reader is a continuous, scrolling view: all pages are stacked top to bottom, and each one renders as it nears the screen. The toolbar gives you everything else.
- Scroll & page indicator — just scroll to move through the book. A live page / total counter in the toolbar tracks where you are.
- Zoom — use the − and + buttons; the current percentage shows between them. Pages open at their natural size (shrunk to fit only if wider than the view) and your zoom level is remembered per book.
- Remember last page — the reader reopens at the page (and zoom) you left off on.
- Back — the back arrow (top-left) returns to the reading list.
Searching
Type into the Search in book… field and press Enter. Every match across the whole book is highlighted, and the reader jumps to the first one. A counter shows match / total; the ‹ and › buttons step between matches (pressing Enter again advances to the next). Search ignores whitespace, so a phrase is found even when the PDF splits it across separate text chunks.
Highlights, notes & annotations
- Create a highlight — select some text in a page. A small popup appears with four colors (yellow, green, blue, pink); click one to highlight the selection.
- Add a note — in that same popup, click Note to highlight and immediately open a comment box. Type your note and click Save.
- Edit later — click an existing highlight to reopen the popup, where you can change its color, edit its comment, or delete it.
- Annotations sidebar — the Notes button (toolbar) opens a side panel listing every highlight and comment in order, with a color dot and page number. Click an entry to scroll to it (it flashes briefly); use the trash icon to delete it.
Architecture
PDF Library follows the suite convention: all data logic lives behind the local API layer, and the app itself is UI plus a self-contained PDF.js reader. The code lives in extension/src/apps/pdflib/.
Modules
| File | Role |
|---|---|
index.ts | The app descriptor and list UI — add-by-URL / upload form, the All / Unread / Read filters, the read checkbox and remove/batch-delete, the dock unread badge, and the "Continue reading" home widget. |
reader.ts | The in-app reader — a PDF.js continuous viewer with on-demand page rendering, manual zoom, a live page indicator, last-page memory, cross-chunk text search & highlight, and the highlights / comments / annotations sidebar. Loaded lazily, so PDF.js stays out of the main bundle. |
pdflib.local.ts | The data layer — book CRUD, stored PDF blobs, and annotation CRUD over IndexedDB, registered on the local API router. |
Data flow
The UI never touches storage directly: both the list and the reader call getApi / jsonApi (the suite's local router), which dispatches to pdflib.local.ts. To display a book, the reader asks the data layer for a blob URL of the stored PDF, hands it to PDF.js, and renders each page to a <canvas> as it scrolls into view.
The reader
The reader stacks one wrapper per page and renders lazily:
- An IntersectionObserver renders pages as they approach the viewport (with a buffer) and unloads pages that scroll far away, to bound memory.
- Pages render at the chosen zoom only; the page wrappers are pre-sized so the scrollbar and page indicator stay correct before a page has drawn.
- A text layer over each canvas powers selection, search highlights, and a separate highlight layer whose rectangles are positioned in percentages of the page box — so annotations track zoom for free.
- Search builds a whitespace-stripped string of each page's text (cached) to find matches across chunk boundaries, then wraps exactly the matched characters in
<mark>elements.
Persistence
Three kinds of record back the app:
book { id, title, url, source, read, added, lastPage, lastReadAt, zoom }
PDF blob key "pdf:<bookId>" → the stored PDF file
annotation { id, bookId, page, rects[], text, color, comment, created }Reading progress (lastPage, lastReadAt) and zoom are saved with a short debounce as you read. Highlight geometry is stored as normalized rects (fractions of the page), so it renders correctly at any zoom. Deleting a book removes its record, its stored PDF blob, and all of its annotations.
Reference
A quick reference for the reader controls, data, and limits.
Reader controls
| Control | What it does |
|---|---|
| Back (‹) | Return to the reading list. |
| Search field | Find a word or phrase across the whole book; press Enter to run / advance. |
| Match counter | Shows current match / total matches. |
| ‹ / › (search) | Jump to the previous / next match. |
| − / + (zoom) | Zoom out / in; the percentage shows between them. Remembered per book. |
| Page indicator | Live current page / total pages, updated as you scroll. |
| Notes | Toggle the annotations sidebar (highlights + comments). |
Selection popup
Appears when you select text in a page, or when you click an existing highlight.
| Action | Result |
|---|---|
| Color swatch | Highlight the selection in yellow, green, blue, or pink. |
| Note | Highlight and open a comment box for a typed note. |
| Comment box + Save | Attach or edit a note on the highlight. |
| Trash | Delete the highlight (and its note). |
Annotations sidebar
| Element | Meaning |
|---|---|
| Color dot | The highlight's color. |
| Text + note | The highlighted text and any comment. |
| Page N | The page the highlight is on. |
| Click an entry | Scroll to the highlight (it flashes briefly). |
| Trash icon | Delete that annotation. |
Reading list
| Control | Action |
|---|---|
| Title field | Optional name; falls back to the file name or URL. |
| Import | Add a PDF from a pasted URL. |
| Upload a PDF | Add a PDF file from your computer. |
| All / Unread / Read | Filter the list. |
| Read checkbox | Mark a book read / unread (updates the dock badge). |
| Trash / Select | Remove one book, or batch-delete several. |
Data & storage
| Item | Where it lives |
|---|---|
| Books (title, source, read state, last page, last-read time, zoom) | IndexedDB records |
| PDF files | IndexedDB blobs, keyed pdf:<bookId> |
| Annotations (highlights + comments) | IndexedDB records, linked by bookId |
Deleting a book also deletes its stored PDF and all of its annotations. Nothing is ever uploaded.
Limits
| Limit | Value |
|---|---|
| Maximum file size (import or upload) | 50 MB |
| Accepted file type | PDF only (validated on add) |
| Highlight colors | Yellow, green, blue, pink |
| "Continue reading" widget | Up to 5 most recently read books |