- import-photo.sh: validate \$SLUG against ^[a-z0-9-]+\$ before writing under content/photography/; rejects '../' and other surprises early. Also fail loudly with a clear message if either 'magick' resize or 'magick mogrify -strip' returns non-zero (prevents shipping a file that still carries EXIF when the strip silently failed). - compress-assets.sh: reject non-numeric MIN_SIZE up front (otherwise the comparison fails later with a cryptic arithmetic error). - extract-dimensions.py / extract-exif.py / extract-palette.py: add traceback.print_exc() after the broad except so a corrupt image produces a stack trace alongside the one-line summary. - extract-exif.py: switch from Pillow's deprecated _getexif() to the public getexif() API; pyproject allows Pillow up to 12 where _getexif may be removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| build | ||
| content | ||
| data | ||
| nginx | ||
| static | ||
| templates | ||
| tools | ||
| .env.example | ||
| .gitignore | ||
| .python-version | ||
| AUDIT.md | ||
| HOMEPAGE.md | ||
| LICENSE | ||
| 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.