e951d57d45
The beat generator had one brief, which assumed the proposal was a prose premise and asked for a whole three-act sheet at "roughly four to eight beats per act". Handed a proposal that already had acts beaten out past that, it would regenerate them — compressing the author's own work into a shorter paraphrase, losing the specific detail that made it worth keeping, and burying the one act they actually wanted among two they did not. `mistral::analyse` now reads a proposal's act headings and counts the numbered beats under each, in whatever notation the author used. An act with three or more beats is drafted; fewer, or an act never mentioned, is still to write. Two stray numbered lines are a note to self, not an act. When some acts are drafted and some are not, a second briefing is sent. That briefing asks for the missing acts only, and asks for them under the author's own heading text and depth, so what comes back drops into their document. It drops the per-act cap in favour of matching the density of the drafted acts, and states which acts are context and which to write. The drafted acts are canon: not to be rewritten, reordered, summarised, condensed or reproduced, and — since a reveal often lands in a later act — the model is told to work backwards from those reveals and plant what they need using setups already placed. The model is asked not to reproduce the drafted acts at all, rather than to echo them unchanged. Reproducing twenty-odd beats verbatim is exactly the drift being avoided; not asking makes it impossible. Because the output is then partial, it is labelled: the status line names the acts written, the file is saved as `<proposal> — beats Two.md`, and its title says what it holds, so a partial sheet is not later mistaken for a whole one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ZGoPiDuZ7vmryNCJWjYSD
482 lines
23 KiB
Markdown
482 lines
23 KiB
Markdown
# md-manuscript
|
||
|
||
A small desktop application for Debian that treats a directory of markdown files
|
||
as an ordered manuscript.
|
||
|
||
* **Left pane** — every `*.md` file in the workspace, as a collapsible folder
|
||
tree, in a manual order you set by **dragging the `⠿` handle** up and down.
|
||
Drag a file onto a folder to move it there. **Hover a file** to see a tooltip
|
||
built from its header — the `POV:` line and the `# Slug:` synopsis — a quick
|
||
at-a-glance summary of each chapter.
|
||
* **Right pane** — a markdown text editor for the selected file (optional live
|
||
preview via the *Preview* checkbox).
|
||
* **Order + contents are saved and version-controlled with git.** The order lives
|
||
in `order.json` inside the workspace, so it syncs along with the files. It
|
||
records each file's path, so subfolders travel with it.
|
||
* **Export** the whole manuscript as a single concatenated `.odt` file, with a
|
||
chapter heading before each file's content.
|
||
* **Start a new project** from a cookiecutter template (**File ▸ New project…**),
|
||
which builds the whole folder structure and opens its drafting folder.
|
||
|
||
Built in Rust with [`egui`](https://github.com/emilk/egui)/`eframe`. The ODT
|
||
writer is native (no `pandoc`/LibreOffice needed at runtime).
|
||
|
||
## Build
|
||
|
||
```sh
|
||
cargo build --release
|
||
# binary: target/release/md-manuscript
|
||
```
|
||
|
||
Requires a Rust toolchain. The GUI needs a normal desktop session (X11 or
|
||
Wayland) with OpenGL — the usual runtime libraries on Debian:
|
||
|
||
```sh
|
||
sudo apt install libgl1 libxkbcommon0 libwayland-client0 libx11-6
|
||
```
|
||
|
||
(Only needed to *run* the GUI; building does not require them.)
|
||
|
||
## Install
|
||
|
||
```sh
|
||
install -Dm755 target/release/md-manuscript ~/.local/bin/md-manuscript
|
||
```
|
||
|
||
Optionally add a desktop launcher at
|
||
`~/.local/share/applications/md-manuscript.desktop`:
|
||
|
||
```ini
|
||
[Desktop Entry]
|
||
Type=Application
|
||
Name=md-manuscript
|
||
Exec=md-manuscript
|
||
Categories=Office;TextEditor;
|
||
Terminal=false
|
||
```
|
||
|
||
## Use
|
||
|
||
1. Set the **Workspace** path in the top bar (default: `~/Manuscript`) — type it,
|
||
or click **📂** to pick a folder in a native file browser — and press **Open**.
|
||
The directory is created if it does not exist.
|
||
2. Press **Init git** once to make the workspace a git repository. To sync with
|
||
another machine, add a remote yourself:
|
||
```sh
|
||
cd ~/Manuscript
|
||
git remote add origin <url>
|
||
git push -u origin main
|
||
```
|
||
After that, the **⟳ Sync (git)** button commits all changes, `pull --rebase`s,
|
||
and pushes.
|
||
|
||
If you open a folder that is *not* itself a repository but sits inside one
|
||
(a parent directory is a git work tree), the app asks whether to **use the
|
||
enclosing repository**. Accept it to have **⟳ Sync** commit and push to that
|
||
repo; decline to leave the workspace on its own, where **Init git** creates a
|
||
separate repository in the folder.
|
||
3. Create files with **+ New** (type a name first — give it a path like
|
||
`part-1/ch-01` to nest it, see [Organising with folders](#organising-with-folders))
|
||
or **+ New from template**
|
||
(no name needed — see [New files from a template](#new-files-from-a-template)),
|
||
edit on the right, `Ctrl+S` (or the Save button)
|
||
to write to disk. Drag the `⠿` handles to reorder or to move between folders. Use the **Zoom** slider
|
||
above the editor to scale the editor text (0% = default; **Reset** returns to
|
||
it), and the **Contrast** slider to brighten dim editor text (0% = theme
|
||
default; higher pushes the text toward white in dark mode / black in light
|
||
mode). Text always wraps to the pane width, so there is no horizontal
|
||
scrolling. Inline markdown has keyboard shortcuts, applied to the current
|
||
selection (press again on wrapped text to remove the markers):
|
||
|
||
| Shortcut | Effect |
|
||
| --- | --- |
|
||
| `Ctrl/Cmd + B` | **bold** (`**…**`) |
|
||
| `Ctrl/Cmd + I` | *italic* (`*…*`) |
|
||
| `Ctrl/Cmd + E` | `inline code` |
|
||
| `Ctrl/Cmd + Shift + X` | ~~strikethrough~~ (`~~…~~`) |
|
||
| `Ctrl/Cmd + K` | link — `[selection](url)`, with `url` selected to replace |
|
||
|
||
Press **`Ctrl/Cmd + F`** (or **Edit ▸ Find / Replace…**) to open the
|
||
find/replace bar above the editor; **`Ctrl/Cmd + H`** opens it with the
|
||
replace field focused. Type a search term to shade every match in the editor
|
||
(the current one brighter); **Enter** / **Shift+Enter** (or **▼** / **▲**)
|
||
step through them, wrapping around, and scroll the match into view. **Aa**
|
||
toggles case sensitivity. **Replace** swaps the current match and advances;
|
||
**Replace all** swaps every match in the file at once. **Esc** closes the bar.
|
||
4. Set the **Chapter title** for the open file if you want to override the
|
||
auto-derived one (the field shows the automatic title as a hint). Leaving it
|
||
blank falls back to a `# Title:` line in the header, and if there is none, to
|
||
the chapter's **position in the left pane** followed by a period (`1.`, `2.`,
|
||
…). Tick **Zero-pad #** in the top bar to pad those numbered titles with
|
||
leading zeros to a uniform width (e.g. `03.` when there are 12 chapters).
|
||
Titles are saved to `titles.json` and sync with git.
|
||
5. Set the **Export** path (type it, or click **📂** to choose the `.odt` file in a
|
||
native save dialog) and press **Export ODT** to produce the concatenated
|
||
document. Each file becomes a chapter headed by its title, optionally followed
|
||
by a caption from its `# Slug:` line. **Every chapter starts on a new page**
|
||
(the first chapter excepted, so there is no blank leading page).
|
||
|
||
The status bar shows a live **word count** for the current file, plus the net
|
||
words added (or removed) to it since the current session started. The count
|
||
updates as you type, including unsaved edits.
|
||
|
||
### Per-file word-count targets
|
||
|
||
Give a file a target by adding a **`Word Count Target:`** line to its header (at
|
||
any heading level):
|
||
|
||
```markdown
|
||
## Word Count Target: 1500 - 2000
|
||
```
|
||
|
||
A **progress bar** then appears in two places, comparing the file's **prose**
|
||
word count (the body below the draft marker, so header metadata isn't counted)
|
||
against the target: **amber** while under the target, **green** once inside the
|
||
range, **blue** when over it.
|
||
|
||
* A **compact bar beside the file name** in the left pane, so you can scan every
|
||
chapter's progress at a glance (hover it for the exact `words / target`). It
|
||
updates live as you type in the open file, and reflects the last saved state
|
||
for the others.
|
||
* A larger, labelled bar in the **status bar** for the file you're currently
|
||
editing.
|
||
|
||
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.
|
||
|
||
## Organising with folders
|
||
|
||
The file list is a tree. Any `*.md` file at or below the workspace is part of the
|
||
manuscript, so you can group a long book into `part-1/`, `part-2/`, or go further
|
||
and give a chapter its own folder of scenes — nesting goes eight levels deep.
|
||
|
||
* **Make a folder** by giving a new file a path: type `part-1/ch-01` beside
|
||
**+ New** and both the folder and the file appear. There are no standalone
|
||
empty folders — a folder exists for as long as it holds a file, and is cleaned
|
||
up automatically once its last file leaves.
|
||
* **Collapse or expand** a folder by clicking its header. The number beside the
|
||
name counts every file beneath it, at any depth.
|
||
* **Move a file** by dragging its `⠿` handle onto a folder header, or in among
|
||
another folder's files. The file really moves on disk, taking its chapter-title
|
||
override with it. Drop it on **↥ drop here for the top level** to bring it back
|
||
out of every folder.
|
||
* **Move by typing** instead, if you prefer: the **Rename** box holds the file's
|
||
whole path within the workspace, so editing the folder part of it moves the
|
||
file just as dragging does.
|
||
* **+ New from template** puts its `untitled-N.md` in the folder of the file you
|
||
are editing, so working inside a part doesn't scatter new files at the top.
|
||
|
||
Folders are skipped when they cannot hold manuscript prose: anything starting
|
||
with a `.` (so `.git` is never walked), plus `target/` and `node_modules/`.
|
||
|
||
### Folders and the manuscript order
|
||
|
||
Order is still yours to set by dragging, and still lives in `order.json` — as
|
||
paths (`part-1/ch-03.md`) rather than bare names. The one rule the app enforces
|
||
is that **a folder's files stay together**: a folder sits wherever its
|
||
earliest-ordered file puts it, and everything under it follows in a block.
|
||
|
||
That is what keeps the panel honest — **the export is the tree read top to
|
||
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/`.
|
||
|
||
## New projects from a cookiecutter template
|
||
|
||
**File ▸ ✨ New project…** scaffolds a whole manuscript project from a
|
||
[cookiecutter](https://cookiecutter.readthedocs.io/) template and then opens its
|
||
drafting folder as the workspace, so you go from nothing to editing chapters in
|
||
one step.
|
||
|
||
The dialog asks for a **name**, **author**, **description** and the **folder to
|
||
create it in** (prefilled with wherever you put the last one). Those are passed
|
||
to the template as the `project_name`, `author` and `description` variables.
|
||
Nothing is written until you press **Create**; generation runs in the background,
|
||
so a template whose hooks talk to the network doesn't freeze the editor.
|
||
|
||
When it finishes, the app opens the project's **`06-First Draft`** subfolder as
|
||
the workspace — that is where the snowflake template keeps the chapter files, in
|
||
`Act 1/`, `Act 2/` and `Act 3/` subfolders that the
|
||
[folder tree](#organising-with-folders) shows directly. If the template didn't
|
||
produce that folder, the project root is opened instead and the status bar says
|
||
so.
|
||
|
||
A folder only appears in the file panel once it holds a `.md` file — the
|
||
template's empty acts are held open with `.gitkeep`, which is not markdown, so
|
||
they show up as you add scenes to them.
|
||
|
||
### Settings ▸ New project…
|
||
|
||
| Setting | Meaning |
|
||
|---|---|
|
||
| **Template** | the folder holding the template's `cookiecutter.json`. Defaults to `~/Documents/Cookiecutters/snowflake`; the dialog confirms whether a `cookiecutter.json` is actually there |
|
||
| **Open subfolder** | which folder of the generated project becomes the workspace (default `06-First Draft`; blank opens the project root) |
|
||
| **cookiecutter path** | leave blank to search `PATH` and then the usual per-user Python prefixes (`~/.anaconda3/bin`, `~/miniconda3/bin`, `~/.local/bin`, …), which is what a desktop launcher needs since it does not inherit a conda `PATH`. The dialog shows which executable was found |
|
||
| **Run the template's hooks** | whether to run the template's pre/post-generation scripts (on by default) |
|
||
| **Gitea** | server URL, user and token, handed to the hooks as `GITEA_URL` / `GITEA_USER` / `GITEA_TOKEN` |
|
||
|
||
Cookiecutter is a Python program and is **not bundled** — install it with
|
||
`pip install cookiecutter` or `conda install cookiecutter` if the settings window
|
||
reports it missing.
|
||
|
||
### Hooks and publishing
|
||
|
||
The snowflake template's `hooks/post_gen_project.py` creates a repository on a
|
||
Gitea server and pushes the new project to it. It only does that when all three
|
||
Gitea settings are filled in; with any of them blank it prints a note and skips,
|
||
and **the project is still generated either way**. The token is passed to the
|
||
push command alone and is not written into the project's `.git/config`.
|
||
|
||
The token is stored in `config.json` in plain text, the same as the Mistral API
|
||
key.
|
||
|
||
## New files from a template
|
||
|
||
The file list has two create buttons:
|
||
|
||
| Button | Name | Contents |
|
||
|---|---|---|
|
||
| **+ New** | the name you type beside it, optionally with folders | `# <name>` and a blank line |
|
||
| **+ New from template** | auto-assigned `untitled-N.md` | the configured template |
|
||
|
||
**+ New from template** takes no typed name — it picks the first free
|
||
`untitled-N.md` (reusing a gap if you have deleted one) in the folder of the file
|
||
you are currently editing, seeds it from the template, adds it to the manuscript
|
||
order and selects it. Rename it later with
|
||
the **Rename** box, or set a `# Title:` header and let the chapter title come
|
||
from that.
|
||
|
||
Edit the template under **Settings ▸ New-file template…**. The built-in default is:
|
||
|
||
```
|
||
# Title: {{name}}
|
||
# Slug:
|
||
# POV:
|
||
# Word Count Target:
|
||
|
||
### Rough Draft:
|
||
|
||
```
|
||
|
||
Three placeholders are expanded when the file is created:
|
||
|
||
| Placeholder | Expands to |
|
||
|---|---|
|
||
| `{{name}}` | the file stem, e.g. `untitled-3` |
|
||
| `{{marker}}` | the draft marker set in the top bar (default `### Rough Draft:`) |
|
||
| `{{date}}` | today's date in UTC, as `YYYY-MM-DD` |
|
||
|
||
Anything else in double braces is left alone. Using `{{marker}}` rather than
|
||
typing the marker literally keeps the template working if you change the marker
|
||
later. Emptying the box restores the built-in default, and **Reset to default**
|
||
does the same in one click.
|
||
|
||
The template is stored in `~/.config/md-manuscript/config.json`, so it is shared
|
||
by every workspace on the machine rather than committed with a manuscript.
|
||
|
||
## Spelling (offline)
|
||
|
||
Spelling is checked **live as you type**, entirely offline — no server, no
|
||
network. Misspelled words get a red underline; **right-click** one for a menu of
|
||
suggested corrections (click a suggestion to apply it). It uses Hunspell
|
||
dictionaries read by the pure-Rust [`spellbook`](https://crates.io/crates/spellbook)
|
||
crate, so the binary stays self-contained.
|
||
|
||
* **Two dictionaries are built in** — Canadian English (`en-CA`, the default)
|
||
and British English (`en-GB`). Pick one from the **Dictionary** dropdown on
|
||
the *Spelling* row of the top bar; the choice is remembered.
|
||
* **More languages** are discovered automatically from the usual Hunspell
|
||
folders (`/usr/share/hunspell`, `/usr/share/myspell`, …) and from a per-user
|
||
folder, `~/.config/md-manuscript/dictionaries/`. Drop a matching
|
||
`xx_YY.aff` + `xx_YY.dic` pair in there (e.g. from your distro's
|
||
`hunspell-de-de` package) and it appears in the dropdown.
|
||
* **Untick “Check as I type”** on the Spelling row to turn the underlines off.
|
||
* Code spans, fenced/indented code blocks and link targets are skipped, and
|
||
ALL-CAPS initialisms (ODT, HTTP) are left alone, to cut false positives.
|
||
|
||
When you run a **LanguageTool** check (below), its richer spelling-and-grammar
|
||
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 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. Anything the file states is read and honoured — the
|
||
**title**, **setting**, **genre**, **tone**, **characters**, **target age**
|
||
(adult, young adult, …), and a **loose plot summary**; the beats follow the
|
||
genre's conventions, keep the tone, and stay age-appropriate. Missing
|
||
attributes are inferred from the rest. 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.
|
||
|
||
### Filling in an act you haven't written
|
||
|
||
If the proposal is **already partly beaten out**, the generator switches to a
|
||
different brief: instead of producing a whole sheet, it writes only the acts you
|
||
are missing and leaves the ones you have written alone.
|
||
|
||
It counts the numbered beats under each act heading it finds (`### Act One:`,
|
||
`## Act 2`, `# Act III — Resolution` — the notation doesn't matter). An act with
|
||
**three or more** beats is treated as drafted; anything fewer, or an act you
|
||
never mention, is treated as still to write. If at least one act is drafted and
|
||
at least one is not, you get the fill-the-gaps brief.
|
||
|
||
So a proposal whose Act One and Act Three are beaten out in detail and whose
|
||
Act Two is a note saying *"this is what I need help with"* sends this:
|
||
|
||
```
|
||
It is already partly beaten out:
|
||
- Act One: 11 beats already drafted — context only
|
||
- Act Two: write this one
|
||
- Act Three: 10 beats already drafted — context only
|
||
|
||
Write only the missing act(s), under exactly these headings:
|
||
### Act Two
|
||
|
||
Do not output the drafted acts.
|
||
```
|
||
|
||
Why it matters: the full-sheet brief tells the model to aim for *four to eight
|
||
beats per act*, which would **compress** an act you had already written to
|
||
eleven. The fill-the-gaps brief drops that cap, tells the model to match the
|
||
density of your drafted acts, and treats them as canon — including any reveal
|
||
that lands in a later act, which it must work backwards from and plant for.
|
||
It also reuses your own heading text and depth, so what comes back drops
|
||
straight into your document.
|
||
|
||
The result is labelled accordingly: the status line says which acts were
|
||
written, the saved file is named `<proposal> — beats Two.md` rather than
|
||
`<proposal> — beats.md`, and its title says which acts it contains — so a
|
||
partial sheet is never mistaken for a whole one.
|
||
|
||
To force a full three-act regeneration anyway, run it against a copy of the
|
||
proposal with the act headings removed.
|
||
|
||
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
|
||
[LanguageTool](https://languagetool.org/) server — typically a **local
|
||
instance**, so your manuscript never leaves your machine.
|
||
|
||
1. Run a LanguageTool server. The standalone server listens on
|
||
`http://localhost:8010` by default (the official Docker image uses `:8081` —
|
||
just point the app at whichever you run):
|
||
```sh
|
||
# Standalone (needs Java):
|
||
java -cp 'languagetool-server.jar' org.languagetool.server.HTTPServer --port 8010
|
||
# or via Docker:
|
||
docker run -d -p 8010:8010 --entrypoint java \
|
||
erikvl87/languagetool -cp languagetool-server.jar \
|
||
org.languagetool.server.HTTPServer --port 8010 --allow-origin '*'
|
||
```
|
||
2. Open **Settings ▸ LanguageTool…** (from the menu bar, or the **⚙** button on
|
||
the Grammar row) and set the connection:
|
||
* **Scheme** — `http` for a local server, `https` for a remote one (TLS is
|
||
supported).
|
||
* **Host / domain** — an IP address or hostname (default `localhost`).
|
||
* **Port** — the server's port (default `8010`).
|
||
* **Token** — optional; sent as an `Authorization: Bearer` header for a server
|
||
behind an auth proxy, or a premium API key. Leave blank for an
|
||
unauthenticated local server.
|
||
* **Language** — `auto` to detect, or a code like `en-US`, `en-GB`, `de-DE`.
|
||
|
||
Press **Test connection** to confirm the settings reach a working server. All
|
||
fields are remembered in the config.
|
||
3. Open a file and press **✓ Check**. The check runs in the background (the UI
|
||
stays responsive) and issues appear two ways:
|
||
* **Underlines in the editor** — red for spelling, blue for grammar/style.
|
||
* **A "Grammar & spelling" panel** at the bottom listing each issue with its
|
||
explanation and suggested fixes. Click a suggestion to apply it; the
|
||
remaining underlines re-align automatically.
|
||
|
||
Editing the text after a check hides the underlines (their positions would no
|
||
longer be accurate) and disables the fix buttons until you re-check. The checker
|
||
sends the raw file text, so occasional markdown tokens (e.g. a `#` heading
|
||
marker) may be flagged.
|
||
|
||
## Header stripping on export
|
||
|
||
Manuscript files often carry editorial notes and metadata above the prose that
|
||
should not appear in the finished document. On export, each file is cleaned up:
|
||
|
||
* **HTML comments** (`<!-- ... -->`, including multi-line ones) are removed.
|
||
* **The header block** — everything above the **Draft marker** line (set in the
|
||
top bar; default `### Rough Draft:`) — is dropped. From that header:
|
||
* `# Title:` becomes the chapter heading (unless a manual override is set), and
|
||
* `# Slug:` becomes an italic caption printed beneath the heading.
|
||
|
||
Any other header text is discarded. Files with no marker line are exported
|
||
whole, but stray `# Title:` / `# Slug:` lines are still lifted out. Clear the
|
||
Draft marker field to disable header splitting entirely.
|
||
|
||
For example, this source file:
|
||
|
||
```markdown
|
||
# Title: The Gate
|
||
# Slug: in which the door will not open
|
||
<!-- remember to foreshadow the key -->
|
||
outline: she arrives, she knocks, nothing
|
||
|
||
### Rough Draft:
|
||
|
||
The prose that actually gets exported begins here.
|
||
```
|
||
|
||
exports as a chapter titled **The Gate**, captioned *in which the door will not
|
||
open*, containing only the prose below the marker.
|
||
|
||
### Field-name autocomplete
|
||
|
||
While typing a header line above the draft marker — `#`, `##`, … followed by the
|
||
start of a field name — a small popup suggests matching field names. Press
|
||
**Tab** or **Enter** (or click) to insert the field and its `": "`, **↑/↓** to
|
||
change the highlighted suggestion, and **Esc** to dismiss. The suggestions
|
||
include common fields (`Title`, `Slug`, `POV`, `Word Count Target`,
|
||
`Characters`, `Setting`, `Conflict`, …) plus any field names it **learns from
|
||
the headers of your other files**, so your own conventions autocomplete too. It
|
||
only triggers in the header region, never in the prose below the marker.
|
||
|
||
## Files the app writes
|
||
|
||
| Location | Purpose |
|
||
|---|---|
|
||
| `<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) |
|
||
| `~/.config/md-manuscript/config.json` | last workspace, export path, prefs, new-file template, project/Gitea settings |
|
||
|
||
## Markdown supported in ODT export
|
||
|
||
Headings, paragraphs, **bold**, *italic*, ***both***, `inline code`, fenced/indented
|
||
code blocks (monospace, whitespace preserved), ~~strikethrough~~, ordered and
|
||
unordered (nested) lists, block quotes, horizontal rules, and links. Images are
|
||
rendered as a text placeholder (not embedded).
|
||
|
||
A quick reference for all of this — plus the chapter metadata, draft-marker
|
||
header, and comment conventions — is built in: open **Help ▸ Markdown cheatsheet**
|
||
from the menu bar.
|