mdbook_pagetoc/
lib.rs

1use mdbook_core::utils::fs;
2use mdbook_preprocessor::book::Book;
3use mdbook_preprocessor::errors::Result;
4use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
5use serde::Deserialize;
6use std::path::Path;
7use tracing::info;
8
9#[derive(Debug, Deserialize)]
10#[serde(default)]
11struct PagetocConfig {
12    scroll_offset: i32,
13}
14
15impl Default for PagetocConfig {
16    fn default() -> Self {
17        Self { scroll_offset: 10 }
18    }
19}
20
21pub struct PagetocPreprocessor;
22
23impl Default for PagetocPreprocessor {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl PagetocPreprocessor {
30    pub fn new() -> PagetocPreprocessor {
31        PagetocPreprocessor
32    }
33}
34
35impl Preprocessor for PagetocPreprocessor {
36    fn name(&self) -> &str {
37        "mdbook-pagetoc"
38    }
39
40    fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book> {
41        let html_config = ctx.config.html_config().unwrap_or_default();
42
43        let config: PagetocConfig = ctx
44            .config
45            .get("preprocessor.pagetoc")
46            .ok()
47            .flatten()
48            .unwrap_or_default();
49
50        let pagetoc_js = include_str!("pagetoc.js")
51            .replace("{{SCROLL_OFFSET}}", &config.scroll_offset.to_string());
52        let pagetoc_css = include_str!("pagetoc.css");
53
54        let theme_dir = ctx
55            .root
56            .join(html_config.theme.as_deref().unwrap_or(Path::new("theme")));
57
58        for (name, contents) in [
59            ("pagetoc.js", pagetoc_js.as_str()),
60            ("pagetoc.css", pagetoc_css),
61        ] {
62            let path = theme_dir.join(name);
63            if !path.exists() {
64                info!("Writing {}", path.display());
65                fs::write(&path, contents)?;
66            }
67        }
68        Ok(book)
69    }
70
71    fn supports_renderer(&self, renderer: &str) -> Result<bool> {
72        Ok(renderer == "html")
73    }
74}