# Inkhaven 1.3.13 — Breadth: every language, cancellable jobs, print & ebook polish

_1.3.8–1.3.12 went **deep** on the world layer (facts, drift, continuity, the
background refresh). 1.3.13 goes **wide**: it makes the AI/NLP features work in
**any** language — not just the five with hand-curated word lists — lets you
**cancel** a long background job mid-flight, and finishes the **print** and
**ebook** export paths. Full plan:
[BREADTH-1_PLAN.md](../PROPOSALS/BREADTH-1_PLAN.md). Zero new dependencies._

## Cancel a deep refresh in flight

The background deep refresh (1.3.12) is a multi-minute AI batch. Until now,
once you pressed `Ctrl+V Shift+F` you waited for the whole thing — four scans —
or quit. Now **re-pressing `Ctrl+V Shift+F` while it runs cancels it**: the
status chip shows `⟳ deep refresh · cancelling…`, each scan checks a shared
cancel flag between chapters/entities and stops at the next boundary, and
whatever finished still wrote its sidecars (the completion reports `cancelled`
rather than `done`). The cancel flag threads all the way down — `facts scan`,
`drift scan`, and `continuity extract` each take a `cancel: &AtomicBool` and
honour it per unit of work — so a cancel lands in seconds, not at the end of
the current scan.

## Full multilingual — it works in your language, or it tells you it doesn't

Inkhaven's style + world tooling has always been keyed to the project
`language`. But "keyed to" used to mean **five** languages with curated word
lists (en/ru/fr/de/es), and an **honesty bug**: an uncurated language silently
fell back to the *English* lists, so a Polish or Japanese project got English
filter-words flagged in its prose. 1.3.13 fixes the honesty and removes the
five-language ceiling.

### Honest fallback — no more English masquerade

The built-in detector lists (filter words, show-don't-tell, repeated-phrase
stop-words, drift pronouns) now return **empty** for a language with no curated
list, never English. An uncurated language gets *no* false flags rather than
wrong-language ones. Stemming (Snowball, 18 languages) and the multilingual
embedding model already worked for far more than five languages; this aligns
the word-list layer with that honesty.

### `inkhaven lang status` — an honest coverage matrix

```
inkhaven lang status [--language <lang>]
```

prints exactly what's automatic, curated, or off for your project (or any
`--language`): stemming, filter words, show-don't-tell, stop-words, drift
pronouns, anachronism lexicon, embeddings, AI world-check **output language**,
and AI world-check **prompts** — each marked `✓` / `built-in N` / `none`. When
nothing is curated, it points you at `lang bootstrap`.

### `inkhaven lang bootstrap <language>` — curate any language with one command

```
inkhaven lang bootstrap polish [--provider <p>] [--yes]
```

asks your LLM (as a careful lexicographer, output forced to the requested
language, lemma forms for Snowball) for the full detector vocabulary —
filter words, the four show-don't-tell categories, stop-words, and the three
pronoun sets — and prints it as a ready-to-paste HJSON snippet. `--yes` patches
`inkhaven.hjson` in place (versioned backup + atomic write + comment
preservation). This is the bridge from "five languages" to "any language":
bootstrap once, verify with `lang status`, tune by hand.

### World-check prompts speak your language

