Pamphlet
Render djot documents with custom elements and url schemes.
Rendering links, images, raw HTML, and symbols often need knowledge the document does not have: asset fingerprints, CDN hosts, valid page routes, async manifests, or build-time reports. Pamphlet exposes those decisions as continuations.
Targets
Pamphlet supports rendering back to djot/markdown, useful for llm.txt and .md pages and lustre for web pages and web apps.
Front matter
Defines pages with frontmatter deliminated by ---.
Currently parses frontmatter to string key and value list.
Rendering
All these examples use symbol but the renderer also provides hooks for urls and raw blocks. Building on continuations we can make custom renderers with their own control logic.
Pure renderer
This renderer always succeeds and transforms thumbs down to thumbs up.
import pamphlet
import pamphlet/djot
pub fn cheerful_symbol_render(text: String) -> String {
let renderer =
djot.Renderer(..djot.default(), resolve_symbol: fn(name) {
fn(callback) {
case name {
"-1" -> ":+1:"
name -> ":" <> name <> ":"
}
|> callback
}
})
let #(_frontmatter, doc) = pamphlet.parse(text)
djot.to_markup(doc, renderer)(fn(x) { x })
}
pub fn cheerful_render_test() {
let out =
":-1: and :+1:"
|> cheerful_symbol_render()
assert ":+1: and :+1:" == out
}
Aborting renderer
This renderer will halt if anyone uses the turd symbol.
import pamphlet
import pamphlet/djot
pub fn check_turd_symbol_render(text: String) -> Result(String, Nil) {
let renderer =
djot.Renderer(..djot.default(), resolve_symbol: fn(name) {
fn(callback) {
case name {
"turd" -> Error(Nil)
name -> callback(":" <> name <> ":")
}
}
})
let #(_frontmatter, doc) = pamphlet.parse(text)
djot.to_markup(doc, renderer)(Ok)
}
pub fn check_render_test() {
let out =
":-1: and :+1:"
|> check_turd_symbol_render()
assert Ok(":-1: and :+1:") == out
let out =
":turd: and :+1:"
|> check_turd_symbol_render()
assert Error(Nil) == out
}
Stateful renderer
This renderer collects the set of all symbols used.
import gleam/set
import pamphlet
import pamphlet/djot
pub fn list_symbols_render(text) {
let renderer =
djot.Renderer(..djot.default(), resolve_symbol: fn(name) {
fn(callback) {
let #(page, acc) = callback(":" <> name <> ":")
#(page, set.insert(acc, name))
}
})
let #(_frontmatter, doc) = pamphlet.parse(text)
djot.to_markup(doc, renderer)(fn(page) { #(page, set.new()) })
}
pub fn list_symbols_render_test() {
let #(page, symbols) =
":-1: and :+1:"
|> list_symbols_render()
assert ":-1: and :+1:" == page
assert set.from_list(["-1", "+1"]) == symbols
}
Development
gleam run # Run the project
gleam test # Run the tests