Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support basic theming #71

Merged
merged 1 commit into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,

/// Add additional directories to search for fonts
#[clap(
long = "font-path",
Expand Down
58 changes: 26 additions & 32 deletions cli/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<BookMeta>,
pub build_meta: Option<BuildMeta>,

Expand All @@ -70,13 +73,21 @@ impl Project {
args.workspace = args.dir.clone();
}

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,
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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
Expand Down
10 changes: 1 addition & 9 deletions cli/src/render/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -289,9 +287,3 @@ fn write_li_open_tag(
li.push_str("\">");
out.write(&li)
}

impl Default for HtmlRenderer {
fn default() -> Self {
Self::new()
}
}
73 changes: 69 additions & 4 deletions cli/src/theme.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -12,6 +27,8 @@ use log::warn;
pub struct Theme {
pub index: Vec<u8>,
pub typst_load_trampoline: Vec<u8>,

asset: ThemeAsset,
}

impl Default for Theme {
Expand All @@ -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),
}
}
}
Expand All @@ -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<Self> {
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
Expand Down Expand Up @@ -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(())
}
}

Expand Down
20 changes: 10 additions & 10 deletions themes/mdbook/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@
{{#if favicon_png}}
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
{{/if}}
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
<link rel="stylesheet" href="{{ path_to_root }}theme/css/variables.css">
<link rel="stylesheet" href="{{ path_to_root }}theme/css/general.css">
<link rel="stylesheet" href="{{ path_to_root }}theme/css/chrome.css">
{{#if print_enable}}
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
<link rel="stylesheet" href="{{ path_to_root }}theme/css/print.css" media="print">
{{/if}}

<!-- Fonts -->
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="{{ path_to_root }}theme/FontAwesome/css/font-awesome.css">
{{#if copy_fonts}}
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
<link rel="stylesheet" href="{{ path_to_root }}theme/fonts/fonts.css">
{{/if}}

<!-- Custom theme stylesheets -->
Expand Down Expand Up @@ -80,7 +80,7 @@
{{!-- window.captureStack = function() { return new Error(); } --}}
</script>

<script id="typst-book-js" type="module" src="{{ path_to_root }}typst-book.js"></script>
<script id="typst-book-js" type="module" src="{{ path_to_root }}internal/typst-book.js"></script>
{{!-- <script id="typst-book-js" type="module" src="/dev/frontend/dist/book.mjs"></script> --}}
<script>
window.typstRerender = () => { };
Expand Down Expand Up @@ -237,7 +237,7 @@

<div class="right-buttons">
{{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
<a href="{{ path_to_root }}theme/print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
{{/if}}
Expand Down Expand Up @@ -322,9 +322,9 @@

</div>

<script src="{{ path_to_root }}svg_utils.js"></script>
<script src="{{ path_to_root }}internal/svg_utils.js"></script>
{{!-- <script src="/dev/frontend/src/svg_utils.cjs"></script> --}}
<script src="{{ path_to_root }}index.js"></script>
<script src="{{ path_to_root }}theme/index.js"></script>

</div>
</body>
Expand Down
Loading