Extends the Phase-1 monogram mark system to every long-form content type (essays, blog posts, poems, fiction, music) and introduces a coverage audit so gaps are visible. * build/Marks.hs gains hasMonogram (predicate), monogramSvgFieldFor + hasMonogramFieldFor (for explicit-path callers like the /build/ and /stats/ pages). Contexts.hs exports hasMonogramField as a siteCtx boolean so templates can conditionally render the slot without emitting an empty <div>. * essay.html, blog-post.html, reading.html: hoist the frontmatter block out of <main id="markdownBody"> so the monogram + epistemic marks render as wrapper chrome rather than indexable prose; left + right mark slots are now unconditional (CSS handles the empty state) so the layout is grid-stable across pieces. * templates/partials/item-card.html: optional monogram chip on cards (item-card--has-monogram modifier), gated on $has-monogram$ so monogram-less pieces stay flush. * build/Stats.hs grows a "Marks coverage" telemetry section: per-type pieces / monogram / epistemic-figure counts + a coverage rollup, rendered between epistemic and output on /build/. * tools/audit-marks.py: coverage report (ASCII table) walking content/**/*.md, plus a pre-commit hook at tools/hooks/pre-commit-marks.sh that runs the same scan against newly-staged .md files. New `make audit-marks` runs the report manually; the hook gates commits. * static/css/marks.css: layout for the new frontmatter slots and the item-card monogram chip. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|---|---|---|
| archive | ||
| build | ||
| content | ||
| data | ||
| nginx | ||
| static | ||
| templates | ||
| tools | ||
| .env.example | ||
| .gitignore | ||
| .python-version | ||
| ARCHIVE.md | ||
| AUDIT.md | ||
| HOMEPAGE.md | ||
| LICENSE | ||
| MARKS.md | ||
| Makefile | ||
| PHOTOGRAPHY.md | ||
| README.md | ||
| WRITING.md | ||
| cabal.project | ||
| cabal.project.freeze | ||
| levineuwirth.cabal | ||
| pyproject.toml | ||
| uv.lock | ||
README.md
levineuwirth.org
Personal site of Levi Neuwirth — essays, blog posts, poetry, fiction, and music.
Built with Hakyll and Pandoc,
with a custom build system in build/ and a Haskell + JS + Python toolchain.
Quickstart
make build # one-shot production build into _site/
make dev # dev build (drafts visible) + local server on :8000
make watch # cabal-watch rebuild (drafts visible)
make clean # cabal run site -- clean
make deploy # clean → build → sign → push → rsync to VPS
make build always runs make clean implicitly when invoked from make deploy.
For day-to-day work, prefer make dev (which serves the site on
http://localhost:8000) or make watch (rebuilds on save without a server).
Run make build any time you add or replace binary assets (JPEG/PNG
figures, PDFs, music assets). make dev and make watch skip the
convert-images.sh / pdf-thumbs preprocessing steps, so a fresh JPEG
will have no .webp companion and a fresh PDF will have no thumbnail
until a full make build regenerates them. Once the companions exist
they survive subsequent make dev runs.
Optional features
-
Similar-links and embeddings.
tools/embed.pyprecomputes page-level embeddings for the "Related" block. To enable:uv sync # creates .venv with sentence-transformers, faiss-cpuThe build silently skips embedding when
.venvis absent. -
Client-side semantic search. Downloads a quantized ONNX model used by
static/js/semantic-search.js(run once; files are gitignored):make download-model -
Image conversion.
make buildcallstools/convert-images.shto produce.webpcompanions next to every JPEG/PNG. Requirescwebp(libwebpon Arch,webpon Debian/Ubuntu). -
PDF thumbnails.
make pdf-thumbsgenerates first-page thumbnails for PDFs instatic/papers/usingpdftoppm(poppleron Arch,poppler-utilson Debian/Ubuntu). Skipped silently when missing.
Configuration
.env (gitignored, copy from .env.example) holds the GitHub PAT and
the VPS rsync target consumed by make deploy. Never commit it.
Repository layout
build/— Haskell build system (Hakyll rules, Pandoc filters, contexts). Seebuild/Filters/for the Pandoc AST transforms (sidenotes, wikilinks, transclusion, score embedding, viz, …).content/— authored Markdown (essays, blog, poetry, fiction, music).templates/— Hakyll/Pandoc HTML templates.static/— CSS, JS, fonts, images, vendored PDF.js.tools/— Python tooling (embeddings, importers) and shell scripts.data/— generated and source data (commonplace.yaml, annotations.json, bibliographies, similar-links.json).nginx/— vhost snippets shipped to the VPS (security-headers.conf,static-assets.conf,popup-proxy.conf). The live vhost on the VPS is the source of truth; seenginx/vhost.conf.examplefor the canonical structure and the include order these snippets expect.
Architecture pointers
build/Site.hsis the Hakyll rules entry point.build/Patterns.hsdefines canonical content patterns shared by Backlinks, Authors, Tags, and Site.build/Compilers.hswires the Pandoc filter chain into Hakyll.build/Filters/Images.hsdoes WebP<picture>wrapping; requires the.webpcompanions produced bytools/convert-images.sh.
License
See LICENSE.