# v1.2.3 — multi-format export · similar-paragraph mode · writing goals · Bund polish

Mid-cycle release on the `1.2.3` branch. Three broad themes:

1. **Multi-format export** — markdown / TeX / EPUB in addition
   to Typst / PDF, generated in-process. No `pandoc`, no
   `tex live` install needed.
2. **Writing-progress subsystem** — append-only event log,
   configurable goals (daily words, streak, per-book deadlines,
   status-ladder targets), status-bar widget, Ctrl+V G modal.
3. **Bund + editor polish** — the floating output pane, the
   `Ctrl+Z ?` script picker, the `ink.input` prompt modal, a
   second editor for similar-paragraph comparison, dynamic
   Quick Help.

## Headline changes

### Multi-format export

CLI:

```sh
inkhaven export markdown -o draft.md
inkhaven export tex      -o draft.tex
inkhaven export epub     -o draft.epub
```

- **markdown** — in-process Typst → markdown converter
  (`src/export/markdown.rs`). Headings, lists, emphasis,
  `#image(…)` with caption. Unknown directives are kept
  verbatim inside inline-code spans.
- **tex** — via the new [`tylax`](https://crates.io/crates/tylax)
  dependency (pure Rust, Apache-2.0). Adds a minimal
  `\documentclass{book}` preamble if tylax didn't emit one.
- **epub** — single-chapter EPUB3 zip built with the existing
  `zip` + `pulldown-cmark` deps. mimetype stored uncompressed
  (readers reject the archive otherwise).

**`--book-name` flag** — required when a project hosts more
than one user book. Matches case-insensitively against title,
falls back to slug. System books (Help / Scripts / Typst /
Prompts / Places / Characters / Notes / Artefacts / Research)
are excluded from every export path — they're inkhaven
internals, not manuscript content.

**Ctrl+B O extras** — new `output.extra_formats: []` HJSON knob.
Listed formats land next to the PDF with the same stem; errors
logged but never abort the take.

**Ctrl+V markdown extraction** — `Ctrl+V 1` (editor: buffer),
`Ctrl+V 2` (editor: containing subchapter subtree), `Ctrl+V 1`
(tree: cursor's node + descendants). All write to launch cwd
as `<slug>-YYYYDDMM-HHMM.md`.

Tutorial: [`15-multi-format-export.md`](../Tutorials/15-multi-format-export.md).

### Writing-progress tracking + goals

Self-contained subsystem at `src/progress/` with its own DuckDB
file at `<project>/progress.db`.

- **Event log** — `writing_events` (every save / status promotion
  carries `word_delta` + `total_words` + book id) +
  `writing_baselines` (per-day morning snapshot per user book +
  project-wide).
- **`today_words` semantics** — current total − today's baseline
  (diff vs morning, not sum-of-deltas). Honest about
  delete-then-rewrite.
- **Streak with grace** — `streak_grace_per_week` allows N
  missed days inside the rolling 7-day window before the streak
  breaks.
- **Per-book targets** — `target_words` + `deadline` (ISO date);
  required pace falls out of arithmetic. Past-due deadlines
  collapse to "the remaining gap, all at once".
- **Status-ladder targets** — trailing-7-days promotion counts
  against per-status goals (`ready: 1`, `final: 3`, …).
- **HJSON** — new `goals:` stanza in `inkhaven.hjson`.

Status-bar widget on the right edge of the status row:
```
today 1,247/1500w · streak 3d · story 12,300/80,000w (pace 165w/d)
```

**Ctrl+V G** opens `Modal::Progress` — two-column layout with
today / streak / per-book / status ladder on the left and a
30-day `ratatui::Sparkline` on the right. `r` refreshes.

`ratatui-plt` was scoped — and dropped — because it's GPL-3.0
and would force inkhaven (Apache-2.0/MIT) to GPL. The built-in
ratatui charting widgets are sufficient for v1.

Tutorial: [`17-writing-goals.md`](../Tutorials/17-writing-goals.md).

### Similar-paragraph mode

`Ctrl+V S` saves the current buffer, runs a vector-similarity
search against every other paragraph (current id filtered, only
paragraphs surfaced), opens a picker. Pick a hit and it loads
into a new `secondary` slot — the AI pane is repurposed as a
second editor.

- **Side-by-side editing** — both editors live; `Tab` swaps
  keyboard focus. Implementation is a `mem::swap` wrapper around
  `handle_editor_key` + `save_current` so all existing editor
  handlers keep working unchanged.
- **Slug-path footer** in each pane in similar mode.
- **Ctrl+V S again** saves both buffers and drops the secondary,
  AI pane returns.

Tutorial: [`16-similar-paragraphs.md`](../Tutorials/16-similar-paragraphs.md).

### Bund: floating output pane

Multi-line script output stops clobbering the status bar.

```bund
"Report" ink.pane.show
"Header"           println
"Row 1: foo"       println
"Row 2: bar"       println
```

While the pane is open `print` / `println` reroute there.
`Ctrl+C` clears, `Esc` closes.

New words: `ink.pane.show`, `ink.pane.close`, `ink.pane.clear`,
`ink.pane.line` — all `editor_read` category (non-destructive UI).

### Bund: Ctrl+Z ? script picker

Discovery chord. Lists `Script` nodes under the cursor's nearest
enclosing branch (Book / Chapter / Subchapter); `A` toggles to
the `Scripts` system book; `Enter` evaluates the chosen script
through the Adam VM. Output flows into the floating pane when
open, otherwise the status bar.

Falls back to the `Scripts` book automatically when the branch
scope is empty — one less keystroke for the most common case.

### Bund: ink.input prompt modal

```bund
:hook.greet ( name -- )
  "Hello, " swap concat println
;

"What's your name?" "greet" ink.input
```

Modal opens, user types, `Enter` pushes the string and fires
the named hook. `Esc` cancels without firing.

Hook-driven rather than synchronous so autosave / inference
polling / Typst build timers keep ticking while the prompt is
open.

### Dynamic Quick Help (Ctrl+B H)

The "Meta chords" and "Bund chords" sections of the
quick-reference panel are now synthesised from
`KeyBindings::active()` instead of hardcoded `&'static str`
tables. User overrides via `keys.bindings` and runtime
`ink.key.*` mutations flow into Ctrl+B H without a rebuild.

Each row uses the new `Action::description()` — a
user-friendly sentence ("Take the book — build then copy the
PDF and any configured extras into the launch cwd.") rather
than the terse status-bar `label()`.

User-bound Bund lambdas surface as
`User-bound Bund lambda <name> (registered via ink.key.bind_lambda).`

## Other changes

- Bund stack helpers now lowercase status strings consistently
  in `record_status_change`.
- `Action::BundOpenScriptPicker` variant added to `keybind::Action`
  enum; default binding is `bund_sub` `?`.
- Modal renderer dispatch table gains `BundPane`, `ScriptPicker`,
  `SimilarPicker`, `Progress`, `BundInput` arms.
- `Action::description()` shipped for every action including the
  `BundLambda(name)` runtime-only variant.

## Migration notes

- **HJSON additions** — `output: {}` and `goals: {}` are both
  `#[serde(default)]`, so 1.2.2 configs continue to work.
- **`progress.db`** is created on first launch under the project
  root. Delete it to reset writing-progress history; the next
  launch recreates an empty store.
- **Default export behaviour changed**: `inkhaven export <fmt>`
  (no `--book-name`) previously concatenated paragraphs from
  every top-level book including system books. In 1.2.3 it
  scopes to user books only; with multiple user books it
  requires `--book-name`. This is a behaviour fix — system-book
  content (Help reference, Typst reference, prompts) was almost
  certainly never wanted in your exported manuscript.
- **`tylax 0.3`** added as a dependency, `default-features = false`
  to drop the bundled CLI. Adds ~2 MB to the release binary.

## Files added

- `src/export/{mod,markdown,tex,epub}.rs` — 4 new files, ~800 LOC
  total.
- `src/progress/{mod,store,aggregates,word_count}.rs` — 4 new
  files, ~700 LOC total.
- `Documentation/Tutorials/{15,16,17,18}-*.md` — 4 new tutorials.

## Stats

- 158 tests pass (10 new across progress + markdown).
- One license-driven dep dropped (`ratatui-plt` GPL-3.0).
- Approximate diffstat: +3,500 LOC across src + Documentation.
