Add project-mode tooling: characters, outline, diff and revision status

Rounds out project mode, where the workspace is the project root and one
subfolder holds the manuscript proper:

- Characters and Outline windows, backed by new `characters` and `outline`
  modules that read the cast from character sheets and measure how much of
  the snowflake outline is actually written.
- Edit ▸ Changes… diffs the open file against its last committed version.
- Revision status, per-file and project word counts, an archive action and
  hidden folders in the file panel.
- Chapter-file export alongside the ODT master, richer header parsing, and
  a project word list for names and invented terms.
- The export path now follows the workspace: opening a project points it at
  that project root, keeping a file name you chose yourself and re-deriving
  one that merely echoed the folder it sat in.
- Clicking an issue in the grammar/spelling panel takes the editor to it,
  selecting the words and centring them; applying a suggestion jumps to the
  rewritten text as well.

README covers the new windows and workflows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bSn3Xijp8GofZVUnRX4oq
This commit is contained in:
2026-08-24 19:56:36 -05:00
parent e951d57d45
commit 043cc692ac
23 changed files with 3879 additions and 144 deletions
+174
View File
@@ -145,6 +145,50 @@ A single number (`## Word Count Target: 1800`) sets a point goal; ranges accept
`-`, ``, `to`, and grouped digits (`1,500`). Like the other header lines, the
target is stripped from the exported document.
## Project mode
Open a **project root** — a folder holding the manuscript alongside the
characters, outline, premise and the rest — and the app recognises the layout
instead of treating every file in it as a chapter.
The trigger is simple: if the folder you open contains a subfolder named like
the **manuscript folder** (`06-First Draft` by default), it is a project. A
plain folder of chapters has no such subfolder, so it behaves exactly as before
— everything in it is the manuscript.
In a project:
* **Files under the manuscript folder are the book.** They are ordered,
numbered, and they are what **Export ODT** concatenates. Chapter numbering
counts only them, so a defaulted title is `3.` because it is the third
*chapter*, not the third file in the project.
* **Everything else is reference** — character sheets, the outline, the scratch
pad — and is **not listed in the file panel**, which stays about the book.
Reference material is reached through the windows that understand it
([Characters](#characters-tools--characters), [Outline](#outline-tools--outline)),
and it stays out of the order and out of the export.
Turn on **View ▸ Show reference files** to list it in the tree anyway, dimmed,
with the manuscript folder tagged **· manuscript**.
* `order.json`, `titles.json` and `wordlist.json` live at the **project root**,
so one word list covers the whole project rather than one folder of it.
* **File ▸ New project…** opens the project root, not the drafting folder.
Set which folder is the manuscript under **Settings ▸ New project… ▸ Manuscript
folder**. The number is optional — `First Draft` matches `06-First Draft` — and
the setting is global, so every project on this machine uses the same layout.
If your writing actually lives in `05-Plot`, point it there.
### Hidden folders
An archive of superseded drafts can hold hundreds of files that would swamp the
tree, so folders named like **`Archive`** are skipped by the scan entirely — the
same treatment `target/` and `node_modules/` already get. The match ignores a
leading number, so `10-Archive` is covered.
Change the list with `hidden_folders` in
`~/.config/md-manuscript/config.json`; set it to `[]` to see everything.
## Organising with folders
The file list is a tree. Any `*.md` file at or below the workspace is part of the
@@ -182,6 +226,101 @@ bottom**, so the order you see down the left is the order the chapters are
concatenated in, folders and all. Chapter numbering follows the same sequence,
so a defaulted title in `part-2/` continues counting from `part-1/`.
## Working on the novel's other parts
A manuscript project is more than its chapters. These read the rest of it.
### Hiding the header while you write
A scene card's fields sit at the top of the same file as its prose, and once
drafting is under way they are mostly in the way. **View ▸ Hide header in
editor** folds away everything above the draft marker, leaving the prose alone
in the editor. A single line above the editor says so — `▸ header hidden (8
fields)` — and clicking it flips back.
While the header is hidden it is genuinely **not in the editor's text**: the
file is put back together on save, so what lands on disk is unchanged. Doing it
that way rather than merely scrolling past the header means search, replace,
spelling underlines and the caret all address exactly what you can see, with no
offsets to go wrong. Everything that wants the whole file — the chapter title
hint, the word-count target, the file-list tooltip — still reads it whole.
Files with no draft marker have nothing to fold, so the line does not appear.
### Characters (Tools ▸ Characters…)
Reads the project's character sheets — a file whose level-1 heading is the name,
as either `# Bixby Reynolds` or `# Name: Jonathan Bronski`. For each character
it shows:
* their descriptor, from a `Slug:` field or the heading beneath the name;
* **which scenes they appear in**, by matching the `Characters:` line of every
manuscript file. A first name matches a full name, so `Characters: Bixby, Mom`
finds *Bixby Reynolds*. Click a scene to open it;
* **▦ Rebuild grid**, which regenerates the project's `character_grid.md` from
the sheets as a markdown table. A hand-kept grid drifts the moment a sheet
changes; this one cannot. Long values are trimmed, since a grid is a
quick-look summary.
### Outline (Tools ▸ Outline…)
The snowflake outline files are scaffolds of HTML comments, one per beat
(`<!-- Midpoint: A major turning point. -->`). The app strips comments, so
these files otherwise render and export as *blank* — there is no way to see how
much of the outline exists.
This window reads the scaffold back: every beat it names, and whether prose has
been written under it, with a progress bar over the whole outline. A beat counts
as written when there is ordinary text between its prompt and the next one.
Beats generated by [the Mistral tool](#plot-beats-mistral) can be appended
straight into the matching act file with **📝 Append to act_0N.md**, which
appears when a run filled exactly one act. It appends under a dated comment
rather than replacing, because the prompts are the reason the file is worth
keeping.
### Revision status
Give a file a `Status:` header line and the file panel shows a compact badge for
it (initials, so `Structural Edit` becomes `SE`) and offers a **Status** filter
above the list. The filter narrows the tree to one stage — every file you have
still to proofread, say.
### Archive
**🗄 Archive** moves the selected file into the project's archive folder,
keeping its place inside it (`Act 1/scene.md` → `10-Archive/Act 1/scene.md`).
It leaves the manuscript order and, since archive folders are
[hidden](#hidden-folders), the file panel — but stays on disk. Deleting is for
things you want gone; this is for things you have moved past.
### Changes since the last commit (Edit ▸ Changes…)
Diffs the open file against its committed version, coloured the way git colours
a diff. Revising is comparing, and the repository already holds the answer.
### Word counts
The status bar carries the open file's word count, its change this session, and
the **whole manuscript's total**. Reference files are not counted.
### Manuscript details (Settings ▸ Manuscript details…)
Title and author, written into exported `.odt` files as their document
properties — what a word processor shows under File ▸ Properties. A blank title
uses the project folder's name. Exports previously carried no metadata at all.
### Exporting chapter files (File ▸ Export chapters + master)
Writes one `.odt` per chapter into a `chapters/` folder, plus an `.odm` master
document that links them — the shape the template's `Full Text.odm` implies.
**The per-chapter files are the reliable part**: they are ordinary documents and
open anywhere. The master is a shell with a title page and one linked section
per chapter; LibreOffice does not follow those links when it opens the file, so
treat it as a starting point to relink rather than as the assembled book. For a
single finished document, use **Export ODT**.
## New projects from a cookiecutter template
**File ▸ ✨ New project…** scaffolds a whole manuscript project from a
@@ -300,6 +439,37 @@ results take over the underlines until you next edit the text — at which point
the live offline checker resumes. In other words, LanguageTool is used when it's
available and current; the offline checker is the always-on default.
### The word list (names and invented terms)
No Hunspell dictionary knows your characters, so without somewhere to record
them a novel's cast is underlined on every line of every scene. **Settings ▸
Word list…** is that place.
* **Right-click an underlined word** in the editor and choose
**📗 Add “…” to the word list**. The underline goes immediately — there is no
wait for the next check.
* **👤 Add character names** reads the project's character sheets and lists
every name in them at once. It looks for a `Characters` folder (a numeric
prefix like `03-Characters` is fine) beside the workspace and up to three
levels above it, so it still finds them while you are editing in
`06-First Draft`. Both `# Bixby Reynolds` and `# Name: Jonathan Bronski`
headings are understood, and an unfilled template sheet (`# Name`,
`# Protagonist`) contributes nothing. Each part of a name is listed
separately, since prose says *Bixby* far more often than *Bixby Reynolds*.
* Words can also be **typed in** directly, and taken back out with **✖**.
Matching is case-insensitive, and a listed name covers its **possessive and
plural** — `Bixby` accepts `Bixby's`, `Bixbys` and `Bixbys'` — because the
tokenizer hands the checker `Bixby's` as a single word.
The list is saved as **`wordlist.json`** in the workspace, beside `order.json`
and `titles.json`, so it is committed to git and travels with the manuscript. It
is a plain sorted JSON array, and a corrupt or hand-mangled file is treated as
empty rather than stopping the checker.
Note that the list belongs to the *workspace*, not the whole project — open a
different folder of the same project and it has its own list.
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).
@@ -467,8 +637,12 @@ only triggers in the header region, never in the prose below the marker.
| `<workspace>/**/*.md` | your manuscript files, in folders if you like |
| `<workspace>/order.json` | the manual ordering, as paths (committed to git) |
| `<workspace>/titles.json` | chapter-title overrides, keyed by path (committed to git) |
| `<workspace>/wordlist.json` | names the spell checker accepts (committed to git) |
| `~/.config/md-manuscript/config.json` | last workspace, export path, prefs, new-file template, project/Gitea settings |
In [project mode](#project-mode) the workspace is the project root, so those
three bookkeeping files sit there and cover the whole project.
## Markdown supported in ODT export
Headings, paragraphs, **bold**, *italic*, ***both***, `inline code`, fenced/indented