No description
Find a file
agent 01624c6efa Bypass the HTTP cache when the service worker caches assets
Hashed assets are served as immutable for a year. Before the nginx MIME
fix, the pdf.js worker was served as application/octet-stream, and the
browser kept that response. Each new service worker then copied it from
the HTTP cache into its offline cache, so PDFs still failed to render
after the server was fixed. Fetching with cache: 'reload' always takes
the current response from the server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VsorPV3JeJRoZ1mD1pdvtL
2026-09-27 10:17:21 +00:00
.claude Init 2026-09-26 19:13:02 +02:00
docker Fix imported PDFs not rendering in the Docker image 2026-09-27 09:37:15 +00:00
memos/memos Init 2026-09-26 19:13:02 +02:00
scripts Init 2026-09-26 19:13:02 +02:00
src Bypass the HTTP cache when the service worker caches assets 2026-09-27 10:17:21 +00:00
static Init 2026-09-26 19:13:02 +02:00
.dockerignore Add Dockerfile and compose setup for self-hosting 2026-09-26 22:33:08 +00:00
.gitignore Init 2026-09-26 19:13:02 +02:00
compose.yaml Add Dockerfile and compose setup for self-hosting 2026-09-26 22:33:08 +00:00
Dockerfile Add Dockerfile and compose setup for self-hosting 2026-09-26 22:33:08 +00:00
package-lock.json Init 2026-09-26 19:13:02 +02:00
package.json Init 2026-09-26 19:13:02 +02:00
palm-rejection-test.html Init 2026-09-26 19:13:02 +02:00
README.md Add Dockerfile and compose setup for self-hosting 2026-09-26 22:33:08 +00:00
spec.md Init 2026-09-26 19:13:02 +02:00
svelte.config.js Init 2026-09-26 19:13:02 +02:00
tsconfig.json Init 2026-09-26 19:13:02 +02:00
vite.config.ts Init 2026-09-26 19:13:02 +02:00

Papure

Lecture notes as a tree of pages ("comb") — live-preview markdown underneath, vector ink on top — stored as one dual-layer PDF per canvas and synced through a GitHub repo. See spec.md for the design.

Run

npm install
npm run dev        # http://localhost:5173
npm test           # unit tests (tree, markdown, PDF round trip, sync against a fake GitHub)
npm run check      # type-check
npm run build      # static PWA in build/ — serve it from any static host (HTTPS for PWA install)

Docker

docker compose up -d --build    # http://localhost:8080 (set PAPURE_PORT to change)

The image builds the PWA and serves it with nginx. Put an HTTPS reverse proxy in front of it: installing the PWA and the offline service worker both need HTTPS. To update, git pull and run the same command again.

Using it

  • Tools (bottom bar): Text T, Pen P, Highlighter H, Eraser E, Import PDF, Undo/Redo, zoom. The pen's eraser end erases too.
  • Pages: the grey "+" tiles next to the active page add a page there; Alt+Arrow moves to (or creates) the neighbour. Right-click a page or use its ⋯ button to insert, resize or delete it.
  • Importing a PDF into a non-empty canvas shows every free spot; click one to place the chain. Into a blank canvas it simply replaces the empty page. The Files panel can also import a PDF as a new canvas.
  • View: wheel/trackpad pans, Ctrl+wheel or pinch zooms, Ctrl+0 fits the page, Ctrl+9 shows the whole tree, space-drag or middle-drag pans.
  • Sync: Settings → Sync. Use a fine-grained token with Contents: read & write on the repo. Edits autosave locally; pushes happen every N minutes and on Ctrl+S / the sync button. Conflicts: newer wins.

Layout

  • src/lib/model — page tree (pure functions: insert/remove/flatten/layout).
  • src/lib/editor — markdown analysis shared by the CodeMirror live preview and the PDF text layout.
  • src/lib/pdf — PDF writer (vector text, Ink annotations, embedded source-of-truth files) and reader.
  • src/lib/ink — stroke recording/outlines and palm rejection (ported from palm-rejection-test.html).
  • src/lib/storage, src/lib/sync — IndexedDB vault and GitHub sync.

Fonts in static/fonts are pre-subset (scripts/subset-fonts.sh) because pdf-lib's own subsetter drops glyphs.