aphid 0.2.0

aphid 0.2.0 is a theming refresh. Your content builds the same as it did before — blog posts still publish, wiki-links still resolve, URLs are unchanged. What’s changed is which variables show up in which templates, plus a couple of small dev-server quality-of-life wins. If you’re using the bundled themes unmodified, you can upgrade with no edits; if you’ve forked or written your own theme, read on.

Why

Templates used to receive a single per-page context object with every conceivable field on it — author, image, description, category, backlinks, the works. Most fields were blog-only or wiki-only, populated as empty for the kinds that didn’t need them, and gated by {% if … %} in the templates. That meant template authors had to remember which fields were “real” on which page kind, and dead fields silently sat there forever.

This release splits the per-page context into three shapes — one for blog posts, one for wiki pages, one for standalone pages — each carrying only the variables its template actually uses.

Theme changes — what to update

The bundled themes have been updated. If you’ve forked or written your own theme, here’s what to change.

wiki_page.html gains category

Wiki pages now expose a per-page category variable for templates to render (e.g. as a breadcrumb or header label). Pages without an explicit category in frontmatter fall back to wiki_default_category (default "Other"), so the field is always non-empty.

<header>
    <h1>{{ title }}</h1>
    <p class="wiki-category"><a href="/wiki/">Wiki</a> &rsaquo; {{ category }}</p>
</header>

Existing themes that don’t render category continue to work — you just won’t see it.

wiki_categories[].name is now a string, never null

The wiki sidebar grouping (wiki_categories on wiki_page.html, categories on wiki_index.html) used to expose name: string?, requiring templates to fall back to a literal "Other" for the uncategorised group:

<!-- Old -->
<div class="wiki-cat-name">{% if category.name %}{{ category.name }}{% else %}Other{% endif %}</div>

The default is now applied at the renderer using the new wiki_default_category config field. name is always populated, so the conditional collapses:

<!-- New -->
<div class="wiki-cat-name">{{ category.name }}</div>

The old form still works (the {% if %} is just always-true), but you can simplify when convenient.

backlinks removed from blog_post.html

The v1 scope spec said “Backlinks on wiki pages” — singular. The implementation drifted into populating backlinks on every per-page context, including blog posts. That’s now reverted: backlinks is a wiki_page.html-only variable.

If your blog template renders a backlinks section, drop it. If you don’t, the variable simply isn’t there to use.

tags removed from wiki_page.html

Wiki frontmatter still accepts tags, and tagged wiki pages still appear on tag listings. But the tags variable is no longer exposed on the wiki page template itself — neither bundled theme rendered it. If you were using it on a custom wiki page, switch to displaying tags on the tag-listing pages instead, or open an issue and we’ll talk about restoring it.

wiki_categories removed from blog_post.html and page.html

The full sidebar grouping was previously exposed on every per-page context but only ever used by wiki_page.html. It’s now only on wiki pages, where it makes sense.

kind removed from per-page contexts

The kind variable ("blog" / "wiki" / "page") was redundant — each template is its kind, so no template branched on it. Gone. If yours did, replace with separate templates per kind.

Universal core: title, url, content, toc, contains_mermaid

These five plus the shared site fields (site_title, nav_pages, socials, favicon_tags, feed_atom_url, feed_rss_url, base_url, version) are present on every page template. Previously toc was wiki+blog only; it’s now also available on page.html so themes can render a table of contents for standalone pages too.

The full per-template variable reference is in Themes.

Frontmatter changes

wiki frontmatter’s title field accepts the same YAML it always has — omit it and the page derives its title from the filename stem.

wiki frontmatter’s category is also unchanged in YAML (still optional). What changed is what the renderer does when it’s omitted: instead of leaving the value empty for templates to handle, the renderer substitutes wiki_default_category (default "Other"), so themes can rely on category always being a real string.

New config field

wiki_default_category = "Other"  # default

Sets the display name for wiki pages without an explicit category. Surfaces both as the page’s own category value and as the heading of the catch-all group on the wiki index. Set this if you’d prefer “Misc” or “Uncategorised” or your own term.

Dev-server quality of life

Two small wins for aphid serve:

Favicon edits hot-reload. Saving a new favicon.png while the server is running now triggers a rebuild that detects the change and re-encodes the icon set. Editing markdown stays as fast as ever — the favicon is only re-encoded when its source actually changed. (Previously, favicon source changes were ignored until you restarted the server.)

Config edits trigger rebuilds. aphid.toml is now watched alongside the content directories, so editing the title, swapping the favicon path, changing posts_per_page or wiki_default_category all trigger a rebuild without a restart. The exception is source_dir / static_dir / theme_dir — changing those still needs a restart, because those are the directories the watcher itself is registered on.

Author profile images

Authors configured in aphid.toml now support an optional image field:

[[authors]]
name = "Alice"
email = "alice@example.com"
image = "authors/alice.jpg"

The image path is relative to your static/ directory (served at /static/); absolute URLs (https://…) also work.

In blog post templates, author is now a struct with .name, .email, and .image instead of a plain string. The bundled themes render a round profile picture next to the author name — when no image is configured, a gray silhouette SVG is shown as a fallback. If the author has an email, the avatar and name are wrapped in a mailto: link.

Breaking: templates that used {{ author }} must update to {{ author.name }}.

Upgrading

cargo install aphid --locked --force
aphid --version

If you use only the bundled themes, that’s it. If you have a custom theme, walk through the Theme changes section above.