All four world-consistency scans — facts **check** (internal fact contradictions),
facts **scan** (prose-vs-fact), **drift**, **continuity** — now run with system
prompts **localized** to the project language (en/ru/fr/de/es built in), and
force their findings' explanations to be written **in that language**. An
uncurated language falls back to the English prompt **with a warning** (never
silently). Each prompt is overridable through a **3-tier cascade**: a `Prompts`
book entry (`{slug}-{lang}` → `{slug}`) → `prompts.hjson` → the localized
built-in. The fact-check prompt also reasons about world domains (planetary
science, physics, geology, hydrology, climate, ecology), treating your invented
rules as authoritative — so it catches *internal* contradictions ("winters are
mild" ⟷ "the harbor freezes each January"), not just textual ones.

### Per-language config, not five fixed fields

The detector config moved from five fixed per-language fields to **maps** keyed
by language name, so a config can populate *any* language:

```
editor: { style_warnings: {
  filter_words:    { languages: { polish: [ … ] } }
  show_dont_tell:  { languages: { polish: { linking_verbs: […], emotion_adjectives: […], … } } }
  repeated_phrases:{ languages: { polish: [ …stop-words… ] } }
} }
drift: { pronouns: { polish: { character: […], place: […], artefact: […] } } }
```

Two worked examples ship in [`custom_languages/`](../../custom_languages/) —
**Arabic** (RTL, pro-drop, suffix pronouns) and **Hungarian** (agglutinative,
genderless 3rd person) — alongside a README.

## Print: a one-command booklet and a proper A5 codex

The 1.3.0 imposition engine (`inkhaven pdf impose`) already plans
saddle-stitch and perfect-bound signatures from named profiles. 1.3.13 adds the
two recipes home binders reach for most.

### `inkhaven pdf booklet` — zero-config saddle stitch

```
inkhaven pdf booklet book.pdf            # auto-fit the sheet to 2× the page
inkhaven pdf booklet book.pdf --sheet A3 # …or centre the spread on a preset
```

No profile, no config file. The press sheet **auto-fits to two source pages
side-by-side**, so *any* trim size works — not just the preset-bound `chapbook`
profile. All pages nest into one folded signature; blanks balance around the
centre fold. `--creep` adds shingle compensation for thick booklets, `--no-marks`
gives a clean already-trimmed proof, `--dry-run` previews the plan.

### `a5_book` profile — a real A5 book from A4 sheets

```
inkhaven pdf impose book.pdf --config a5_book
```

The popular home-binding recipe: a proper **A5 codex** folded from A4 sheets.
**Perfect-bound** (gathered + glued/sewn signatures) rather than saddle-stitched,
so it scales to a full-length book where the saddle path tops out around 40
pages. Four A4 sheets per signature = 16 A5 pages (the standard home section),
shingle creep keeps the fold square, and the **signature-number mark** lets you
gather sections in order by hand. (Source pages should already be A5 trim —
imposition centres, it doesn't scale.)

## Ebook: inline images and popup footnotes in the EPUB

The EPUB export (`inkhaven epub`) gained the two pieces it was missing.

- **Inline images** — `Image` nodes in a chapter, previously dropped, now embed
  as EPUB3 `<figure>` resources: the bytes are read from the store, written
  verbatim into `OEBPS/` (stored, like the cover), declared in the OPF manifest,
  and referenced with `alt` text (falling back to the node title) and an optional
  `<figcaption>`. A missing-bytes image warns and is skipped rather than failing
  the whole export.
- **Popup footnotes** — footnote placeholders become proper EPUB3
  `<a epub:type="noteref">` / `<aside epub:type="footnote">` pairs with
  back-links, collected into a `<section epub:type="footnotes">`, so a
  conforming reader shows them as popups instead of inline brackets.

## Compatibility

- **No breaking config changes.** The old five fixed per-language fields still
  deserialize; the new `languages` maps are additive. If you relied on an
  uncurated language inheriting the English lists, that (buggy) behaviour is
  gone — set `language: english` explicitly or bootstrap your language.
- **No new dependencies.** Everything here is pure-Rust over the existing
  imposition, EPUB, config, and background-job machinery.

## Under the hood

- The world-check prompts live in `src/cli/world_prompts.rs` (4 scans × 5
  languages + English fallback + the 3-tier resolver).
- `inkhaven lang` is `src/cli/lang.rs`; the bootstrap patch goes through the
  same `apply_in_place_edits` path the config editor uses.
- The booklet command builds `ImpositionParams` directly (no profile); `a5_book`
  is a built-in `ImpositionProfile`.
- EPUB images are an `EpubImage` resource on each `EpubChapter`; footnotes are
  rewritten in `footnotes_to_asides`.

Tests 1359 → 1369. One new tutorial
([73 — writing in any language](../Tutorials/73-multilingual.md)).

The 1.4 headline remains the **Whole-Book AI Editor** (RAG over the whole
manuscript) — 1.3.13 makes sure it will speak every language it's pointed at.
