Skip to content

FlynnSolutions/NoteSync

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

74 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NoteSync

Edit your Markdown notes by hand on a Supernote e-ink tablet, and have the changes flow back into the source files automatically — checkboxes ticked, lines struck, margin notes applied — read by Claude from your actual handwriting.

Your Markdown is the source of truth. The tool renders it to PDFs on the device, you annotate in ink, and on check-in it extracts your strokes, has Claude interpret them, and 3-way-merges the result back into the source. Write in any pen colour; counts and tallies stay correct on their own; every automated edit is a revertible git commit.

   render (mirror)                annotate by hand            read ink (.mark layer)
 Markdown ──────────► PDF on Supernote ──────────► your ink ──────────► Claude reads it
    ▲      source = truth      │  (Google Drive sync)      │                    │
    │                          ▼                           ▼                    ▼
    └────────── 3-way merge into source ◄── recompute derived counts ◄── interpret + apply
                (auto-commit, revertible)

The bigger picture

This tool is one piece of a larger idea: a knowledge architecture for human + Claude collaboration — a structure where your thoughts, your docs, and project state are organized so that the right context surfaces at the right moment with minimal friction, both for you to find things and for Claude to recall them. Handwriting on the tablet is one input; the real product is retrieval and shared memory. Everything below is in service of that: plain Markdown, in git, that a person and an agent can both read and trust.

Why it works the way it does

  • Markdown is canonical. The PDF on the device is a disposable view; edits are folded back into the .md and a fresh PDF is re-rendered.
  • Ink comes from the .mark layer, not pixels. A Supernote annotation sidecar (<doc>.pdf.mark) is a native Supernote document whose strokes live in a layer separate from the page — so we extract them directly with supernote-tool. This is colour-independent (black pen works as well as red) and exact, with none of the fragility of trying to colour-filter a flattened export.
  • The LLM interprets; code derives. Claude only does the genuinely fuzzy part — reading handwriting and deciding what a mark means. Anything that's a pure function of the document (e.g. a "Quick stats" tally of [x]/[ ]/[!] items) is recomputed deterministically in code, so it can't drift or become a merge conflict.
  • Conflict-safe + revertible. Device edits and desktop edits are reconciled with a real 3-way merge (true collisions surface as conflict markers rather than auto- applying). Source docs live in git, and every automated edit is its own commit.

Requirements

  • Python 3.11+ (uses tomllib)
  • poppler — provides pdftoppm (brew install poppler / apt install poppler-utils)
  • Claude access for the handwriting read — by default your Claude subscription via the claude CLI (claude -p; no API key, no metered tokens). Or switch to the Anthropic API backend (backend = "api" in config) with an ANTHROPIC_API_KEY.
  • A Supernote tablet synced to a cloud folder your computer can see (the reference setup is Google Drive for Desktop; any path works via config)

Setup

git clone <this-repo> NoteSync && cd NoteSync
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt

# Config: copy the example and edit it (config.toml is gitignored).
cp config.example.toml config.toml      # set source_base, scan_roots, supernote_root

# Handwriting reader: your Claude subscription (the `claude` CLI) is the default — no key.
# Only if you set backend = "api" in config.toml, add your Anthropic key:
cp .env.example .env                     # ANTHROPIC_API_KEY (api backend only)

# Put your notes under version control so every auto-edit is revertible:
git -C "$(python config.py source_base)" init   # if not already a repo

It renders out of the box with the bundled DejaVu Sans. To use your own font, point font_regular / font_bold / font_italic in config.toml at your TTFs.

Every setting can also be supplied via environment (handy for headless/cloud runs), which overrides config.toml: SUPERNOTE_SOURCE_BASE, SUPERNOTE_SCAN_ROOTS (comma-separated), SUPERNOTE_ROOT, SUPERNOTE_MODEL, SUPERNOTE_CHECKLIST.

Usage

./sync.sh mirror                 # render the whole notes tree -> PDFs on the device (+ prune renamed/moved orphans)
#   ... annotate a doc by hand on the tablet, then "Export with annotations" ...
./sync.sh in                     # extract your ink, route it, report any drift
./sync.sh process <rel> --apply  # Claude reads the ink -> 3-way merge -> apply -> re-render
./sync.sh status                 # show the device Document/ and EXPORT/ folders
  • <rel> is the source path relative to source_base, e.g. notes/todo.md.
  • Run process without --apply first to preview; it only writes with --apply.
  • A clean merge applies and re-renders; a real collision writes conflict markers to merge_out/ for you to resolve, then re-mirror.

