fix(toc): honour toc_header/level in single-wiki config + div.toc on export
Two TOC bugs: 1. :NuwikiTOC ignored toc_header / toc_header_level (always "= Contents =" at level 1). In the single-wiki shorthand the server only carried file_extension/syntax from the top-level options into the synthesized wiki; every other per-wiki key (toc_header, toc_header_level, links_header, html_path, auto_export, …) was dropped. Add single_wiki_from_value(), which re-parses the top-level object as a full RawWiki (injecting root from wiki_root), so all per-wiki keys set at the top level flow through. Wired into from_init_params + apply_change. 2. HTML export put class="toc" on the <hN> element, but upstream vimwiki (and its stylesheet) wraps the TOC heading in <div class="toc">…</div>. render_heading now emits the div wrapper, matching upstream so .toc CSS applies. With (1) fixed, detection also works for a custom toc_header. Tests: single_wiki_shorthand_honours_top_level_per_wiki_keys (config), toc_header_heading_wrapped_in_div_toc + non_toc_heading_not_wrapped (renderer). Full suite 572 passed; clippy clean; config-parity goldens match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -60,8 +60,9 @@ pub struct HtmlRenderer {
|
||||
/// section number (e.g. `.` → `1.2. Heading`). Empty by default.
|
||||
header_numbering_sym: String,
|
||||
/// vimwiki `toc_header`: the heading text that identifies the TOC
|
||||
/// section. When a heading matches this value (case-insensitive), it
|
||||
/// gets `class="toc"` in HTML export.
|
||||
/// section. A heading matching this value (case-insensitive) is wrapped
|
||||
/// in `<div class="toc">…</div>` on export, matching upstream so vimwiki's
|
||||
/// stylesheet applies.
|
||||
toc_header: String,
|
||||
/// vimwiki `valid_html_tags`: inline HTML tag names passed through to
|
||||
/// export verbatim (instead of escaping `<`/`>`). Empty = escape all.
|
||||
@@ -327,25 +328,18 @@ impl HtmlRenderer {
|
||||
fn render_heading(&self, n: &HeadingNode, number: &str, w: &mut dyn Write) -> io::Result<()> {
|
||||
let level = n.level.clamp(1, 6);
|
||||
let anchor = crate::ast::inline_text(&n.children);
|
||||
let is_toc = self
|
||||
.toc_header
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.zip(anchor.as_bytes())
|
||||
.all(|(a, b)| a.eq_ignore_ascii_case(b))
|
||||
&& !self.toc_header.is_empty()
|
||||
&& self.toc_header.len() == anchor.len();
|
||||
let mut classes = Vec::new();
|
||||
// The TOC section heading (vimwiki `toc_header`) is wrapped in a
|
||||
// `<div class="toc">`, matching upstream's HTML so vimwiki's stylesheet
|
||||
// (`.toc { … }`) applies. The class goes on the wrapping div, not the
|
||||
// heading element.
|
||||
let is_toc = !self.toc_header.is_empty() && anchor.eq_ignore_ascii_case(&self.toc_header);
|
||||
if is_toc {
|
||||
classes.push("toc");
|
||||
w.write_all(b"<div class=\"toc\">")?;
|
||||
}
|
||||
if n.centered {
|
||||
classes.push("centered");
|
||||
}
|
||||
let class = if classes.is_empty() {
|
||||
String::new()
|
||||
let class = if n.centered {
|
||||
" class=\"centered\""
|
||||
} else {
|
||||
format!(" class=\"{}\"", classes.join(" "))
|
||||
""
|
||||
};
|
||||
write!(w, "<h{level}{class} id=\"")?;
|
||||
write_escaped(&anchor, w)?;
|
||||
@@ -354,7 +348,11 @@ impl HtmlRenderer {
|
||||
write_escaped(number, w)?;
|
||||
}
|
||||
self.render_inlines(&n.children, w)?;
|
||||
writeln!(w, "</h{level}>")
|
||||
write!(w, "</h{level}>")?;
|
||||
if is_toc {
|
||||
w.write_all(b"</div>")?;
|
||||
}
|
||||
writeln!(w)
|
||||
}
|
||||
|
||||
fn render_paragraph(&self, n: &ParagraphNode, w: &mut dyn Write) -> io::Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user