feat: Remove ### Rough Draft: marker, use YAML frontmatter only for draft files
- Everything after YAML frontmatter closing --- is now considered the draft content - No ### Rough Draft: marker needed - Chapter titles are handled by the program on export based on file order - Draft content should not include chapter titles - Updated default_new_file_template to use YAML frontmatter - Updated preprocess.rs to parse YAML frontmatter for metadata - Updated all relevant tests - Updated documentation in help.rs - Updated template files Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
+2
-2
@@ -834,8 +834,8 @@ mod tests {
|
||||
#[test]
|
||||
fn commented_prose_is_dropped_from_the_exported_body() {
|
||||
// The whole point of the hotkey: preprocess must not see the hidden line.
|
||||
let md = "### Rough Draft:\n\nKeep this.\n\n<!-- Cut this. -->\n";
|
||||
let body = crate::preprocess::parse(md, "### Rough Draft:").body;
|
||||
let md = "---\ntitle: Test\n---\n\nKeep this.\n\n<!-- Cut this. -->\n";
|
||||
let body = crate::preprocess::parse(md, "").body;
|
||||
assert!(body.contains("Keep this."));
|
||||
assert!(!body.contains("Cut this."));
|
||||
}
|
||||
|
||||
+9
-13
@@ -813,10 +813,6 @@ order: {}
|
||||
|
||||
---
|
||||
|
||||
## Draft Content
|
||||
|
||||
### Rough Draft:
|
||||
|
||||
[Write your scene content here...]
|
||||
|
||||
---
|
||||
@@ -2027,14 +2023,14 @@ mod tests {
|
||||
#[test]
|
||||
fn render_template_expands_every_placeholder() {
|
||||
let out = render_template(
|
||||
"# Title: {{name}}\n# Started: {{date}}\n\n{{marker}}\n",
|
||||
"---\ntitle: {{name}}\n---\n",
|
||||
"untitled-7",
|
||||
"### Rough Draft:",
|
||||
"",
|
||||
"2026-08-22",
|
||||
);
|
||||
assert_eq!(
|
||||
out,
|
||||
"# Title: untitled-7\n# Started: 2026-08-22\n\n### Rough Draft:\n"
|
||||
"---\ntitle: untitled-7\n---\n"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2074,7 +2070,7 @@ mod tests {
|
||||
assert_eq!(h.slug, None);
|
||||
assert_eq!(h.pov, None);
|
||||
assert_eq!(h.goal, None);
|
||||
// Nothing above the marker leaks into the exported prose.
|
||||
// Nothing above the --- leaks into the exported prose.
|
||||
assert_eq!(h.body.trim(), "");
|
||||
}
|
||||
|
||||
@@ -2083,8 +2079,7 @@ mod tests {
|
||||
fn filled_template_parses_into_header_fields() {
|
||||
let marker = crate::config::default_marker();
|
||||
let seeded = render_template(
|
||||
"# Title: {{name}}\n# Slug: the door\n# POV: Ada\n\
|
||||
# Word Count Target: 1500 - 2000\n\n{{marker}}\n\nReal prose here.\n",
|
||||
"---\ntitle: {{name}}\nslug: the door\npov: Ada\nword_count_target: 1500 - 2000\n---\n\nReal prose here.\n",
|
||||
"chapter-02",
|
||||
&marker,
|
||||
"2026-08-22",
|
||||
@@ -2102,11 +2097,12 @@ mod tests {
|
||||
let out = render_template(
|
||||
&crate::config::default_new_file_template(),
|
||||
"chapter-01",
|
||||
"### Rough Draft:",
|
||||
"",
|
||||
"2026-08-22",
|
||||
);
|
||||
assert!(out.starts_with("# Title: chapter-01\n"), "got {out:?}");
|
||||
assert!(out.contains("\n### Rough Draft:\n"), "got {out:?}");
|
||||
assert!(out.starts_with("---\n"), "got {out:?}");
|
||||
assert!(out.contains("title: chapter-01"), "got {out:?}");
|
||||
assert!(out.contains("---\n"), "got {out:?}");
|
||||
assert!(!out.contains("{{"), "placeholder left unexpanded in {out:?}");
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ mod tests {
|
||||
Character {
|
||||
name: heading_name(text).unwrap_or_default(),
|
||||
file: PathBuf::from("x.md"),
|
||||
fields: crate::preprocess::fields(text, "### Rough Draft:"),
|
||||
fields: crate::preprocess::fields(text, ""),
|
||||
subtitle: subtitle(text),
|
||||
}
|
||||
}
|
||||
|
||||
+11
-15
@@ -236,20 +236,16 @@ pub fn default_projects_dir() -> PathBuf {
|
||||
})
|
||||
}
|
||||
|
||||
/// Default new-file template: the editorial header fields this app already
|
||||
/// understands, followed by the draft marker so the body starts below it.
|
||||
///
|
||||
/// Note: For YAML frontmatter support, create files manually with --- delimiters.
|
||||
/// The legacy header format is kept for backward compatibility with the parser.
|
||||
/// Default new-file template: Uses YAML frontmatter for metadata,
|
||||
/// with everything after the closing --- being the draft content.
|
||||
pub fn default_new_file_template() -> String {
|
||||
[
|
||||
"# Title: {{name}}",
|
||||
"# Slug:",
|
||||
"# POV:",
|
||||
"# Word Count Target:",
|
||||
"",
|
||||
"{{marker}}",
|
||||
"",
|
||||
"---",
|
||||
"title: {{name}}",
|
||||
"slug: ",
|
||||
"pov: ",
|
||||
"word_count_target: ",
|
||||
"---",
|
||||
"",
|
||||
]
|
||||
.join("\n")
|
||||
@@ -296,8 +292,9 @@ pub fn default_languagetool_language() -> String {
|
||||
}
|
||||
|
||||
/// Default header/body separator, matching the manuscript drafting convention.
|
||||
/// Now empty by default since we use YAML frontmatter.
|
||||
pub fn default_marker() -> String {
|
||||
"### Rough Draft:".to_string()
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -456,7 +453,7 @@ mod tests {
|
||||
assert!(cfg.show_preview);
|
||||
// The new field is filled in from its default rather than left blank.
|
||||
assert_eq!(cfg.new_file_template, default_new_file_template());
|
||||
assert!(cfg.new_file_template.contains("{{marker}}"));
|
||||
assert!(cfg.new_file_template.contains("---"));
|
||||
// Likewise the Gitea pair. `use_ssh` defaulting to *true* is the point:
|
||||
// a `#[serde(default)]` bool would come back false and quietly attach
|
||||
// every repository over HTTPS.
|
||||
@@ -472,7 +469,6 @@ mod tests {
|
||||
"workspace": "/home/writer/Manuscript",
|
||||
"export_path": "/home/writer/Manuscript/book.odt",
|
||||
"show_preview": true,
|
||||
"draft_marker": "### Rough Draft:",
|
||||
"editor_zoom": 10.0,
|
||||
"languagetool_host": "lt.example.com",
|
||||
"mistral_api_key": "secret",
|
||||
|
||||
+11
-9
@@ -1,6 +1,6 @@
|
||||
//! In-app **Markdown cheatsheet** window: a quick reference for the markdown
|
||||
//! md-manuscript understands, plus the manuscript-specific conventions (chapter
|
||||
//! metadata, the draft-marker header, and comments) applied at export time.
|
||||
//! md-manuscript understands, plus the manuscript-specific conventions (YAML frontmatter
|
||||
//! metadata and comments) applied at export time.
|
||||
//!
|
||||
//! Pure rendering — the caller owns the `open` flag so the window's ✕ works via
|
||||
//! [`egui::Window::open`].
|
||||
@@ -136,24 +136,26 @@ fn cheatsheet_body(ui: &mut egui::Ui) {
|
||||
ui,
|
||||
"cs-header",
|
||||
&[
|
||||
("### Rough Draft:", "The draft marker — everything above it is your working header."),
|
||||
("---", "YAML frontmatter delimiter — metadata between --- lines is not exported."),
|
||||
("title: ...", "Chapter title (used for export, not included in draft content)."),
|
||||
("slug: ...", "Optional caption shown beneath the chapter title."),
|
||||
("pov: ...", "Point of view character for this chapter."),
|
||||
("word_count_target: ...", "Target word count (e.g., 2000 or 1500-2000)."),
|
||||
("<!-- note -->", "A comment; dropped on export (may span multiple lines)."),
|
||||
],
|
||||
);
|
||||
note(
|
||||
ui,
|
||||
"The draft marker text is configurable in the top bar. Everything above the \
|
||||
marker line is treated as notes and left out of the export; leave the marker \
|
||||
blank to export the whole file. Comments are removed everywhere.",
|
||||
"Files use YAML frontmatter for metadata. Everything between --- delimiters is \
|
||||
metadata and is not included in the export. Comments are removed everywhere. \
|
||||
Chapter titles are automatically numbered based on file order in the file list.",
|
||||
);
|
||||
note(
|
||||
ui,
|
||||
"Typing a header field name above the marker (e.g. “## PO”) pops up an \
|
||||
"Typing a YAML field name (e.g. \"title:\" or \"pov:\") in the frontmatter pops up an \
|
||||
autocomplete — Tab/Enter to accept, ↑/↓ to choose, Esc to dismiss. It \
|
||||
suggests common fields plus ones learned from your other files.",
|
||||
);
|
||||
|
||||
section(ui, "Spelling & grammar");
|
||||
body(
|
||||
ui,
|
||||
"Spelling is checked live and offline: misspellings are underlined in red \
|
||||
|
||||
+191
-2
@@ -281,13 +281,55 @@ fn meta_line(line: &str) -> Option<(Meta, String)> {
|
||||
}
|
||||
|
||||
/// Pre-process one markdown document, splitting header metadata from the body.
|
||||
/// `marker` is the header/body separator line (e.g. `### Rough Draft:`); an empty
|
||||
/// marker disables header-block splitting.
|
||||
/// Tries YAML frontmatter first, then falls back to the legacy marker-based header.
|
||||
/// `marker` is the legacy header/body separator line (e.g. `### Rough Draft:`); an empty
|
||||
/// marker disables marker-based splitting, but YAML frontmatter is still parsed.
|
||||
pub fn parse(markdown: &str, marker: &str) -> Header {
|
||||
let cleaned = strip_comments(markdown);
|
||||
let lines: Vec<&str> = cleaned.lines().collect();
|
||||
let marker_norm = marker.trim().to_ascii_lowercase();
|
||||
|
||||
// Try YAML frontmatter first
|
||||
if let Some(body_start_line) = find_yaml_frontmatter_end(&lines) {
|
||||
// Parse metadata from YAML frontmatter
|
||||
let mut title = None;
|
||||
let mut slug = None;
|
||||
let mut pov = None;
|
||||
let mut goal = None;
|
||||
|
||||
// Extract YAML fields from the frontmatter lines
|
||||
for line in &lines[..body_start_line] {
|
||||
// Skip the --- delimiters
|
||||
if line.trim() == "---" {
|
||||
continue;
|
||||
}
|
||||
// Parse YAML-style key: value lines
|
||||
if let Some((key, value)) = parse_yaml_field(line) {
|
||||
match key.to_ascii_lowercase().as_str() {
|
||||
"title" => title = Some(value),
|
||||
"slug" => slug = Some(value),
|
||||
"pov" => pov = Some(value),
|
||||
"word_count_target" | "word count target" | "wordcounttarget" => {
|
||||
goal = parse_goal(&value);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Body is everything after the closing ---
|
||||
let body_lines: Vec<&str> = lines[body_start_line..].to_vec();
|
||||
|
||||
return Header {
|
||||
title: title.filter(|s| !s.is_empty()),
|
||||
slug: slug.filter(|s| !s.is_empty()),
|
||||
pov: pov.filter(|s| !s.is_empty()),
|
||||
goal,
|
||||
body: body_lines.join("\n").trim().to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
// Fall back to legacy marker-based parsing
|
||||
let marker_idx = if marker_norm.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -345,6 +387,115 @@ pub fn parse(markdown: &str, marker: &str) -> Header {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the line index where YAML frontmatter ends (after the closing `---`).
|
||||
/// Returns Some(body_start_line) where body_start_line is the index after the closing ---.
|
||||
fn find_yaml_frontmatter_end(lines: &[&str]) -> Option<usize> {
|
||||
if lines.is_empty() || lines[0].trim() != "---" {
|
||||
return None;
|
||||
}
|
||||
|
||||
for (i, line) in lines.iter().enumerate().skip(1) {
|
||||
if line.trim() == "---" {
|
||||
return Some(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Parse a YAML field line like `key: value` or `key: "value"`.
|
||||
/// Returns (key, value) if the line looks like a YAML field.
|
||||
/// Strips inline comments (everything after # that's not in quotes).
|
||||
fn parse_yaml_field(line: &str) -> Option<(String, String)> {
|
||||
let trimmed = line.trim();
|
||||
|
||||
// Skip empty lines and full-line comments
|
||||
if trimmed.is_empty() || trimmed.starts_with('#') {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Strip inline comments first (everything after # that's not in quotes)
|
||||
let mut in_quote = false;
|
||||
let mut quote_char = '\0';
|
||||
let mut comment_pos = None;
|
||||
|
||||
for (i, c) in trimmed.char_indices() {
|
||||
match c {
|
||||
'"' | '\'' => {
|
||||
if in_quote && c == quote_char {
|
||||
in_quote = false;
|
||||
quote_char = '\0';
|
||||
} else if !in_quote {
|
||||
in_quote = true;
|
||||
quote_char = c;
|
||||
}
|
||||
}
|
||||
'#' => {
|
||||
if !in_quote && comment_pos.is_none() {
|
||||
comment_pos = Some(i);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Trim at the comment position if found
|
||||
let trimmed_no_comment = if let Some(pos) = comment_pos {
|
||||
trimmed[..pos].trim()
|
||||
} else {
|
||||
trimmed
|
||||
};
|
||||
|
||||
if trimmed_no_comment.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find the first colon that's not part of a quoted string
|
||||
let mut in_quote = false;
|
||||
let mut quote_char = '\0';
|
||||
let mut colon_pos = None;
|
||||
|
||||
for (i, c) in trimmed_no_comment.char_indices() {
|
||||
match c {
|
||||
'"' | '\'' => {
|
||||
if in_quote && c == quote_char {
|
||||
in_quote = false;
|
||||
quote_char = '\0';
|
||||
} else if !in_quote {
|
||||
in_quote = true;
|
||||
quote_char = c;
|
||||
}
|
||||
}
|
||||
':' => {
|
||||
if !in_quote && colon_pos.is_none() {
|
||||
colon_pos = Some(i);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let colon_pos = colon_pos?;
|
||||
|
||||
let key = trimmed_no_comment[..colon_pos].trim().to_string();
|
||||
let value = trimmed_no_comment[colon_pos + 1..].trim();
|
||||
|
||||
// Remove surrounding quotes if present
|
||||
let value = if value.starts_with('"') && value.ends_with('"') {
|
||||
value[1..value.len() - 1].to_string()
|
||||
} else if value.starts_with('\'') && value.ends_with('\'') {
|
||||
value[1..value.len() - 1].to_string()
|
||||
} else {
|
||||
value.to_string()
|
||||
};
|
||||
|
||||
if key.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some((key, value))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -443,6 +594,44 @@ mod tests {
|
||||
let md2 = "### Rough Draft:\n\nPOV of the crowd was tense.";
|
||||
assert_eq!(parse(md2, MARKER).pov, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_yaml_frontmatter() {
|
||||
let md = "---\ntitle: The Gate\nslug: an opening\npov: Bixby\nword_count_target: 1500 - 2000\n---\n\nThe prose begins here.\n\n## A scene";
|
||||
let h = parse(md, "");
|
||||
assert_eq!(h.title.as_deref(), Some("The Gate"));
|
||||
assert_eq!(h.slug.as_deref(), Some("an opening"));
|
||||
assert_eq!(h.pov.as_deref(), Some("Bixby"));
|
||||
assert_eq!(h.goal, Some(WordGoal { min: 1500, max: 2000 }));
|
||||
assert!(h.body.starts_with("The prose begins here."));
|
||||
assert!(h.body.contains("## A scene"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_yaml_frontmatter_with_quoted_values() {
|
||||
let md = "---\ntitle: \"The Gate\"\nslug: \"an opening\"\n---\n\nProse here.";
|
||||
let h = parse(md, "");
|
||||
assert_eq!(h.title.as_deref(), Some("The Gate"));
|
||||
assert_eq!(h.slug.as_deref(), Some("an opening"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_yaml_frontmatter_with_comments() {
|
||||
let md = "---\n# This is a comment
|
||||
title: The Gate # inline comment\nslug: an opening\n---\n\nProse here.";
|
||||
let h = parse(md, "");
|
||||
assert_eq!(h.title.as_deref(), Some("The Gate"));
|
||||
assert_eq!(h.slug.as_deref(), Some("an opening"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn falls_back_to_marker_if_no_yaml_frontmatter() {
|
||||
let md = "# Title: The Gate\n# Slug: an opening\n### Rough Draft:\n\nThe prose begins here.";
|
||||
let h = parse(md, "### Rough Draft:");
|
||||
assert_eq!(h.title.as_deref(), Some("The Gate"));
|
||||
assert_eq!(h.slug.as_deref(), Some("an opening"));
|
||||
assert!(h.body.starts_with("The prose begins here."));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+3
-118
@@ -1,128 +1,13 @@
|
||||
---
|
||||
id: scene_01
|
||||
title: "Chapter One: The Beginning"
|
||||
slug: chapter-one-the-beginning
|
||||
title: "Scene 1"
|
||||
slug: "scene-1"
|
||||
act: 1
|
||||
chapter: 1
|
||||
scene: 1
|
||||
type: chapter
|
||||
type: scene
|
||||
status: draft
|
||||
word_count_target: 2000
|
||||
word_count_min: 1800
|
||||
word_count_max: 2200
|
||||
pov: ""
|
||||
characters: []
|
||||
setting: ""
|
||||
date: ""
|
||||
time: ""
|
||||
conflict: ""
|
||||
theme: ""
|
||||
tone: ""
|
||||
word_count: 0
|
||||
word_count_updated: ""
|
||||
session_word_count: 0
|
||||
last_session: ""
|
||||
total_sessions: 0
|
||||
version: 1
|
||||
created: ""
|
||||
updated: ""
|
||||
order: 1
|
||||
---
|
||||
|
||||
# Chapter One: The Beginning
|
||||
|
||||
**Scene File** | **ID:** scene_01 | **Status:** Draft
|
||||
|
||||
---
|
||||
|
||||
## Scene Metadata
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| **Act** | 1 |
|
||||
| **Chapter** | 1 |
|
||||
| **Scene** | 1 |
|
||||
| **Type** | Chapter |
|
||||
| **POV** | Not set |
|
||||
| **Version** | 1 |
|
||||
| **Word Count Target** | 2000 |
|
||||
|
||||
---
|
||||
|
||||
## Word Count Tracking
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| **Target** | 2000 |
|
||||
| **Minimum** | 1800 |
|
||||
| **Maximum** | 2200 |
|
||||
| **Current** | 0 |
|
||||
| **This Session** | 0 |
|
||||
| **Last Updated** | Never |
|
||||
|
||||
---
|
||||
|
||||
## Creative Elements
|
||||
|
||||
### POV
|
||||
> Not specified
|
||||
|
||||
### Characters
|
||||
|
||||
### Setting
|
||||
> Not specified
|
||||
|
||||
### Date & Time
|
||||
> Not specified | Not specified
|
||||
|
||||
---
|
||||
|
||||
## Story Structure
|
||||
|
||||
### Conflict
|
||||
> Not specified
|
||||
|
||||
### Theme
|
||||
> Not specified
|
||||
|
||||
### Tone
|
||||
> Not specified
|
||||
|
||||
---
|
||||
|
||||
## Draft Content
|
||||
|
||||
### Rough Draft:
|
||||
|
||||
[Write your scene content here...]
|
||||
|
||||
---
|
||||
|
||||
## Writing Notes
|
||||
|
||||
### Session Log
|
||||
| Date | Words Added | Notes |
|
||||
|------|--------------|-------|
|
||||
|
||||
|
||||
### Revision Notes
|
||||
-
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## Status Checklist
|
||||
|
||||
- [ ] Planning complete
|
||||
- [ ] First draft complete
|
||||
- [ ] Word count target met
|
||||
- [ ] POV consistent
|
||||
- [ ] Character voices distinct
|
||||
- [ ] Setting details accurate
|
||||
- [ ] Conflict resolved/advanced
|
||||
- [ ] Ready for revision
|
||||
|
||||
---
|
||||
|
||||
**Created:** | **Updated:**
|
||||
|
||||
|
||||
+5
-121
@@ -1,129 +1,13 @@
|
||||
---
|
||||
id: scene_02_01
|
||||
title: "Chapter Two: Development"
|
||||
slug: chapter-two-development
|
||||
title: "Scene 2"
|
||||
slug: "scene-2"
|
||||
act: 2
|
||||
chapter: 1
|
||||
scene: 1
|
||||
type: chapter
|
||||
chapter: 2
|
||||
scene: 2
|
||||
type: scene
|
||||
status: draft
|
||||
word_count_target: 2000
|
||||
word_count_min: 1800
|
||||
word_count_max: 2200
|
||||
pov: ""
|
||||
characters: []
|
||||
setting: ""
|
||||
date: ""
|
||||
time: ""
|
||||
conflict: ""
|
||||
theme: ""
|
||||
tone: ""
|
||||
word_count: 0
|
||||
word_count_updated: ""
|
||||
session_word_count: 0
|
||||
last_session: ""
|
||||
total_sessions: 0
|
||||
version: 1
|
||||
created: ""
|
||||
updated: ""
|
||||
planning_template: ""
|
||||
draft_file: ""
|
||||
order: 1
|
||||
---
|
||||
|
||||
# Chapter Two: Development
|
||||
|
||||
**Draft File** | **ID:** scene_02_01 | **Status:** Draft
|
||||
|
||||
---
|
||||
|
||||
## Scene Metadata
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| **Act** | 2 |
|
||||
| **Chapter** | 1 |
|
||||
| **Scene** | 1 |
|
||||
| **Type** | Chapter |
|
||||
| **POV** | Not set |
|
||||
| **Version** | 1 |
|
||||
| **Word Count Target** | 2000 |
|
||||
|
||||
---
|
||||
|
||||
## Word Count Tracking
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| **Target** | 2000 |
|
||||
| **Minimum** | 1800 |
|
||||
| **Maximum** | 2200 |
|
||||
| **Current** | 0 |
|
||||
| **This Session** | 0 |
|
||||
| **Last Updated** | Never |
|
||||
|
||||
---
|
||||
|
||||
## Creative Elements
|
||||
|
||||
### POV
|
||||
> Not specified
|
||||
|
||||
### Characters
|
||||
|
||||
### Setting
|
||||
> Not specified
|
||||
|
||||
### Date & Time
|
||||
> Not specified | Not specified
|
||||
|
||||
---
|
||||
|
||||
## Story Structure
|
||||
|
||||
### Conflict
|
||||
> Not specified
|
||||
|
||||
### Theme
|
||||
> Not specified
|
||||
|
||||
### Tone
|
||||
> Not specified
|
||||
|
||||
---
|
||||
|
||||
## Draft Content
|
||||
|
||||
### Rough Draft:
|
||||
|
||||
[Write your scene content here...]
|
||||
|
||||
---
|
||||
|
||||
## Writing Notes
|
||||
|
||||
### Session Log
|
||||
| Date | Words Added | Notes |
|
||||
|------|--------------|-------|
|
||||
|
||||
|
||||
### Revision Notes
|
||||
-
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## Status Checklist
|
||||
|
||||
- [ ] Planning complete
|
||||
- [ ] First draft complete
|
||||
- [ ] Word count target met
|
||||
- [ ] POV consistent
|
||||
- [ ] Character voices distinct
|
||||
- [ ] Setting details accurate
|
||||
- [ ] Conflict resolved/advanced
|
||||
- [ ] Ready for revision
|
||||
|
||||
---
|
||||
|
||||
**Created:** | **Updated:**
|
||||
|
||||
+5
-121
@@ -1,129 +1,13 @@
|
||||
---
|
||||
id: scene_03_01
|
||||
title: "Chapter Three: Climax"
|
||||
slug: chapter-three-climax
|
||||
title: "Scene 3"
|
||||
slug: "scene-3"
|
||||
act: 3
|
||||
chapter: 1
|
||||
scene: 1
|
||||
type: chapter
|
||||
chapter: 3
|
||||
scene: 3
|
||||
type: scene
|
||||
status: draft
|
||||
word_count_target: 2000
|
||||
word_count_min: 1800
|
||||
word_count_max: 2200
|
||||
pov: ""
|
||||
characters: []
|
||||
setting: ""
|
||||
date: ""
|
||||
time: ""
|
||||
conflict: ""
|
||||
theme: ""
|
||||
tone: ""
|
||||
word_count: 0
|
||||
word_count_updated: ""
|
||||
session_word_count: 0
|
||||
last_session: ""
|
||||
total_sessions: 0
|
||||
version: 1
|
||||
created: ""
|
||||
updated: ""
|
||||
planning_template: ""
|
||||
draft_file: ""
|
||||
order: 1
|
||||
---
|
||||
|
||||
# Chapter Three: Climax
|
||||
|
||||
**Draft File** | **ID:** scene_03_01 | **Status:** Draft
|
||||
|
||||
---
|
||||
|
||||
## Scene Metadata
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| **Act** | 3 |
|
||||
| **Chapter** | 1 |
|
||||
| **Scene** | 1 |
|
||||
| **Type** | Chapter |
|
||||
| **POV** | Not set |
|
||||
| **Version** | 1 |
|
||||
| **Word Count Target** | 2000 |
|
||||
|
||||
---
|
||||
|
||||
## Word Count Tracking
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| **Target** | 2000 |
|
||||
| **Minimum** | 1800 |
|
||||
| **Maximum** | 2200 |
|
||||
| **Current** | 0 |
|
||||
| **This Session** | 0 |
|
||||
| **Last Updated** | Never |
|
||||
|
||||
---
|
||||
|
||||
## Creative Elements
|
||||
|
||||
### POV
|
||||
> Not specified
|
||||
|
||||
### Characters
|
||||
|
||||
### Setting
|
||||
> Not specified
|
||||
|
||||
### Date & Time
|
||||
> Not specified | Not specified
|
||||
|
||||
---
|
||||
|
||||
## Story Structure
|
||||
|
||||
### Conflict
|
||||
> Not specified
|
||||
|
||||
### Theme
|
||||
> Not specified
|
||||
|
||||
### Tone
|
||||
> Not specified
|
||||
|
||||
---
|
||||
|
||||
## Draft Content
|
||||
|
||||
### Rough Draft:
|
||||
|
||||
[Write your scene content here...]
|
||||
|
||||
---
|
||||
|
||||
## Writing Notes
|
||||
|
||||
### Session Log
|
||||
| Date | Words Added | Notes |
|
||||
|------|--------------|-------|
|
||||
|
||||
|
||||
### Revision Notes
|
||||
-
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## Status Checklist
|
||||
|
||||
- [ ] Planning complete
|
||||
- [ ] First draft complete
|
||||
- [ ] Word count target met
|
||||
- [ ] POV consistent
|
||||
- [ ] Character voices distinct
|
||||
- [ ] Setting details accurate
|
||||
- [ ] Conflict resolved/advanced
|
||||
- [ ] Ready for revision
|
||||
|
||||
---
|
||||
|
||||
**Created:** | **Updated:**
|
||||
|
||||
@@ -1,164 +1,21 @@
|
||||
---
|
||||
# Merged Planning + Draft Template
|
||||
# YAML frontmatter contains all planning fields; body is the draft content
|
||||
|
||||
id: scene_new
|
||||
# Unique identifier for this scene
|
||||
# Basic metadata for your scene/chapter
|
||||
# Everything after the closing --- is your draft content
|
||||
|
||||
id: ""
|
||||
title: "New Scene Title"
|
||||
# Display title for the scene
|
||||
|
||||
slug: new-scene-title
|
||||
# URL-friendly slug
|
||||
|
||||
# === Core Metadata ===
|
||||
slug: ""
|
||||
act: 1
|
||||
chapter: 1
|
||||
scene: 99
|
||||
scene: 1
|
||||
type: scene
|
||||
# Type: scene, chapter, interlude, etc.
|
||||
|
||||
# === Planning Fields ===
|
||||
status: draft
|
||||
# Status: planning, draft, revised, done
|
||||
word_count_target: 2000
|
||||
word_count_min: 1800
|
||||
word_count_max: 2200
|
||||
|
||||
# === Creative Elements ===
|
||||
pov: ""
|
||||
# Point of view character
|
||||
|
||||
characters: []
|
||||
# Characters appearing in this scene (array of names)
|
||||
|
||||
setting: ""
|
||||
# Location/setting
|
||||
date: ""
|
||||
time: ""
|
||||
# Date and time of scene
|
||||
|
||||
conflict: ""
|
||||
# Central conflict in this scene
|
||||
theme: ""
|
||||
# Themes explored
|
||||
tone: ""
|
||||
# Mood/tone
|
||||
|
||||
# === Draft Tracking ===
|
||||
word_count: 0
|
||||
word_count_updated: ""
|
||||
session_word_count: 0
|
||||
last_session: ""
|
||||
total_sessions: 0
|
||||
version: 1
|
||||
|
||||
# === Timestamps ===
|
||||
created: ""
|
||||
updated: ""
|
||||
|
||||
# === Linking ===
|
||||
# These are kept for backward compatibility but are no longer needed
|
||||
# since planning and draft are merged
|
||||
planning_template: ""
|
||||
draft_file: ""
|
||||
order: 99
|
||||
---
|
||||
|
||||
# New Scene Title
|
||||
|
||||
**Scene File** | **ID:** scene_new | **Status:** Draft
|
||||
|
||||
---
|
||||
|
||||
## Scene Metadata
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| **Act** | 1 |
|
||||
| **Chapter** | 1 |
|
||||
| **Scene** | 99 |
|
||||
| **Type** | Scene |
|
||||
| **POV** | Not set |
|
||||
| **Version** | 1 |
|
||||
| **Word Count Target** | 2000 |
|
||||
|
||||
---
|
||||
|
||||
## Word Count Tracking
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| **Target** | 2000 |
|
||||
| **Minimum** | 1800 |
|
||||
| **Maximum** | 2200 |
|
||||
| **Current** | 0 |
|
||||
| **This Session** | 0 |
|
||||
| **Last Updated** | Never |
|
||||
|
||||
---
|
||||
|
||||
## Creative Elements
|
||||
|
||||
### POV
|
||||
> Not specified
|
||||
|
||||
### Characters
|
||||
|
||||
### Setting
|
||||
> Not specified
|
||||
|
||||
### Date & Time
|
||||
> Not specified | Not specified
|
||||
|
||||
---
|
||||
|
||||
## Story Structure
|
||||
|
||||
### Conflict
|
||||
> Not specified
|
||||
|
||||
### Theme
|
||||
> Not specified
|
||||
|
||||
### Tone
|
||||
> Not specified
|
||||
|
||||
---
|
||||
|
||||
## Draft Content
|
||||
|
||||
### Rough Draft:
|
||||
|
||||
[Write your scene content here...]
|
||||
|
||||
---
|
||||
|
||||
## Writing Notes
|
||||
|
||||
### Session Log
|
||||
| Date | Words Added | Notes |
|
||||
|------|--------------|-------|
|
||||
|
||||
|
||||
### Revision Notes
|
||||
-
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## Status Checklist
|
||||
|
||||
- [ ] Planning complete
|
||||
- [ ] First draft complete
|
||||
- [ ] Word count target met
|
||||
- [ ] POV consistent
|
||||
- [ ] Character voices distinct
|
||||
- [ ] Setting details accurate
|
||||
- [ ] Conflict resolved/advanced
|
||||
- [ ] Ready for revision
|
||||
|
||||
---
|
||||
|
||||
**Created:** | **Updated:**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user