aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-05 11:54:56 +0200
committerDanilo M. <danix@danix.xyz>2026-10-05 11:54:56 +0200
commit20e515b79796789a835a76aaf5656f5370764968 (patch)
treeef2c4ff9bf2b139f59bfcd936ae9668b9aae039f
parente3e08870d4d120145d5667a16602fc3a15167b76 (diff)
downloadfanfictioner-20e515b79796789a835a76aaf5656f5370764968.tar.gz
fanfictioner-20e515b79796789a835a76aaf5656f5370764968.zip
Drop chapter_title from the story frontmatter
It only replaced the plan.md heading, which nothing downstream reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--README.md5
-rw-r--r--docs/superpowers/specs/2026-10-04-fanfictioner-design.md5
-rwxr-xr-xfanfictioner12
3 files changed, 9 insertions, 13 deletions
diff --git a/README.md b/README.md
index 4ce0704..ff3cec0 100644
--- a/README.md
+++ b/README.md
@@ -22,12 +22,11 @@ instead of letting Gemma invent them; every key is optional:
series: A new life
book: Life as Daniel
chapter: 1
- chapter_title: Intro
title: Meet Daniel
---
-`series` and `book` name the library folders, `title` is the cover lettering,
-`chapter_title` heads plan.md. With `chapter`, pages are saved as
+`series` and `book` name the library folders, `title` is the lettering on the
+chapter's page 1. With `chapter`, pages are saved as
`01-001.png`, `01-002.png`, so every chapter of a book (one story file each)
reads as one continuous book in book-reader. These values always override what
Gemma writes in plan.md.
diff --git a/docs/superpowers/specs/2026-10-04-fanfictioner-design.md b/docs/superpowers/specs/2026-10-04-fanfictioner-design.md
index d762243..9ebcb8f 100644
--- a/docs/superpowers/specs/2026-10-04-fanfictioner-design.md
+++ b/docs/superpowers/specs/2026-10-04-fanfictioner-design.md
@@ -56,10 +56,9 @@ read from `~/.config/book-reader.conf` (shell `KEY="value"` lines).
Series and Book come from plan.md.
story.md may start with a `---` frontmatter (`series`, `book`, `chapter`,
-`chapter_title`, `title`, all optional). The script writes these into plan.md
+`title`, all optional). The script writes these into plan.md
after every draft and revision and whenever story.md changes, overriding
-Gemma: Series/Book/Chapter header lines, the `# ` heading, and page 1's
-`Title:`. plan.json is rebuilt when plan.md or story.md is newer than it.
+Gemma: Series/Book/Chapter header lines and page 1's `Title:`. plan.json is rebuilt when plan.md or story.md is newer than it.
Resume is derived from disk, no state file: a character with `refs/<Name>.png`
is skipped; a page whose `NNN.png` exists in the library is skipped.
diff --git a/fanfictioner b/fanfictioner
index 267b9dc..010b521 100755
--- a/fanfictioner
+++ b/fanfictioner
@@ -137,10 +137,8 @@ def apply_front(md, meta):
if not (drop and re.match(r"[-*\s]*\**(%s)\**:" % "|".join(drop), x.strip()))]
top = next((i for i, x in enumerate(lines) if x.startswith("# ")), None)
if top is None:
- lines.insert(0, "# " + (meta.get("chapter_title") or meta.get("title") or "Untitled"))
+ lines.insert(0, "# " + meta.get("title", "Untitled"))
top = 0
- elif meta.get("chapter_title"):
- lines[top] = "# " + meta["chapter_title"]
lines[top + 1:top + 1] = [f"{k}: {meta[k.lower()]}" for k in keys]
if meta.get("title"):
p1 = next((i for i, x in enumerate(lines) if re.match(r"###\s+1\.", x.strip())), None)
@@ -656,19 +654,19 @@ def selftest():
parse_plan(SAMPLE_PLAN.replace("wide shot, portrait", "wide shot")))
meta, body = parse_front('---\nseries: A new life\nbook: "Life as Daniel"\nchapter: 1\n'
- 'chapter_title: Intro\ntitle: Meet Daniel\n---\nThis is the story.\n')
+ 'title: Meet Daniel\n---\nThis is the story.\n')
assert meta == {"series": "A new life", "book": "Life as Daniel", "chapter": "1",
- "chapter_title": "Intro", "title": "Meet Daniel"} and body == "This is the story.\n", meta
+ "title": "Meet Daniel"} and body == "This is the story.\n", meta
assert parse_front("no frontmatter") == ({}, "no frontmatter")
forced = apply_front(SAMPLE_PLAN.replace("Framing: wide shot, eye level, landscape",
"Framing: wide shot, eye level, landscape\nTitle: Gemma Title"), meta)
assert apply_front(forced, meta) == forced
assert apply_front(SAMPLE_PLAN, {}) == SAMPLE_PLAN
fp = parse_plan(forced)
- assert (fp["title"], fp["series"], fp["book"], fp["chapter"]) == ("Intro", "A new life", "Life as Daniel", "1"), fp
+ assert (fp["title"], fp["series"], fp["book"], fp["chapter"]) == ("Rooftop", "A new life", "Life as Daniel", "1"), fp
assert fp["pages"][0]["lettering"] == "Meet Daniel" and fp["pages"][1]["lettering"] == "", fp["pages"]
assert fp["pages"][0]["framing"] == "wide shot, eye level, landscape" and validate_plan(fp) == []
- assert forced.count("Title:") == 1 and forced.startswith("# Intro\nSeries: A new life\nBook: Life as Daniel\n"
+ assert forced.count("Title:") == 1 and forced.startswith("# Rooftop\nSeries: A new life\nBook: Life as Daniel\n"
"Chapter: 1\nStyle:"), forced
assert build_json(fp)["chapter"] == 1 and build_json(plan)["chapter"] is None
assert 'Chapter must be a number, not "one"' in validate_plan(