A fling toward a free side of the active page now offers a page at any zoom or pan position, not only once that edge is on screen. If the side's plus is visible it pulses as before; otherwise an "Add page" button appears at that edge of the screen, tapped through the same palm-checked gesture as the plus. Settings → Pen & touch gets "Swipe to add pages" (on by default). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GaoZtJQZwLXkEgWs8twCia
102 lines
6.9 KiB
Markdown
102 lines
6.9 KiB
Markdown
# Lecture Notes App — Architecture Doc v1
|
|
|
|
## 1. Page Tree Model ("Comb")
|
|
|
|
One canvas = one tree = one `.pdf` file.
|
|
|
|
- **Trunk**: a single vertical chain of pages. Grows **up or down** (both allowed).
|
|
- **Branch**: a horizontal chain of pages attached to exactly one trunk page, going **left or right**.
|
|
- A branch can only ever continue in the direction it started (strictly linear — no sub-branches off a branch).
|
|
- A trunk page can have at most one left branch and one right branch.
|
|
- **PDF import**: creates a chain of pages in whatever direction you pick when placing it (up/down = extends the trunk, left/right = becomes a branch off the trunk page you clicked). Only one trunk exists per canvas — additional imported PDFs must attach as a branch off some trunk page, or extend an existing branch further outward in its established direction.
|
|
- Each free side of the active page shows a small "+" circle just off its edge; tapping it (or `Alt+Arrow`) adds a blank page there. A quick finger fling toward a free side offers a page there for a few seconds: that side's "+" pulses, or, if it is off screen, an "Add page" button appears at that edge of the screen. The fling never adds a page by itself, and can be turned off in Settings.
|
|
- The canvas background is a faint grid with one cell per active page, aligned to its edges; Settings can hide it or subdivide it.
|
|
- Deleting a page removes it (undo via Ctrl+Z); no permanent trash.
|
|
- Default zoom: one page fills the screen. Zoom out reveals the tree.
|
|
- Pages default to A4, or auto-match the aspect ratio of an imported PDF page; resizable manually otherwise (page menu: presets, or a custom size in mm).
|
|
|
|
## 2. Flatten Algorithm (tree → linear page order, e.g. for PDF page order / print)
|
|
|
|
Starting at the **topmost** trunk node, walk down:
|
|
|
|
```
|
|
for each trunk_node, top → bottom:
|
|
emit trunk_node
|
|
emit left_branch pages, closest-to-trunk → outward
|
|
emit right_branch pages, closest-to-trunk → outward
|
|
```
|
|
|
|
This is recomputed at export/save time (not creation order), since the trunk can grow upward later and shift what "topmost" means.
|
|
|
|
## 3. File Format
|
|
|
|
One canvas = **one `.pdf` file**, dual-layer:
|
|
|
|
- **Visual layer** (any standard PDF viewer sees this): rendered markdown as real vector text, ink drawn as PDF Ink annotations, original imported PDF content where applicable. Fully readable/printable by anyone, no app required.
|
|
- **Source-of-truth layer** (embedded file attachments, invisible in normal viewers): raw markdown source per page, full vector stroke data (points + pressure + tilt — richer than what Ink annotations can hold), and grid metadata (each page's tree position, size, and which PDF import it originated from, for provenance search).
|
|
- App reads the attachment layer first to reconstruct the live editable canvas; falls back to parsing visual content only if attachments are missing.
|
|
|
|
**Libraries**: `pdf-lib` (write/edit/attach), `pdf.js` (render/import).
|
|
|
|
**Trade-off accepted**: binary PDFs mean no line-level git diffs. Flag for later if this becomes annoying — we could add a parallel auto-generated `.md` export per canvas purely for git-diff readability, PDF stays canonical.
|
|
|
|
## 4. Organization
|
|
|
|
Canvases (PDF files) live in a **file tree** you define, e.g. `school/english/lecture1.pdf`, `school/math/lecture2.pdf`, `work/...`. Folders are purely organizational, no canvas of their own.
|
|
|
|
## 5. Sync & Storage
|
|
|
|
- Repo-backed (GitHub for now; GitLab not implemented), one file per canvas.
|
|
- PWA: offline-first, caches locally, explicit save/sync pushes to repo.
|
|
- Multi-device: git pull/push is the sync mechanism (not real-time collab).
|
|
|
|
## 6. Tech Stack (tentative)
|
|
|
|
- **Frontend**: SvelteKit, deployed on your VPS, installable as PWA.
|
|
- **Ink input**: Pointer Events + pressure (your existing artifact — pending).
|
|
- **PDF read/write**: pdf.js + pdf-lib.
|
|
- **Storage**: IndexedDB locally; Git repo via the GitHub REST API (token in the browser, no proxy).
|
|
|
|
## 7. Confirmed Product Features
|
|
|
|
- Live-rendered markdown (background layer) + vector ink (foreground layer), never converted to text.
|
|
- Full ink toolset: colors, highlighter, eraser.
|
|
- Search across typed markdown text.
|
|
- Pages remember their PDF-import origin, searchable.
|
|
- No export needed beyond the PDF itself (for now).
|
|
|
|
## 8. Sync, Auth & Conflicts (resolved)
|
|
|
|
- **Git auth**: personal access token stored in the browser/PWA (no backend proxy, for now).
|
|
- **Save model**: autocommit + periodic push, plus a manual "sync now" button that does pull + push on demand.
|
|
- **Conflicts**: newer version wins (timestamp-based), no merge UI for now.
|
|
- **Repo size**: not a concern for now — revisit if it becomes a problem.
|
|
|
|
## 9. Pen Input & Palm Rejection
|
|
|
|
Source: user-provided `palm-rejection-test.html` test harness. The acceptance policy carries over almost verbatim:
|
|
|
|
- `pointerType === 'pen'` is always accepted; its down/move/up updates `lastPenActivityTime`.
|
|
- A `touch` pointer is rejected if any of: a pen stroke is active or was active within the last ~5s, its contact ellipse (`width`/`height`) exceeds ~35px (palm heel vs. fingertip), it's within 150ms of the last pen activity, or a pen is currently hovering (armed lockout before it even touches down).
|
|
- A touch stroke already in progress gets cut off mid-stroke if pen activity appears.
|
|
|
|
**How the app applies it** (`src/lib/ink/palm.ts`):
|
|
- Each touch is evaluated with an intent. `draw` (finger ink) gets the full policy above. `navigate` (pan, pinch, and taps on canvas buttons such as the add-page "+") skips the 5 s pen session and the hover lockout, so fingers can move around and tap between pen strokes; pen-down, wide contacts and the 150 ms window still reject it.
|
|
- Once a pen has been used, fingers only navigate. "Draw with finger" in Settings turns finger ink off entirely.
|
|
- Each rule (pen session, wide contact, timing, hover) can be switched off in Settings → Pen & touch.
|
|
- An interrupted pointer (`pointercancel`) drops the stroke or erase in progress instead of committing it.
|
|
|
|
**Ink engine (implemented)**:
|
|
- `StrokeRecorder.recordPoint()` stores `x, y, pressure, tiltX, tiltY, t` per sample in a flat `Stroke.points` array (stride 6); rendering replays it. Tilt is recorded but not yet used for rendering.
|
|
- Outlines are smoothed with `perfect-freehand`; mouse and finger strokes simulate pressure.
|
|
- Pen, highlighter (translucent, no thinning) and stroke eraser, with configurable colour palettes; the pen's eraser end erases too.
|
|
- Undo/redo covers ink, erasing and page-tree changes.
|
|
- Strokes are written to the PDF both as Ink annotations and as JSON attachments.
|
|
|
|
## 10. Open Items
|
|
|
|
- [ ] Conflict copies: sync currently lets the newer version win silently; keep the losing version as a separate file.
|
|
- [ ] Tell the user when a new version of the app has been deployed (the service worker updates, but the open tab keeps old code until reload).
|
|
- [ ] CI that runs `check`, `test` and `build` on every pull request.
|
|
- [ ] Use recorded tilt for rendering.
|
|
- [ ] GitLab sync.
|