From 0594aa4323ce1bfa99fa8a0ac129efdd4cae23bf Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Sat, 15 Jun 2024 05:58:31 +0800 Subject: [PATCH] feat: support basic theming (#71) --- cli/src/lib.rs | 7 ++++ cli/src/project.rs | 58 +++++++++++++++----------------- cli/src/render/html.rs | 10 +----- cli/src/theme.rs | 73 ++++++++++++++++++++++++++++++++++++++--- themes/mdbook/index.hbs | 20 +++++------ 5 files changed, 113 insertions(+), 55 deletions(-) diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 27acdbd..c8852ca 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -67,6 +67,13 @@ pub struct CompileArgs { #[clap(long, default_value = "/")] pub path_to_root: String, + /// Specify a theme directory to copy recursively. + /// + /// The files will be copied to the `theme/` in the output + /// directory. + #[clap(long)] + pub theme: Option, + /// Add additional directories to search for fonts #[clap( long = "font-path", diff --git a/cli/src/project.rs b/cli/src/project.rs index 426f50a..5cd64de 100644 --- a/cli/src/project.rs +++ b/cli/src/project.rs @@ -10,7 +10,8 @@ use crate::{ error::prelude::*, meta::{BookMeta, BookMetaContent, BookMetaElem, BuildMeta}, render::{DataDict, HtmlRenderer, TypstRenderer}, - utils::{copy_dir_embedded, create_dirs, release_packages, write_file}, + theme::Theme, + utils::{create_dirs, release_packages, write_file}, CompileArgs, }; use include_dir::include_dir; @@ -44,8 +45,10 @@ impl fmt::Display for JsonContent { } pub struct Project { + pub theme: Theme, pub tr: TypstRenderer, pub hr: HtmlRenderer, + pub book_meta: Option, pub build_meta: Option, @@ -70,13 +73,21 @@ impl Project { args.workspace.clone_from(&args.dir); } + let theme = match &args.theme { + Some(theme) => Theme::new(Path::new(theme))?, + None => Theme::default(), + }; + let tr = TypstRenderer::new(args); - let hr = HtmlRenderer::new(); + let hr = HtmlRenderer::new(&theme); let mut proj = Self { dest_dir: tr.dest_dir.clone(), + + theme, tr, hr, + book_meta: None, build_meta: None, path_to_root, @@ -212,46 +223,29 @@ impl Project { pub fn build(&mut self) -> ZResult<()> { let mut write_index = false; - create_dirs(&self.dest_dir)?; - copy_dir_embedded( - include_dir!("$CARGO_MANIFEST_DIR/../themes/mdbook/css"), - self.dest_dir.join("css"), - )?; - copy_dir_embedded( - include_dir!("$CARGO_MANIFEST_DIR/../themes/mdbook/FontAwesome/css"), - self.dest_dir.join("FontAwesome/css"), - )?; - copy_dir_embedded( - include_dir!("$CARGO_MANIFEST_DIR/../themes/mdbook/FontAwesome/fonts"), - self.dest_dir.join("FontAwesome/fonts"), - )?; + let themes = self.dest_dir.join("theme"); - // todo use themes in filesystem - // copy_dir_all("themes/mdbook/css", self.dest_dir.join("css")).unwrap(); - // copy_dir_all( - // "themes/mdbook/fontAwesome", - // self.dest_dir.join("fontAwesome"), - // ) - // .unwrap(); + // Always update the theme if it is static + // Or copy on first build + if self.theme.is_static() || !themes.exists() { + log::info!("copying theme assets to {:?}", themes); + self.theme.copy_assets(&themes)?; + } - // copy files - create_dirs(self.dest_dir.join("renderer"))?; + // copy internal files + create_dirs(self.dest_dir.join("internal"))?; write_file( - self.dest_dir.join("renderer/typst_ts_renderer_bg.wasm"), + self.dest_dir.join("internal/typst_ts_renderer_bg.wasm"), include_bytes!("../../assets/artifacts/typst_ts_renderer_bg.wasm"), )?; write_file( - self.dest_dir.join("svg_utils.js"), + self.dest_dir.join("internal/svg_utils.js"), include_bytes!("../../assets/artifacts/svg_utils.cjs"), )?; write_file( - self.dest_dir.join("typst-book.js"), + self.dest_dir.join("internal/typst-book.js"), include_bytes!("../../assets/artifacts/book.mjs"), )?; - write_file( - self.dest_dir.join("index.js"), - include_bytes!("../../themes/mdbook/index.js"), - )?; for ch in self.iter_chapters() { if let Some(path) = ch.get("path") { @@ -386,7 +380,7 @@ impl Project { // inject chapters data.insert("chapters".to_owned(), json!(self.iter_chapters())); - let renderer_module = format!("{}renderer/typst_ts_renderer_bg.wasm", self.path_to_root); + let renderer_module = format!("{}internal/typst_ts_renderer_bg.wasm", self.path_to_root); data.insert("renderer_module".to_owned(), json!(renderer_module)); // inject content diff --git a/cli/src/render/html.rs b/cli/src/render/html.rs index c47f85d..ae1311f 100644 --- a/cli/src/render/html.rs +++ b/cli/src/render/html.rs @@ -10,10 +10,8 @@ pub struct HtmlRenderer { } impl HtmlRenderer { - pub fn new() -> Self { + pub fn new(theme: &theme::Theme) -> Self { let mut handlebars = Handlebars::new(); - // todo std::path::Path::new("themes/mdbook") - let theme = theme::Theme::default(); debug!("Register the index handlebars template"); handlebars @@ -289,9 +287,3 @@ fn write_li_open_tag( li.push_str("\">"); out.write(&li) } - -impl Default for HtmlRenderer { - fn default() -> Self { - Self::new() - } -} diff --git a/cli/src/theme.rs b/cli/src/theme.rs index 4df385f..3dabae1 100644 --- a/cli/src/theme.rs +++ b/cli/src/theme.rs @@ -1,6 +1,21 @@ use std::{fs::File, io::Read, path::Path}; use log::warn; +use typst_ts_core::{error::prelude::*, ImmutPath}; + +use crate::utils::{self, copy_dir_embedded, write_file}; +use include_dir::include_dir; + +#[derive(Debug, PartialEq)] +pub enum EmbeddedThemeAsset { + MdBook, +} + +#[derive(Debug, PartialEq)] +pub enum ThemeAsset { + Static(EmbeddedThemeAsset), + Dir(ImmutPath), +} /// The `Theme` struct should be used instead of the static variables because /// the `new()` method will look if the user has a theme directory in their @@ -12,6 +27,8 @@ use log::warn; pub struct Theme { pub index: Vec, pub typst_load_trampoline: Vec, + + asset: ThemeAsset, } impl Default for Theme { @@ -20,6 +37,7 @@ impl Default for Theme { index: include_bytes!("../../themes/mdbook/index.hbs").to_vec(), typst_load_trampoline: include_bytes!("../../themes/mdbook/typst-load-trampoline.hbs") .to_vec(), + asset: ThemeAsset::Static(EmbeddedThemeAsset::MdBook), } } } @@ -28,12 +46,18 @@ impl Theme { /// Creates a `Theme` from the given `theme_dir`. /// If a file is found in the theme dir, it will override the default /// version. - pub fn new(theme_dir: &Path) -> Self { - let mut theme = Self::default(); + pub fn new(theme_dir: &Path) -> ZResult { + let mut theme = Self { + asset: ThemeAsset::Dir(theme_dir.into()), + ..Default::default() + }; // If the theme directory doesn't exist there's no point continuing... if !theme_dir.exists() || !theme_dir.is_dir() { - panic!("Theme directory doesn't exist: {:?}", theme_dir); + return Err(error_once!( + "Theme directory doesn't exist", + theme_dir: theme_dir.display(), + )); } // Check for individual files, if they exist copy them across @@ -67,7 +91,48 @@ impl Theme { // let fonts_dir = theme_dir.join("fonts"); // ... - theme + Ok(theme) + } + + pub fn is_static(&self) -> bool { + matches!(self.asset, ThemeAsset::Static(_)) + } + + pub fn copy_assets(&self, dest_dir: &Path) -> ZResult<()> { + if !dest_dir.exists() { + log::debug!( + "{} does not exist, creating the directory", + dest_dir.display() + ); + utils::create_dirs(dest_dir)?; + } + + match &self.asset { + ThemeAsset::Static(EmbeddedThemeAsset::MdBook) => { + copy_dir_embedded( + include_dir!("$CARGO_MANIFEST_DIR/../themes/mdbook/css"), + dest_dir.join("css"), + )?; + copy_dir_embedded( + include_dir!("$CARGO_MANIFEST_DIR/../themes/mdbook/FontAwesome/css"), + dest_dir.join("FontAwesome/css"), + )?; + copy_dir_embedded( + include_dir!("$CARGO_MANIFEST_DIR/../themes/mdbook/FontAwesome/fonts"), + dest_dir.join("FontAwesome/fonts"), + )?; + write_file( + dest_dir.join("index.js"), + include_bytes!("../../themes/mdbook/index.js"), + )?; + } + ThemeAsset::Dir(theme_dir) => { + utils::copy_dir_all(theme_dir, dest_dir) + .map_err(error_once_map!("copy_theme_directory"))?; + } + } + + Ok(()) } } diff --git a/themes/mdbook/index.hbs b/themes/mdbook/index.hbs index 5205e42..1175ef3 100644 --- a/themes/mdbook/index.hbs +++ b/themes/mdbook/index.hbs @@ -26,17 +26,17 @@ {{#if favicon_png}} {{/if}} - - - + + + {{#if print_enable}} - + {{/if}} - + {{#if copy_fonts}} - + {{/if}} @@ -80,7 +80,7 @@ {{!-- window.captureStack = function() { return new Error(); } --}} - + {{!-- --}} + {{!-- --}} - +