Skip to content

Latest commit

History

1,078 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Notes

Version

Field notes from a life being lived: essays, household recipes, and the books on the shelf. Fatherhood, cooking, culture, work. English and Spanish, with links between twins.

This is not a magazine. It is not a recipe site with a blog attached. One public notebook. Essays and recipes share the same collection. Recipes live under /p/recipes/ and list on the Cookbook.

Live site: notes.antoniwan.online

Release history: CHANGELOG.md

What a reader sees

  • Writing — essays and notes in src/content/p/ (Markdown and MDX)
  • Cookbook/recipes is a plate grid plus A–Z contents. Recipes ship at /p/recipes/<slug>. English listed; Spanish via the dish toggle
  • Book library — books on the shelf, under /library and /library/books
  • Paths in — Guided Path (seasonal order, progress in the browser only), Everything (English archive), categories, tags as an idea map, header search
  • Home — highlight masonry for featured English posts
  • Writing Insights (/writing-insights) — cadence, topics, lexicon. /brain-science is the origin note; old dashboard subpaths 301
  • Feeds — RSS (/rss.xml) and JSON Feed (/feed.json). Spanish stays out of feeds, same as listings
  • Quotes APIGET /api/quotes (Stoic excerpts, other philosophy, lines from posts; optional ?kind=)

English is the listing language. Spanish is a twin: language toggle, title search, SEO, direct URL. Not a second card stream. Details: docs/multilingual-setup.md.

How it is built

  • 10 categories (for example On Parenting; Psychology, Roughly; On Politics; Metaspace; DIY & Creation) — essays only; household recipes list on the Cookbook and Everything. See src/data/categories.ts
  • Tags — idea map for readers; Tag management is an author overview (noindex). Tags describe content, not the site name
  • Dark and light theme, including system preference
  • Responsive layout for small and large screens
  • Reading time — remark plugin (minutesRead). Hidden on recipe cards and dish pages
  • Reading progress on posts — localStorage only, no server
  • Table of contents on long posts — floating contents control
  • Schema.org JSON-LD where it fits the page type
  • Comments — optional Remark42 when env vars are set (see docs/comments-setup.md)
  • Service worker — registration URL includes the package version from package.json
  • On Vercel: Web Analytics and Speed Insights in the base layout (they only send data when those products are enabled)

Stack

  • Astro 7 — hybrid output (static pages + on-demand GET /api/quotes), MDX, @astrojs/vercel adapter
  • TypeScript
  • Tailwind CSS
  • Sharp — used by the social-image step to resize AVIF sources to JPEG/PNG

Quick start

git clone https://github.com/antoniwan/notes.git
cd notes
pnpm install
pnpm run dev

Then open http://localhost:4321.

This repo expects pnpm; you can use npm or yarn if you change commands yourself.

Environment variables (optional)

For production builds of the About page, Letterboxd “latest watched” needs RSS URLs. Copy .env.example to .env.local and set:

  • LETTERBOXD_PROFILE_URL
  • LETTERBOXD_RSS_URL

If they are missing, that block on About simply won’t have fresh data (or may be empty depending on fallbacks).

Remark42 uses PUBLIC_REMARK42_HOST and PUBLIC_REMARK42_SITE_ID when you turn comments on — see docs/comments-setup.md. Both are listed in .env.example.

Scripts

CommandWhat it does
pnpm run devDev server
pnpm run buildBuilds social JPG/PNG from AVIF (skipped when fingerprints + files match), then astro build
pnpm run previewServes the production build locally
pnpm testVitest unit tests (publish filters, SEO routing, feed HTML, quotes helpers)
pnpm changelog:sinceCommits + file groups since the previous version (for CHANGELOG drafts)
pnpm run checkastro check (TypeScript / Astro diagnostics)
pnpm run lintESLint
pnpm run lint:fixESLint with --fix
pnpm run formatPrettier write
pnpm run format:checkPrettier check (CI verifier; does not rewrite files)
pnpm run audit-frontmatterRequired-field / language sanity check on src/content/p
pnpm run validate-feedsValidates dist/rss.xml + dist/feed.json (run after build)
pnpm run validate-structured-dataSmoke-checks structured-data module exports
pnpm run generate-social-imagesAVIF → JPEG/PNG under public/social/ only (same logic as the start of pnpm run build)
pnpm run generate-faviconsFavicon assets
pnpm run analyzeRuns astro build only (no social-image step), then Vercel static-build analysis
pnpm run lighthouseLighthouse HTML report (start dev server first)
pnpm run performanceRuns pnpm run build, then pnpm run analyze
pnpm run audit-performanceFull pnpm run build, then Lighthouse performance JSON

