This page is itself a demo of two things at once: a custom slides.lua filter that turns a ::: slides div into a navigable deck, and a tour of what Pandoc Lua filters look like inside Markdown notes. Use the numbered nav above the deck (or the ← / → keys, after clicking inside the deck) to step through.
Why filters?
Pandoc parses your Markdown into a typed AST — paragraphs, headings, links, code blocks. A Lua filter is a function that walks that tree and rewrites it before Emanote renders to HTML.
Anything you can say about a node in Lua, you can transform.
Adding one to your notebook
Drop a .lua file anywhere in your notebook layer (convention: a filters/ folder).
Save. Emanote resolves the path against every -L’d Layer system.
What this page does
This page declares pandoc.filters.render.html: [filters/slides.lua]. Each ## heading inside a ::: slides div becomes one slide; the filter wraps them in <section> elements, prepends a nav strip, and emits CSS + a tiny arrow-key handler.
Edit filters/slides.lua — change a colour, tweak the layout, add a feature — and every note that references it refreshes on the next save. No touch of the .md required, no live-server restart.
The reverse index from filter path → dependent notes is maintained inside Emanote’s model; an edit invalidates exactly the affected notes, and the render-time filter runs again with FORMAT == "html".
A second example: word count
Pure-Lua filters compose cleanly. The main guide page chains list-table.lua with wordcount.lua:
Both are bundled in Emanote’s default layer and run on every save; the order matches the array.
Writer-specific filters
Many Pandoc Lua filters branch on Pandoc’s FORMAT variable to emit HTML or LaTeX. Put Markdown-agnostic AST rewrites under pandoc.filters.parse, and put HTML-specific filters under pandoc.filters.render.html.
This deck uses the render-time slot because slides.lua emits raw HTML, CSS, and JavaScript.
The pattern is always the same: drop the .lua in, name it in frontmatter, save.
This deck is rendered by slides.lua. If you view source, you’ll see it is plain Markdown inside one fenced div — the filter does the HTML-specific structural work at render time.