diff --git a/src/app/editor.rs b/src/app/editor.rs index d7537e2..783310f 100644 --- a/src/app/editor.rs +++ b/src/app/editor.rs @@ -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\n"; - let body = crate::preprocess::parse(md, "### Rough Draft:").body; + let md = "---\ntitle: Test\n---\n\nKeep this.\n\n\n"; + let body = crate::preprocess::parse(md, "").body; assert!(body.contains("Keep this.")); assert!(!body.contains("Cut this.")); } diff --git a/src/app/workspace.rs b/src/app/workspace.rs index bd2c074..e101ca1 100644 --- a/src/app/workspace.rs +++ b/src/app/workspace.rs @@ -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:?}"); } diff --git a/src/characters.rs b/src/characters.rs index 003ab00..1b78776 100644 --- a/src/characters.rs +++ b/src/characters.rs @@ -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), } } diff --git a/src/config.rs b/src/config.rs index eb0dcc7..8d1f670 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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", diff --git a/src/help.rs b/src/help.rs index 8053cc9..541a9ea 100644 --- a/src/help.rs +++ b/src/help.rs @@ -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)."), ("", "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 \ diff --git a/src/preprocess.rs b/src/preprocess.rs index 40e0271..e3e5b88 100644 --- a/src/preprocess.rs +++ b/src/preprocess.rs @@ -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 { + 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)] diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 1/scene01.md b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 1/scene01.md index bfafc95..3f44386 100644 --- a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 1/scene01.md +++ b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 1/scene01.md @@ -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:** - diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 2/scene01.md b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 2/scene01.md index 7715eaa..78abaaa 100644 --- a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 2/scene01.md +++ b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 2/scene01.md @@ -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:** diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 3/scene01.md b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 3/scene01.md index 4f6e998..946c0b1 100644 --- a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 3/scene01.md +++ b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/Act 3/scene01.md @@ -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:** diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/_template.md b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/_template.md index 9b336ea..f85fd60 100644 --- a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/_template.md +++ b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/_template.md @@ -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:** - diff --git a/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/draft_v1.md b/templates/snowflake/{{ cookiecutter.project_name }}/05-First Draft/draft_v1.md deleted file mode 100644 index e69de29..0000000