Read imported PDFs in the app
Stage three: the viewer. Drawing on pages comes next. pdf.js stays out of the bundle everyone downloads — it is larger than the rest of the application put together, so it is behind a dynamic import and a session that never opens a PDF never fetches it. Main bundle grew by 8 kB for the viewer shell; the 162 kB library arrives only when needed. The worker is bundled rather than pulled from a CDN, because a viewer that stops working during someone else's outage is not one you can rely on offline. Two things that only showed up once it ran in a browser: - pdf.js 6 calls Uint8Array.prototype.toHex, which only very recent browsers have. Anything older failed with "n.toHex is not a function", which says nothing about the cause. Now on the legacy build, which compiles those away — a little larger, and the difference between working everywhere and working on this year's browsers. - nginx has no MIME type for .mjs and served the worker as octet-stream, which a browser refuses to run as a module. Pages are laid out at their true proportions before anything is drawn, so the scrollbar is honest immediately and a document mixing portrait and landscape keeps both — the first version scaled every page to the widest one, which the suite caught by checking the two aspect ratios differ. Bytes come from the authenticated attachment route and are cached in IndexedDB. Content-addressed storage means a cached copy can never be stale, so a document opened once needs no connection again. Verified with the network switched off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
53965281b4
commit
2986bcb620
16 files changed
+1079
-12
No files matched your search
@@ -17,6 +17,7 @@ A self-hosted note-taking application with rich text editing, freehand drawing,
|
||||
- **Undo/Redo** — Full history stack for drawing operations
|
||||
- **Notebooks & Folders** — Create notebooks with custom gradient covers, organize notes by folder
|
||||
- **Star/Favorite** — Quick access to important notes
|
||||
- **PDF Reading** — Import a PDF and read it in the app; the file is cached so it stays readable offline
|
||||
- **Export** — Any note, notebook or the whole account as Markdown or PDF, entirely in the browser
|
||||
- **Dark & Light Mode** — Full theme support with refined color schemes
|
||||
|
||||
@@ -329,6 +330,7 @@ docker compose -f docker-compose.dev.yml up -d --build # must be running
|
||||
| `search-attachments` | 12 | body-only matches, ranking, umlauts; images as blob URLs, not inline bytes |
|
||||
| `history` | 10 | a large deletion is snapshotted, restoring works, restoring is itself undoable |
|
||||
| `drawing` | 7 | strokes survive the poller, and match across four viewport widths |
|
||||
| `pdf` | 10 | import, page proportions, lazy loading, and reading with the network off |
|
||||
| `conflict` | 10 | a late write wins but the state it buries survives as a version |
|
||||
| `export` | 21 | the downloaded bytes are real Markdown, ZIP and PNG, unpacked and read back |
|
||||
|
||||
@@ -409,6 +411,37 @@ a snippet around the first match.
|
||||
|
||||
---
|
||||
|
||||
## Reading PDFs
|
||||
|
||||
An imported PDF becomes a note of type `pdf` and is shown by a viewer instead of
|
||||
the text editor. Pages are laid out at their true proportions before anything is
|
||||
drawn, so the scrollbar is honest from the first frame; a document mixing
|
||||
portrait and landscape keeps both, rather than stretching the narrow pages.
|
||||
|
||||
Only pages near the viewport are rendered, and each keeps its own canvas — a PDF
|
||||
page is expensive to draw and cheap to hold, so scrolling back up repaints
|
||||
nothing. Canvases are sized in device pixels and scaled down by CSS, which is
|
||||
what keeps text sharp on a high-density screen.
|
||||
|
||||
**pdf.js is not in the main bundle.** It is larger than the rest of the
|
||||
application put together, so it sits behind a dynamic import: a session that
|
||||
never opens a PDF never downloads it. The worker is bundled too, not fetched
|
||||
from a CDN — a viewer that stops working during someone else's outage is not one
|
||||
you can rely on offline.
|
||||
|
||||
The **legacy** pdf.js build is used deliberately. Version 6 calls
|
||||
`Uint8Array.prototype.toHex`, which only very recent browsers have; anything
|
||||
older fails to parse the document with a message that says nothing about why.
|
||||
|
||||
Bytes are read through the authenticated attachment route and cached in
|
||||
IndexedDB, so a document opened once stays readable with no connection. Because
|
||||
storage is content-addressed, a cached copy can never go stale.
|
||||
|
||||
> nginx needs a MIME type for `.mjs` — without one it serves the worker as
|
||||
> `application/octet-stream` and the browser refuses to run it as a module.
|
||||
|
||||
---
|
||||
|
||||
## PDF storage
|
||||
|
||||
An imported PDF is stored as an attachment: encrypted, named by the hash of its
|
||||
|
||||
Reference in new issue
Block a user