Add Mistral plot-beat generation (Tools menu)

New Tools ▸ "Generate plot beats (Mistral)…" command: pick a novel
proposal markdown file (characters + loose plot summary) and have the
Mistral chat API lay it out as plot beats in the classic three-act
structure. The result opens in a floating window where it can be edited,
saved as a new "<proposal> — beats.md" workspace file, or copied.

- src/mistral.rs: pure-Rust ureq/rustls POST to /v1/chat/completions
  (self-contained; no C bindings). System prompt fixes the three-act
  markdown shape; friendly API-error surfacing. Unit tests for content
  parsing, empty/blank responses, error-message extraction, and the
  missing-key guard.
- config: mistral_api_key / mistral_model / mistral_base_url with
  effective-value helpers and defaults (mistral-large-latest,
  https://api.mistral.ai).
- app: background generation + poll (editor stays responsive), a
  Settings ▸ Mistral… window (key/model/base URL), and the results
  window with save/copy.
- Docs: README "Plot beats (Mistral)" section and help cheatsheet note.

Verified: cargo build --release clean, 56 tests pass (+6 Mistral),
ldd still only libc/libgcc/libm. Clippy: no new warnings in the added
code (the two extra vs. the old 3-warning baseline are toolchain-surfaced
in pre-existing app.rs code).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDfaEsTgcn61n3wgP62DFF
This commit is contained in:
landon
2026-08-20 14:39:08 -05:00
parent 5bc2acad25
commit e5eadc08c1
6 changed files with 635 additions and 0 deletions
+25
View File
@@ -166,6 +166,31 @@ The bundled dictionaries live under `dictionaries/` and are derived from
[SCOWL](http://wordlist.sourceforge.net/) under a permissive license (kept
alongside them in each `license` file).
## Plot beats (Mistral)
**Tools ▸ ✨ Generate plot beats (Mistral)…** turns a rough novel proposal into
a structured set of plot beats laid out in the classic **three-act structure**,
using the [Mistral](https://mistral.ai/) chat API.
1. Open **Settings ▸ Mistral…** once and paste your **API key** (from
`console.mistral.ai`). You can also set the **Model** (default
`mistral-large-latest`) and the **Base URL** (default `https://api.mistral.ai`,
handy if you route through a proxy). The settings are remembered.
2. Choose **Tools ▸ Generate plot beats…** and pick a markdown (or text) file
describing the novel — its **characters** and a **loose plot summary**. The
request runs in the background, so the editor stays responsive.
3. The result opens in a **floating window** with the beats grouped under
*Act I — Setup*, *Act II — Confrontation*, and *Act III — Resolution*, each a
numbered list. You can **edit** the text in place, then:
* **💾 Save as new file** — writes `<proposal> — beats.md` into the workspace
(auto-numbered if that name is taken) and opens it in the editor, or
* **⧉ Copy** — copies the beats to the clipboard.
The chosen proposal text is sent to Mistral to generate the beats; nothing else
in your workspace is transmitted. The API key is stored in the app's config file
in plain text. Because the request uses the pure-Rust `ureq`/rustls stack, the
binary stays self-contained.
## Grammar & spelling (LanguageTool)
The editor can *additionally* check the current file against a