Hovering a file in the left pane now shows its `# Slug:` line as a
tooltip — a one-line synopsis per chapter. Slugs are cached per file
(name -> slug), filled when the workspace opens (snapshot_slugs) and
refreshed on save; the selected file is parsed live from the buffer so
unsaved slug edits show immediately. The cache is kept consistent on
rename and delete.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kRuP7JvXoUGdNNeg5ZSs
Header parsing now recognises a `Word Count Target:` line at any heading
level (## included) in addition to `# Title:`/`# Slug:`, and accepts one
or more leading `#`. The value parses a single number or a range —
"1500 - 2000", "1500-2000", "1,500 to 2,000" — into a min/max WordGoal
(stripped from the exported document like the other header lines).
When a target is set, the status bar shows a progress bar of the file's
prose word count (the body below the draft marker) against it: amber
under the target, green in range, blue over. Aliases "Word Count Goal:"
and "Word Count:" also work.
Adds preprocess tests for the range forms, ## heading level, alias, and
absence; 44 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kRuP7JvXoUGdNNeg5ZSs
Spelling is now checked live as you type, fully offline, using the
pure-Rust `spellbook` crate to read Hunspell .aff/.dic dictionaries (no C
libhunspell — the binary stays self-contained; ldd unchanged).
- Canadian (en-CA, default) and British (en-GB) English dictionaries are
compiled in; more languages are auto-discovered from system Hunspell
folders and ~/.config/md-manuscript/dictionaries/. A "Spelling" row in
the top bar toggles live checking and picks the dictionary.
- Misspellings are underlined in red; right-clicking a word opens a menu
of suggested corrections. A results-panel and one-click fixes work too.
- Checks run on a background thread, debounced ~400ms after the last edit.
Prose is extracted with pulldown-cmark so code spans, code blocks and
link targets are skipped; ALL-CAPS initialisms are ignored.
- LanguageTool still layers on top: when its results are fresh they own
the underlines (spelling + grammar); once you edit, the offline checker
resumes. The editor underlines, issues panel and fix-apply logic are
now shared between the two sources.
Bundled dictionaries are SCOWL-derived under a permissive license (kept
in dictionaries/<lang>/license). Adds spell-check unit tests (tokenizer,
code-block skipping, offset mapping, en-CA/en-GB spelling); 40 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kRuP7JvXoUGdNNeg5ZSs
Ctrl+F opens a find bar above the editor (Ctrl+H focuses the replace
field; Edit ▸ Find / Replace… opens it too). Matches are shaded in the
editor with the active one highlighted more strongly; Enter/Shift+Enter
and ▼/▲ step through them with wraparound and scroll-to-view. An "Aa"
toggle controls case sensitivity. Replace swaps the current match and
advances; Replace all swaps every match in one pass. Esc closes the bar.
Search/replace logic lives in pure, unit-tested functions (find_matches,
replace_all) with non-overlapping matching and correct multibyte offset
mapping for case-insensitive search. Highlighting reuses the editor's
layout-job builder alongside the grammar underlines.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9kRuP7JvXoUGdNNeg5ZSs
Ctrl/Cmd+B/I/E wrap the selection in bold / italic / inline-code markers,
Ctrl/Cmd+Shift+X in strikethrough, and Ctrl/Cmd+K as a link (with the url
placeholder selected). Pressing the same key on already-wrapped text
removes the markers. Applied to the live selection via the TextEdit
cursor range, with the caret/selection restored around the edit.
Documented in the built-in cheatsheet and the README.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the logrs treatment: at startup, pull every widget state's
foreground stroke toward white (dark) / black (light) so all UI text —
menus, top bar, buttons, panels, headings, not just the file list and
editor — reads more clearly against the theme background. Also scale
every base font size up ~12%. Explicitly coloured spans and the existing
per-pane contrast controls are unaffected and still layer on top.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the logrs cheatsheet: a scrollable reference window covering the
markdown md-manuscript renders on export, plus the manuscript-specific
conventions (# Title:/# Slug: metadata, the draft-marker header, and
comments). Opened from Help ▸ Markdown cheatsheet in the menu bar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a workspace folder isn't a git repo itself but sits inside one, ask
the user to confirm before using the parent repository for Sync, rather
than silently operating on whichever repo git walks up to find.
- gitsync: add repo_root() via `git rev-parse --show-toplevel`
- app: detect_repo() distinguishes repo-root / parent-repo / no-repo;
a confirmation dialog adopts or declines the parent repo; git_init
tracks repo_root and clears any pending prompt
- Sync button hover names the enclosing repo when one is adopted
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The left file pane now boosts its widget text colours (toward white in dark
mode, black in light mode) so file names stay legible against the panel
background; the change is scoped to that panel only.
Add a "Contrast" slider beside Zoom that interpolates the editor text colour
from the theme default toward maximum contrast, persisted as
editor_text_contrast in the config. build_editor_job takes the computed colour
so underlined grammar/spelling runs match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Check the current file against a LanguageTool server (typically a local
instance) from a background thread. Issues are underlined in the editor
(red spelling, blue grammar) and listed in a bottom panel with one-click
fixes that re-align the remaining matches.
A menu bar (File / View / Settings) exposes a LanguageTool settings
dialog with discrete scheme / host / port / token / language fields, an
endpoint preview, and a Test connection button. Config stores the parts
and assembles the base URL. The token is sent as a bearer header.
Uses ureq with a statically-linked rustls backend, so both http and
https endpoints work with no new runtime library dependencies.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A Zoom slider (with Reset) above the editor scales the monospace font
from -50% to +200%, defaulting to 0%. The editor keeps infinite desired
width inside a vertical-only scroll area, so text wraps to the pane at
any zoom with no horizontal scrolling. The zoom level persists in config.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A "Zero-pad #" checkbox pads chapter titles that default to their
position number with leading zeros to a uniform width (the digit count
of the largest chapter number, e.g. 03. of 12). Padding never applies to
"# Title:" headers or manual overrides. The setting persists in config.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every chapter after the first now starts on a new page via a
Chapter_20_Break heading style (fo:break-before="page"); the first
chapter keeps the plain heading so there is no blank leading page.
Chapter titles now resolve as: manual override > "# Title:" header >
the chapter's 1-based position in the left pane (e.g. "3."). The old
"# heading" / filename fallbacks are replaced by this numbering, and the
same logic drives the chapter-title hint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A 📂 button beside each path field opens a native picker: a folder
chooser for the workspace and an .odt save dialog for the export (which
appends the extension if omitted). Uses rfd with the XDG Desktop Portal
backend, so there is no GTK build dependency and no new shared library
at runtime.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Shows a live word count for the open file plus the net words added or
removed since this session started. Per-file baselines are snapshotted
when the workspace opens (session_start_counts) and follow renames.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each file is now cleaned before conversion: HTML comments are removed
and everything above the configurable Draft marker (default
"### Rough Draft:") is dropped. A "# Title:" line becomes the chapter
heading and a "# Slug:" line becomes an italic caption beneath it.
- new preprocess module (strip_comments + header parse) with tests
- Chapter gains a slug rendered via a Chapter_20_Caption style, italic
at both paragraph-style and character-run level
- draft_marker config setting + top-bar field
- export title priority: override > # Title: > first # heading > filename
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>