# Inkhaven 1.4.10 — Jinja template paragraphs

_Some content isn't free prose — it's **structured** and **data-driven**: a character sidebar that
should read the character's actual name and species, an endpoint table built from a list of fields,
a shared admonition written once. STRUCT-1 adds **Jinja template paragraphs**: a paragraph with
`content_type: "jinja"` is a [minijinja](https://docs.rs/minijinja) template the assembler renders
to **Typst** before `typst compile` runs — two layers, strictly sequential, never nested. It's
**self-gating** (no Jinja paragraphs, no change) and adds **one crate** (`minijinja`; its transitive
deps were already in the tree)._

_Plan: [STRUCT-1_PLAN.md](../PROPOSALS/STRUCT-1_PLAN.md)_

## Write a template

Put the tree cursor in one of your books and press **`e`** (mnemonic: t**e**mplate). You get a
`.jinja` paragraph — `⟡` glyph in the tree, `[jinja]` badge in the editor, Jinja syntax
highlighting — seeded with a starter that documents the variables you can use:

```jinja
= {{ title }}

#block[Edit this template. Link HJSON paragraphs with Ctrl+V a to populate `linked`.]
```

`{{ … }}` is an expression, `{% … %}` a statement, `{# … #}` a comment; everything else is plain
Typst, passed straight through.

## Pull in real data

The interesting variable is `linked`. Link an HJSON paragraph to your template with **`Ctrl+V a`** —
say a Characters entry `01-aria` with `{ name: "Aria", species: "fox" }` — and read its fields,
keyed by the linked paragraph's slug:

```jinja
= {{ linked["01-aria"].name }}

#block[A {{ linked["01-aria"].species }}.]
```

At assembly that renders to `= Aria` / `#block[A fox.]`. Change Aria's species in one place and
every template that reads her re-renders. You also get `title`, `slug`, `book.title`,
`chapter.title`, `language`, and `genre`.

## Share fragments via the Snippets book

A `.jinja` paragraph **in the Snippets book** (REUSE-1) becomes an includable template, named by its
tree path — `Snippets/Admonitions/warning` → `snippets/admonitions/warning.jinja`. Include it from
any manuscript template:

```jinja
{% include "snippets/admonitions/warning.jinja" %}
```

inkhaven registers every snippet template **before** rendering anything, so the include always
resolves — even a snippet that includes another snippet.

## Assemble

At Book assembly (**`Ctrl+B A`** / `inkhaven build`) inkhaven renders each Jinja paragraph to a
`.typ` in the assembled tree, then runs `typst compile`. The `{{ … }}` tags are gone by the time
Typst sees anything.

By default a render failure (bad syntax, missing include, typo'd variable) **aborts the whole
assembly** with the offending paragraph + error — CI-safe, no silently-dropped content. Set
`jinja.continue_on_error: true` to write a **visible** Typst error block in place and keep going.

## Convert an existing paragraph

`e` *creates* a fresh seeded template. To *convert* an existing leaf, cycle its type with **`t`** /
**`T`** in the Tree pane — the node-type morph gained a `jinja` rung:
`typst → hjson → jinja → bund`.

## Works in any language

The template receives `language`/`genre`, so you can branch yourself
(`{% if language == "ru" %}Глава{% else %}Chapter{% endif %}`); linked HJSON values (Cyrillic,
accented Latin, …) pass through verbatim.

## Also in this release

- **`#include` validator completeness (REUSE-1 polish).** The save-time snippet validator now also
  catches a **typo'd snippets directory** (`../nippets/<slug>.typ` whose filename matches a known
  snippet) and — once a book is assembled — verifies **generic** Typst `#include` paths resolve
  against the assembled output (not just snippet includes), while keeping the snippet-slug check as
  the pre-assembly path.

## Notes

- **Self-gating, additive:** no Jinja paragraphs → no behaviour change. **No new `NodeKind`, no
  `ChildRef` variant, no DuckDB schema change** — `content_type: "jinja"` on an ordinary paragraph,
  `.jinja` on disk. Jinja paragraphs are skipped by the Inner Editor, Inner Socrates, and the idle
  fact-checker (markup, not prose).
- **One new crate:** `minijinja` (in-memory template registration — no filesystem globs; its
  transitive deps were already present). Built on the existing `.typ` + DuckDB + serde-hjson stack.
- Orthogonal to the text-expansion snippet system (`tui/snippets.rs`): that injects a value as you
  type; Jinja paragraphs generate structured Typst at assembly. New tutorial 93. Tests 1936 → 1949.