CI’s format step checks; it does not rewrite or open a follow-up commit. After pnpm install, a pre-commit hook runs Prettier on staged files so commits already match that check.

Build (social images)

pnpm run build runs scripts/generate-social-images.js before astro build.

Hero images are stored as AVIF under public/. Many preview surfaces still expect JPEG, so the script writes 1200×630 cover-cropped JPEGs under public/social/ (names end with -social.jpg). The mapping lives in src/data/socialImageManifest.ts. Posts without a heroImage share /social/images/default-social.jpg.

Each AVIF is hashed (SHA-256). If the hash matches src/data/socialImageFingerprints.json and the output file is on disk, that file is skipped. After adding or changing AVIFs, run pnpm run build or pnpm run generate-social-images and commit the updated manifest, fingerprints, and any new files under public/social/.

CI restores public/social/ from cache when possible (see .github/workflows/ci.yml). Timing depends on how many images need encoding; routine builds with everything already up to date stay short.

Project layout

notes/
├── public/ # Static assets; generated social JPEG/PNG live under public/social/
├── scripts/ # generate-social-images.js, generate-favicons.js
├── src/
│ ├── components/ # Astro components (shared + feature folders like brain-science/)
│ ├── config/ # Comments, storage, assets
│ ├── content/p/ # Essays and notes (Markdown / MDX)
│ │ └── recipes/ # Household recipes → /p/recipes/<slug>
│ ├── data/ # Categories, navigation, socialImageManifest.ts, socialImageFingerprints.json, …
│ ├── layouts/
│ ├── pages/ # Routes (cookbook, category, tag, writing-insights, api, …)
│ │ └── recipes.astro
│ ├── styles/
│ ├── utils/
│ └── types/
├── docs/ # Longer how-tos and specs
└── astro.config.mjs

Feature-specific components live under src/components/<feature>/ when they are only used by matching routes. Shared pieces sit at the top level of src/components/.

Content

One collection (blog in src/content.config.ts). Folder is the discriminator, not a kind field:

PathPublic URLListing
src/content/p/<slug>.md/p/<slug>Everything, categories, tags, home Highlights, Guided Path, feeds
src/content/p/recipes/<slug>.md/p/recipes/<slug>Cookbook (/recipes). Cards say Recipe / Receta. More Recipes instead of Continue reading

Frontmatter: docs/frontmatter-spec.md. Tags: docs/tag-policy.md and docs/tag-vocabulary.md.

Translations: same translationGroup on each language version. Spanish stays off English listings; English cards show an ES marker when a twin exists. Details: docs/multilingual-setup.md.

Using Cursor AI in this repo: docs/cursor-agent-skills.md.

Documentation in docs/

FileTopic
frontmatter-spec.mdPost frontmatter
tag-policy.mdSignal-first tag policy
tag-vocabulary.mdCanonical vocabulary
tag-cleanup-assessment-2026-04-10.mdCurrent cleanup audit
multilingual-setup.mdEN/ES linking
comments-setup.mdRemark42
quotes-api.md/api/quotes
structured-data-optimization.mdSchema.org
performance-optimization.mdPerformance notes
roadmap.mdIdeas, product audit, technical roadmap
TECHNICAL-AUDIT.mdSystem map, integrations, technical debt
cursor-agent-skills.mdCursor agent skill guide
midjourney-og-image-prompts.mdImage prompt notes

Private generated materials

Generated planning/audit reports in docs/materials/ are intentionally local-only and private. The folder is gitignored (except docs/materials/.gitkeep) so these files are not committed or published from this repository.

License

About

⚡ A modern, secure, SEO-optimized Astro blog system powering notes.antoniwan.online

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages