literary-garden Svelte Themes

Literary Garden

A digitized intellectual orbit — Kindle vocab + highlights as a botanically-illustrated garden. Local-first, BYOK template.

Literary Garden

A digitized intellectual orbit — a private, botanically-illustrated vocabulary practice and highlights scrapbook, grown from your own Kindle reading.


Requirements

  • macOS — walkthrough below is Mac-only. Windows/Linux welcomed as contributions.
  • A physical Amazon Kindle with Vocabulary Builder enabled (Settings → Language & Dictionary → Vocabulary Builder → On).
  • Node 20+ (node --version).
  • pnpm (npm i -g pnpm).

Not supported (yet)

  • Kobo, Boox, or other non-Kindle e-readers — they don't expose the vocab.db sqlite file.
  • The Kindle phone/tablet app — same reason.
  • Windows / Linux — step-by-step below is Mac only. Core code runs everywhere; the walkthrough is what's missing. PRs welcome.

Philosophy

Everyone has their own intellectual orbit — the shape of thought inherited from family, schooling, the city of one's youth. To live is to widen that orbit. Reading is the humblest route: a book is a letter from a stranger who has lived what you have not. This garden is the digital form of the orbit — pressed, tended, revisited, and sometimes shared as a postcard.

There are three states a word can be in: seedling (never reviewed), in bloom (actively learning), pressed (21+ days of proven memory). The garden visualizes where each of your words is, and nudges you to return daily.

A glimpse

The home page — a cameo, a title, and a "Dearest Gentle Reader" letter in the spirit of the garden's aesthetic (edit the letter in src/pages/index.astro to your voice after cloning):

Daily practice (the words bed) — flip a botanical card to rate your memory; the word settles into its FSRS interval and eventually presses:

The garden — an isometric plot that grows over days, weeks, months, and years as you press words:

The scrapbook share flow — pick any highlight, turn it into a 1080×1350 social-ready PNG with your personal wax-seal stamp:

Getting your Kindle data (Mac)

  1. Plug your Kindle into your Mac via USB.
  2. It appears in Finder under Locations as Kindle (usually at /Volumes/Kindle).
  3. In Finder, press ⌘ + ⇧ + . (Cmd-Shift-dot) to reveal hidden folders. The system/ directory becomes visible.
  4. Copy two files into this repo's data/raw/:
    • /Volumes/Kindle/system/vocabulary/vocab.dbrequired (powers the words bed + garden)
    • /Volumes/Kindle/documents/My Clippings.txtoptional (powers the scrapbook tab)

Nothing leaves your Mac. data/raw/ and public/covers/ are both gitignored. A pre-commit hook blocks any accidental git add -f on personal paths.

Setup

pnpm install                                        # also creates src/site.config.ts (see below)
# copy your Kindle files per the walkthrough above
pnpm seed                                           # process vocab.db + clippings
pnpm dev                                            # Astro at http://localhost:4321

Edit src/site.config.ts with your name and title. pnpm install auto-copies this file from src/site.config.example.ts the first time, so you don't have to. Subsequent installs leave your edits alone. The file is gitignored so your values stay local — the template ships with only the example committed.

Optional personalization:

  • Drop your own public/og-image.png (1200×630) for LinkedIn/Twitter/Facebook link previews.
  • PWA icons (home-screen icon on iOS/Android): the template ships with the repo's default cameo as the app icon. To brand the installed PWA with your own mark, replace public/cameo.png with your 512×512 PNG, then run pnpm tsx scripts/gen-icons.ts to regenerate icon-192.png, icon-512.png, apple-touch-icon.png, and favicon.png in one shot.
  • The PWA manifest name is auto-generated from siteConfig.siteTitle at build time via src/pages/manifest.webmanifest.ts — no manual sync needed.

Personalizing your share-card stamp

When you share a passage from the Scrapbook tab, the generated card closes with a small wax-seal "stamp" in the bottom-right corner. This is the signature that says "from this reader's garden." The template ships with a generic seal — replace it with your own mark for a personal touch.

Out of the box: The template ships public/stamp-default.svg (a generic red-wax seal with a botanical flourish). If you don't customize anything, every share card you generate uses this seal. It works; it's just not yours.

To use your own:

  1. Create or export your stamp as a PNG (~400×400 px, transparent background works best) or an SVG.
  2. Drop it into public/, e.g. public/alice-stamp.png.
  3. Edit src/site.config.tsstampImage: "/alice-stamp.png".
  4. Restart pnpm dev (or rebuild).

That's it. The share modal probes the configured path at open time and uses it if present; missing files silently fall back to the default seal, so there's no broken-image failure mode.

Artwork ideas: a signet initial (hand-drawn), a personal sigil, a wax-seal generator output, a small illustrated icon that means something to you. Avoid anything rectangular — the card foot is styled for a roughly circular mark.

public/*-stamp.png is gitignored by default (see .gitignore) so your personal stamp doesn't accidentally upstream into a fork. If you want to commit a shared team stamp, use a different filename like public/brand-mark.png.

Dev scripts

Script What it does
pnpm seed Import vocab.db + clippings → data/processed/. Incremental.
pnpm seed:refresh-covers Re-fetch book covers from Open Library even if cached.
pnpm seed:refresh-definitions Re-fetch definitions (dictionaryapi.dev).
pnpm seed:refresh Refresh both.
pnpm seed:offline Skip all network; use only cached enrichments.
pnpm reseed Seed + clear .astro cache (use after import changes).
pnpm dev Astro dev server at http://localhost:4321.
pnpm build Static build → dist/.
pnpm preview Serve the built site locally.
pnpm check Astro + Svelte type check.

Keyboard shortcuts

Key Action
Space / Enter Flip the practice card
1 Rate "didn't know"
2 Rate "blurry, had to think"
3 Rate "knew it instantly"
Swipe / (touch) Didn't know / knew it

Full legend + more context lives on the /about page in the running app.

Re-seeding when you have new Kindle data

Rerun pnpm seed whenever you've added new highlights or vocabulary. The contract:

  • New words are added as seedlings.
  • Existing words keep their FSRS card state — spaced-repetition progress is keyed by Kindle's stable word_id, stored in your browser's localStorage, and never touched by the seed script.
  • Words you've removed from your Kindle vocab don't vanish — their FSRS history stays in localStorage, cosmetically orphaned but harmless.
  • Re-seeding is safe to run as often as you like. It's incremental and never destroys progress.

In short: your practice history lives in the browser; the seed script only rewrites data/processed/*.json and public/covers/. They don't talk to each other except at render time.

Troubleshooting

  • pnpm seed exits with "No vocab.db found" — copy it from /Volumes/Kindle/system/vocabulary/vocab.db per the walkthrough above.
  • pnpm seed says "Seeded 0 words" — your Kindle's vocab.db is empty. Enable Vocabulary Builder on the device (Settings → Language & Dictionary → Vocabulary Builder → On), then look up a few words while reading. Then re-copy the db.
  • Covers missing for some books — Open Library and Google Books don't always have every edition. Sideloaded books in particular are often unknown. The placeholder illustrations are intentional; they won't harm practice.
  • Book title reads weirdly, e.g. "(Author) (Z-Library)" — the import script strips those artifacts from sideloaded filenames but the rule set is heuristic. Report a missed pattern as an issue and we'll extend it.
  • Practice cards or flora look stale after re-seeding — delete .astro/ (Astro's content cache) and restart dev: rm -rf .astro && pnpm dev. Or just pnpm reseed, which does both.
  • pnpm install fails on better-sqlite3 — Node native module; check that your Node matches the project's (node --version should be ≥ 20) and that you're on Apple Silicon (arm64). If you switched Nodes, pnpm install --force rebuilds.

Privacy

Local-first by default. Nothing is uploaded, tracked, or synced. Definitions and covers are fetched once at seed time and bundled into the built site.

data/raw/, data/processed/, public/covers/, My Clippings.sdr/, and book_cover/ are all gitignored. The pre-commit hook (scripts/block-private.sh) blocks any accidental git add -f on those paths.

Do not deploy this to a public URL. Even just public/covers/ — which is your own cover cache — reveals your reading history via filenames. If you want always-on access from your phone, see the next section for the password-gated private-deploy path.

Private Deploy (optional, owner-only)

If you want to browse your garden from your phone, the simplest path is a private Vercel deploy:

pnpm add -D vercel
vercel login
vercel link                                 # one-time: creates .vercel/, gitignored

Before the first deploy, in the Vercel dashboard → Project Settings → Deployment Protection, enable Vercel Authentication (free on Hobby). This gates your site behind Vercel's login — no public URL.

Also before the first deploy, make sure git config --local user.email matches an email verified on your GitHub account (see https://github.com/settings/emails). Vercel blocks deploys from commits whose author email isn't GitHub-verified — the default Mac-generated [email protected] trips this check. If you've already committed with the wrong email, rewrite history:

git config --local user.email "[email protected]"
git config --local user.name "Your Name"
git filter-branch -f --env-filter '
  export GIT_AUTHOR_NAME="Your Name"
  export GIT_AUTHOR_EMAIL="[email protected]"
  export GIT_COMMITTER_NAME="Your Name"
  export GIT_COMMITTER_EMAIL="[email protected]"
' -- --all
git push -f origin main

(Force-push is safe on a brand-new repo with no collaborators. Skip this step if your commits were already authored correctly.)

pnpm deploy:prod      # seed → build → vercel build → vercel deploy --prebuilt

(NB: the script is deploy:prod, not deploy — the latter is a pnpm built-in that does something unrelated.)

Why deploys are CLI-only, not git-triggered. The repo ships with vercel.json setting git.deploymentEnabled.main: false. Reason: a remote git-triggered build would try to run pnpm seed, which needs data/raw/vocab.db (your personal Kindle file, gitignored). Seeding would fail on Vercel with no vocab.db. So we only deploy via the CLI's prebuilt path — seed + build locally, upload the static output. If you want git-triggered deploys (e.g. after migrating to a setup that doesn't need local Kindle data), delete the git.deploymentEnabled block from vercel.json.

Three warnings you must not skip:

  1. Enable Deployment Protection before deploying. Settings → Deployment Protection → Vercel Authentication → on.
  2. After your first deploy, immediately remove the three "short" aliases. Vercel Hobby's Standard Protection only covers the hashed deployment URL (e.g. literary-garden-ab12cd-...), NOT the short aliases it auto-creates (your-project.vercel.app, your-project-<scope>.vercel.app, your-project-git-main-<scope>.vercel.app). Those serve publicly. Find them with pnpm vercel alias ls | grep your-project and remove each with pnpm vercel alias rm <alias> --yes. Then verify with curl -I <alias> — you want a 404, not a 200.
  3. State is per-browser. Your spaced-repetition progress lives in each device's localStorage. Pressing a word on your phone doesn't sync to your laptop. Pick one "primary" device for serious practice.

If you need shared-password access (a friend who doesn't have a Vercel account), switch to Cloudflare Pages + Access — their free tier allows magic-link email policies and up to 50 users. The pnpm build output is the same; only the deploy step changes.

Stack

  • Astro v6 (static site)
  • Svelte 5 (interactive islands, runes mode)
  • Tailwind v4 (CSS-first, OKLCH palette)
  • better-sqlite3 (read vocab.db at build time)
  • ts-fsrs (spaced repetition)
  • html-to-image (Pressed Album + Quote PNG export)
  • simple-git-hooks (the pre-commit privacy guard)
  • sharp (scripts/gen-icons.ts for PWA icons)

Project status

  • v1 — words bed, practice, garden: shipped.
  • Scrapbook tab (Kindle highlights): shipped.
  • BYOK template + share export: shipped (this pass).
  • Private deploy pipeline: documented.

Plans + brainstorms

Top categories

Loading Svelte Themes