Tooling
One binary, and everything below is a subcommand or a flag of it.
{
gap: 110; clearance: 22;
|box| { fill: --purple-wash; stroke: --purple-deep; color: --purple-ink; }
|out::box| { fill: --rose-wash; stroke: --rose-deep; color: --rose-ink; }
|-| { font-size: 11; stroke: --gray-deep; }
.round { stroke: --teal-deep; color: --teal-ink; }
}
|box#src| "figure.lini" [
|icon| "file-text" { fill: none; stroke: --purple-deep; width: 20; height: 20; }
]
|box#cli| "lini" { font-weight: bold; font-size: 16; }
|column#outs| { gap: 30; } [
|out#svg| "SVG"
|out#html| "HTML"
]
src -> cli "compile"
cli -> outs.svg & outs.html
cli ~> src "fmt · desugar" .round
Text in, a figure out — and fmt and desugar are the arrow back, rewriting
the text you started from.
The loop
lini figure.lini -o figure.svg # compile
lini serve figure.lini # live preview at localhost:7700
lini serve samples/ # a folder: pick, edit and render in the browser
lini --check --strict figure.lini # the full compile, nothing written; warnings fail
echo "a -> b -> c" | lini - # stdin to stdout
serve does two different things, depending on what you point it at:
- A file — live-reloads it on every save.
- A directory — opens the playground, the same editor as lini.rs/play: source left, figure right.
--check runs everything a compile runs and writes nothing. With --strict,
which turns every warning into a failure, that is the gate to put in CI.
Errors that say what to do
figure.lini:12:9: error: link endpoint 'kitchen.bowl' not found at scene root;
did you mean 'kitchen.counter.bowl'?
Every diagnostic is file:line:col, carries a stable code, and suggests a fix
— an unknown property names the nearest one, an unknown endpoint the full path
that does exist.
--json emits the same diagnostics as a document with spans and
machine-applicable edits, which is what an editor integration reads.
Nothing is dropped silently. A property that cannot apply where it is written is an error, not a no-op — so a figure never quietly ignores you.
Export
lini --static figure.lini -o figure.svg # self-contained: no var(), text as paths
lini --embed-font figure.lini -o figure.svg # @font-face inlined, browser-only
lini --theme dark figure.lini -o dark.svg # one palette pinned
lini --format html figure.lini -o figure.html
By default an SVG carries font names and live CSS variables — the embeddable form, which a host page can re-theme. Two flags trade that away for portability:
--staticinlines every variable and outlines the text to paths, so the file renders identically in resvg, librsvg, an email client or a PDF pipeline. Every figure in this book is built this way.--embed-fontkeeps text as text and inlines the used weights. Browsers only — resvg and librsvg ignore@font-face.
Format and desugar
lini fmt figure.lini # canonical style, in place
lini fmt --check figure.lini # exit 1 if it would change anything
lini desugar figure.lini # the file with every template and shorthand expanded
fmt is the one style, and it is not configurable: two-space indent,
declarations grouped on a line, children one per line, table cells padded into
columns.
desugar prints the engine’s true input — a |table| as its grid of
|cell|s, a tree’s generated branch links, a mindmap’s palette rules. When
the sugar surprises you, the expansion is where the answer is.
Highlighting and books
lini highlight figure.lini # the source as <span class="lini-tok-…"> HTML
lini highlight --css # the palette those spans wear
One scanner colours every listing — this book, the site, the playground, the
VS Code and Zed extensions — from the same word lists the compiler reads.
mdbook-lini is the preprocessor
behind these pages: ```lini fences compile to inline SVG at build time,
with the source a click away.
```lini
|chart| "Signups" { categories: "Jan", "Feb", "Mar"; } [
|line| { data: 14, 19, 26; curve: smooth; marker: dot; }
]
```
The same fence, and the same words after it, work in an Astro site: astro-lini is the integration, and both ride on lini-wasm — the compiler as an npm package, which is what to reach for to build a third.
For agents
Anything that writes text can write a figure. The repository ships
SKILL.md, a
self-contained guide an agent can be handed, and
schema/lini.schema.json
— every type and property with its value shape, generated from the ledger the
compiler reads.
Go deeper
The CLI lists every flag, SVG output the structure and class hooks a host can style, and errors the diagnostic catalogue.