Strip comments and header metadata on ODT export
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>
This commit is contained in:
+31
-5
@@ -276,21 +276,33 @@ impl App {
|
||||
|
||||
fn export_odt(&mut self) {
|
||||
self.save_current();
|
||||
let marker = self.config.draft_marker.clone();
|
||||
let mut chapters = Vec::new();
|
||||
for name in &self.files {
|
||||
let markdown = std::fs::read_to_string(self.path_for(name)).unwrap_or_default();
|
||||
// The leading `# heading` is always consumed as the chapter-title
|
||||
// slot; a manual override, if set, replaces the derived title.
|
||||
let (auto_title, body) = split_title(&markdown, name);
|
||||
let raw = std::fs::read_to_string(self.path_for(name)).unwrap_or_default();
|
||||
// Strip comments + the editorial header, and lift out any
|
||||
// `# Title:` / `# Slug:` metadata.
|
||||
let header = crate::preprocess::parse(&raw, &marker);
|
||||
|
||||
// Title priority: manual override > `# Title:` metadata > the body's
|
||||
// leading `# heading` (consumed) > file name. When a title comes from
|
||||
// metadata/override, the body is left intact; otherwise the leading
|
||||
// `# heading` is consumed into the title slot to avoid duplication.
|
||||
let (title, body) = if let Some(t) = &header.title {
|
||||
(t.clone(), header.body.clone())
|
||||
} else {
|
||||
split_title(&header.body, name)
|
||||
};
|
||||
let title = self
|
||||
.titles
|
||||
.get(name)
|
||||
.map(|t| t.trim())
|
||||
.filter(|t| !t.is_empty())
|
||||
.map(|t| t.to_string())
|
||||
.unwrap_or(auto_title);
|
||||
.unwrap_or(title);
|
||||
chapters.push(Chapter {
|
||||
title,
|
||||
slug: header.slug.clone(),
|
||||
markdown: body,
|
||||
});
|
||||
}
|
||||
@@ -376,6 +388,20 @@ impl App {
|
||||
{
|
||||
self.config.save();
|
||||
}
|
||||
ui.separator();
|
||||
ui.label("Draft marker:")
|
||||
.on_hover_text(
|
||||
"Text above this line in each file (its header) is dropped on export; \
|
||||
# Title: / # Slug: lines become the chapter heading and caption. \
|
||||
Leave blank to export files whole.",
|
||||
);
|
||||
let marker_resp = ui.add(
|
||||
egui::TextEdit::singleline(&mut self.config.draft_marker)
|
||||
.desired_width(140.0),
|
||||
);
|
||||
if marker_resp.lost_focus() {
|
||||
self.config.save();
|
||||
}
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user