fix(export): render vimwiki-compatible HTML for templates, links, and tasks
Stock vimwiki templates and stylesheets rendered broken pages because the
HTML output diverged from vimwiki conventions on three fronts:
- Templates: the renderer only substituted nuwiki's `{{key}}` placeholders,
so vimwiki's `%title%`/`%content%`/`%root_path%`/`%date%`/`%wiki_css%`
passed through literally — dropping the body and breaking asset links.
Both delimiter styles are now recognised; `%wiki_css%` aliases the css var.
- Links: `[[todo.wiki]]` exported to `todo.wiki.html`. render_page_html now
takes the wiki file extension and strips it from wiki/interwiki targets
(via index::strip_wiki_extension) before the `.html` URL is built, matching
in-editor navigation. file:/local: links keep their literal extension.
- Tasks: checkbox items used bespoke `task-*` classes plus an `<input>`,
which double-rendered against vimwiki stylesheets. Emit `done0..done4`
(`[ ] [.] [o] [O] [X]`) and `rejected` (`[-]`) classes with no input — the
stylesheet draws the box.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,11 @@
|
||||
//! `{{date}}`, `{{root_path}}`, `{{toc}}`). Order: `{{content}}` is
|
||||
//! substituted first so a body that happens to contain a literal
|
||||
//! `{{title}}` isn't itself rewritten in the next pass.
|
||||
//!
|
||||
//! Both delimiter styles are recognised for every placeholder: nuwiki's
|
||||
//! native `{{key}}` and vimwiki's `%key%` (`%title%`, `%content%`,
|
||||
//! `%root_path%`, `%date%`, `%wiki_css%`, …). This lets stock vimwiki
|
||||
//! templates render unchanged.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, Write};
|
||||
@@ -107,14 +112,17 @@ impl Renderer for HtmlRenderer {
|
||||
let body_str = std::str::from_utf8(&body)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||
let title = doc.metadata.title.as_deref().unwrap_or("");
|
||||
// Replace `{{content}}` first so its substituted body, which may
|
||||
// legitimately contain the literal `{{title}}`, isn't itself
|
||||
// rewritten in the next pass.
|
||||
// Substitute `content` before `title` so a body that legitimately
|
||||
// contains a literal title placeholder isn't rewritten. Both the
|
||||
// native `{{key}}` and vimwiki's `%key%` delimiters are accepted so
|
||||
// stock vimwiki templates render unchanged.
|
||||
let mut out = template.replace("{{content}}", body_str);
|
||||
out = out.replace("%content%", body_str);
|
||||
out = out.replace("{{title}}", title);
|
||||
out = out.replace("%title%", title);
|
||||
for (k, v) in &self.vars {
|
||||
let needle = format!("{{{{{k}}}}}");
|
||||
out = out.replace(&needle, v);
|
||||
out = out.replace(&format!("{{{{{k}}}}}"), v);
|
||||
out = out.replace(&format!("%{k}%"), v);
|
||||
}
|
||||
w.write_all(out.as_bytes())?;
|
||||
} else {
|
||||
@@ -239,18 +247,20 @@ impl HtmlRenderer {
|
||||
fn render_list_item(&self, n: &ListItemNode, w: &mut dyn Write) -> io::Result<()> {
|
||||
match n.checkbox {
|
||||
Some(state) => {
|
||||
let (checked, class) = match state {
|
||||
CheckboxState::Empty => ("", "task-empty"),
|
||||
CheckboxState::Quarter => ("", "task-quarter"),
|
||||
CheckboxState::Half => ("", "task-half"),
|
||||
CheckboxState::ThreeQuarters => ("", "task-three-quarters"),
|
||||
CheckboxState::Done => (" checked", "task-done"),
|
||||
CheckboxState::Rejected => ("", "task-rejected"),
|
||||
// vimwiki-compatible checkbox classes: `[ ]`→done0, `[.]`→done1,
|
||||
// `[o]`→done2, `[O]`→done3, `[X]`→done4, `[-]`→rejected. The
|
||||
// checkbox glyph/progress fill is drawn entirely by the
|
||||
// stylesheet (`li.doneN::before`); vimwiki emits no `<input>`,
|
||||
// so neither do we — an input would double-render against it.
|
||||
let class = match state {
|
||||
CheckboxState::Empty => "done0",
|
||||
CheckboxState::Quarter => "done1",
|
||||
CheckboxState::Half => "done2",
|
||||
CheckboxState::ThreeQuarters => "done3",
|
||||
CheckboxState::Done => "done4",
|
||||
CheckboxState::Rejected => "rejected",
|
||||
};
|
||||
write!(
|
||||
w,
|
||||
"<li class=\"{class}\"><input type=\"checkbox\" disabled{checked}> "
|
||||
)?;
|
||||
write!(w, "<li class=\"{class}\">")?;
|
||||
}
|
||||
None => w.write_all(b"<li>")?,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user