How the round-trip works

  1. mirror walks source_base (limited to scan_roots), renders each Markdown doc to a tablet-shaped PDF at the matching path under the Supernote sync folder, snapshots the exact source bytes to base/, and writes manifest.json (PDF → source + hash). It also recomputes any self-labelled count lines so tallies stay honest, and prunes device files orphaned by a renamed/moved/deleted source (see below).
  2. You annotate the PDF on the device in any pen, then Export with annotations — the explicit "I'm done" signal.
  3. in pulls your strokes straight from the .mark layer (marklayer.py), composites them over the source page for context, and routes the doc back to its source, flagging whether the source also changed on the desktop (route.py).
  4. process has Claude read the ink against the page + a handwriting-quirks profile (read_ink.py), then 3-way-merges its interpretation into the live source with the original render as the common ancestor (reconcile.py). Counts are re-derived, the edit is committed, and the doc re-renders to the device.

Once a .mark has been read it's logged by content hash (marks.py), so the device re-uploading the same ink never reprocesses or freezes the doc.

Self-cleaning on rename, move, or delete

The mirror only adds PDFs, so renaming or moving a source folder once stranded its whole old tree on the device. mirror now prunes: after writing the manifest it deletes any device PDF (plus its base/ snapshot and already-read .mark) that's no longer in the manifest — the orphans a rename/move/delete leaves behind. It only ever touches the tool's own namespace (Document/Library + base/), never your tablet's Document/, EXPORT/, or Note/ content.

One hard rule: if an orphan still has unread handwriting (an unprocessed .mark on a doc whose source moved), the whole prune aborts and tells you to reconcile it first — so cleanup never deletes around ink you haven't applied yet. mirror.py --force-prune overrides to prune the rest, and still never deletes a pending mark.

Pruning is on for ./sync.sh mirror and the unattended heartbeat (run.py), and off for a bare python mirror.py (safe to run anywhere). Deletions go to the Drive mount (recoverable from Drive trash ~30 days). Preview anytime with ./sync.sh mirror --dry-run.

Every note is an instruction

You don't tag anything. Claude treats every mark as if it were prefixed with @claude: it carries out what the note intends rather than transcribing your words into the page, and it rebuilds the sections you touch so the doc comes back finished, not annotated. It reads the rest of the repo for context whenever a note needs it, and leaves sections you didn't touch (and derived counts) alone.

  • A check / strike / status marker → done / remove, applied as-is.
  • A margin note or directive ("tighten this", "reconcile with the punchlist", "these are the decisions") → carried out, with whole-repo awareness when the change depends on information beyond this page.
  • New lines in a blank area → folded in as new content.

(@claude is still honored as optional emphasis but is no longer needed. Repo-awareness uses the claude_code backend — the default.)

Document formatting

It syncs any Markdown tree — folders carry no special meaning. Two light conventions:

  • Checkboxes style themselves- [ ] / [~] / [x] / [!] / [-] render as real boxes on the device, in any doc, no setup.
  • Optional frontmatter — a --- block at the top (parsed and stripped, never shown on the PDF) tunes a doc: format: checklist adds a status legend, format: book gives a clean prose layout. Most docs need none.

See docs/05-conventions.md for the details and RECIPES.md for worked structures (a punch-list, a book, reviewing a repo's docs) with runnable examples/.

Project layout

File Role
config.py All environment/user settings (env > config.toml > auto-detect)
mirror.py Render the notes tree → device PDFs + manifest
marklayer.py Extract ink from the .mark layer, composite over the page
read_ink.py Claude reads the handwriting → interpreted Markdown
reconcile.py 3-way merge device edits into the source
derive.py Recompute derived counts/totals deterministically
marks.py Ledger of already-read annotations (by content hash)
vcs.py Auto-commit each applied edit (revertible restore points)
pending.py / route.py Find pending annotations / route back to source
render.py / export.py Markdown → tablet-shaped PDF
sync.sh One-command orchestration of the above

Design notes and decisions live in docs/.

Status

The local check-out/check-in loop is complete and battle-tested. Running it unattended (so it works with the laptop shut) is designed but not yet deployed — a persistent container that rclone-mounts Drive and runs the loop; see deploy/HOSTING.md and docs/04-status.md.

License

MIT — see LICENSE. The bundled DejaVu Sans font is under its own permissive license (assets/fonts/DejaVu-LICENSE.txt).

About

Hand-annotate Markdown notes on a Supernote e-ink tablet; Claude reads the ink from the .mark layer and 3-way-merges edits back into source.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages