From 92ebc29dafae75ab918ced3d486e227258f33e76 Mon Sep 17 00:00:00 2001 From: lqdev Date: Mon, 28 Aug 2023 21:14:51 +0200 Subject: [PATCH] add page breadcrumbs --- content/about-treehouse.tree | 2 +- content/{about => about-treehouse}/emoji.tree | 0 crates/treehouse/src/cli/generate.rs | 75 +++++++++++++------ crates/treehouse/src/html/breadcrumbs.rs | 39 ++++++++++ crates/treehouse/src/html/mod.rs | 1 + static/css/main.css | 1 + static/css/tree.css | 55 ++++++++++++++ static/svg/dark/breadcrumb.svg | 3 + static/svg/light/breadcrumb.svg | 3 + template/tree.hbs | 6 ++ 10 files changed, 162 insertions(+), 23 deletions(-) rename content/{about => about-treehouse}/emoji.tree (100%) create mode 100644 crates/treehouse/src/html/breadcrumbs.rs create mode 100644 static/svg/dark/breadcrumb.svg create mode 100644 static/svg/light/breadcrumb.svg diff --git a/content/about-treehouse.tree b/content/about-treehouse.tree index bcdda9c..f0e3771 100644 --- a/content/about-treehouse.tree +++ b/content/about-treehouse.tree @@ -25,7 +25,7 @@ % id = "01H8VWEFHZA94G0DNPD79YV535" + … - % content.link = "about/emoji" + % content.link = "about-treehouse/emoji" id = "01H8VWEFHZ7Z71WJ347WFMC9YT" + by the way did you know this website has custom emojis? and quite a lot of them, too diff --git a/content/about/emoji.tree b/content/about-treehouse/emoji.tree similarity index 100% rename from content/about/emoji.tree rename to content/about-treehouse/emoji.tree diff --git a/crates/treehouse/src/cli/generate.rs b/crates/treehouse/src/cli/generate.rs index 2ebff7b..da3998a 100644 --- a/crates/treehouse/src/cli/generate.rs +++ b/crates/treehouse/src/cli/generate.rs @@ -21,7 +21,11 @@ use walkdir::WalkDir; use crate::{ cli::parse::parse_tree_with_diagnostics, config::Config, - html::{navmap::build_navigation_map, tree::branches_to_html}, + html::{ + breadcrumbs::breadcrumbs_to_html, + navmap::{build_navigation_map, NavigationMap}, + tree::branches_to_html, + }, tree::SemaRoots, }; @@ -34,6 +38,12 @@ struct Generator { tree_files: Vec, } +struct ParsedTree { + tree_path: String, + file_id: FileId, + target_path: PathBuf, +} + impl Generator { fn add_directory_rec(&mut self, directory: &Path) -> anyhow::Result<()> { for entry in WalkDir::new(directory) { @@ -98,22 +108,8 @@ impl Generator { Ok(()) } - fn generate_all_files(&self, config: &Config, paths: &Paths<'_>) -> anyhow::Result { + fn parse_trees(&self, paths: &Paths<'_>) -> anyhow::Result<(Treehouse, Vec)> { let mut treehouse = Treehouse::new(); - - let mut handlebars = Handlebars::new(); - let tree_template = Self::register_template( - &mut handlebars, - &mut treehouse, - "tree", - &paths.template_dir.join("tree.hbs"), - )?; - - struct ParsedTree { - tree_path: String, - file_id: FileId, - target_path: PathBuf, - } let mut parsed_trees = vec![]; for path in &self.tree_files { @@ -155,7 +151,28 @@ impl Generator { } } + Ok((treehouse, parsed_trees)) + } + + fn generate_all_files( + &self, + treehouse: &mut Treehouse, + config: &Config, + paths: &Paths<'_>, + navigation_map: &NavigationMap, + parsed_trees: impl IntoIterator, + ) -> anyhow::Result<()> { + let mut handlebars = Handlebars::new(); + let tree_template = Self::register_template( + &mut handlebars, + treehouse, + "tree", + &paths.template_dir.join("tree.hbs"), + )?; + for parsed_tree in parsed_trees { + let breadcrumbs = breadcrumbs_to_html(config, navigation_map, &parsed_tree.tree_path); + let mut tree = String::new(); // Temporarily steal the tree out of the treehouse. let roots = treehouse @@ -164,7 +181,7 @@ impl Generator { .expect("tree should have been added to the treehouse"); branches_to_html( &mut tree, - &mut treehouse, + treehouse, config, parsed_tree.file_id, &roots.branches, @@ -174,15 +191,20 @@ impl Generator { #[derive(Serialize)] pub struct TemplateData<'a> { pub config: &'a Config, + pub breadcrumbs: String, pub tree: String, } + let template_data = TemplateData { + config, + breadcrumbs, + tree, + }; - let template_data = TemplateData { config, tree }; let templated_html = match handlebars.render("tree", &template_data) { Ok(html) => html, Err(error) => { Self::wrangle_handlebars_error_into_diagnostic( - &mut treehouse, + treehouse, tree_template, error.line_no, error.column_no, @@ -201,7 +223,7 @@ impl Generator { std::fs::write(parsed_tree.target_path, templated_html)?; } - Ok(treehouse) + Ok(()) } } @@ -220,10 +242,10 @@ pub fn regenerate(paths: &Paths<'_>) -> anyhow::Result<()> { info!("copying static directory to target directory"); copy_dir(paths.static_dir, paths.target_dir.join("static"))?; - info!("generating standalone pages"); + info!("parsing tree"); let mut generator = Generator::default(); generator.add_directory_rec(paths.content_dir)?; - let treehouse = generator.generate_all_files(&config, paths)?; + let (mut treehouse, parsed_trees) = generator.parse_trees(paths)?; info!("generating navigation map"); let navigation_map = build_navigation_map(&treehouse, "index"); @@ -232,6 +254,15 @@ pub fn regenerate(paths: &Paths<'_>) -> anyhow::Result<()> { navigation_map.to_javascript(), )?; + info!("generating standalone pages"); + generator.generate_all_files( + &mut treehouse, + &config, + paths, + &navigation_map, + parsed_trees, + )?; + treehouse.report_diagnostics()?; let duration = start.elapsed(); diff --git a/crates/treehouse/src/html/breadcrumbs.rs b/crates/treehouse/src/html/breadcrumbs.rs new file mode 100644 index 0000000..d64f5ba --- /dev/null +++ b/crates/treehouse/src/html/breadcrumbs.rs @@ -0,0 +1,39 @@ +use std::{borrow::Cow, fmt::Write}; + +use crate::config::Config; + +use super::{navmap::NavigationMap, EscapeAttribute}; + +pub fn breadcrumbs_to_html( + config: &Config, + navigation_map: &NavigationMap, + tree_path: &str, +) -> String { + let mut s = String::new(); + + if let Some(path) = navigation_map.paths.get(tree_path) { + for (i, element) in path.iter().enumerate() { + // Skip the index because it's implied by the logo on the left. + if element != "index" { + s.push_str("
  • "); + { + let element = path + .get(i - 1) + .map(|p| format!("{p}/")) + .and_then(|prefix| element.strip_prefix(prefix.as_str()).map(Cow::Borrowed)) + .unwrap_or_else(|| Cow::Owned(format!("/{element}"))); + write!( + s, + "{element}", + site = EscapeAttribute(&config.site), + element = EscapeAttribute(&element) + ) + .unwrap(); + } + s.push_str("
  • "); + } + } + } + + s +} diff --git a/crates/treehouse/src/html/mod.rs b/crates/treehouse/src/html/mod.rs index 938568a..78a78d7 100644 --- a/crates/treehouse/src/html/mod.rs +++ b/crates/treehouse/src/html/mod.rs @@ -1,5 +1,6 @@ use std::fmt::{self, Display, Write}; +pub mod breadcrumbs; mod markdown; pub mod navmap; pub mod tree; diff --git a/static/css/main.css b/static/css/main.css index a674c79..8afb27b 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -257,6 +257,7 @@ th { nav { display: flex; + align-items: center; } nav .logo { diff --git a/static/css/tree.css b/static/css/tree.css index fb2b4cd..dd4555b 100644 --- a/static/css/tree.css +++ b/static/css/tree.css @@ -1,3 +1,52 @@ +/*** Breadcrumbs ***/ + +.breadcrumbs { + list-style-type: none; + display: flex; + flex-direction: row; + flex-wrap: wrap; + + height: min-content; + + margin: 0; + padding: 0; + + align-items: center; + + opacity: 70%; +} + +.breadcrumb::before { + content: ''; + display: inline-block; + + background-image: + /* breadcrumb */ + url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTYgMTIgNC00LTQtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNTU0MjNlIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4='); + background-repeat: no-repeat; + background-position: 50% 50%; + opacity: 70%; + + width: 32px; + height: 1.2em; + + vertical-align: text-bottom; +} + +.breadcrumb a { + --recursive-mono: 1.0; + --recursive-wght: 500; + + color: var(--text-color); + text-decoration: none; +} + +.breadcrumb a:hover { + text-decoration: underline; +} + +/*** Tree ***/ + .tree ul { padding-left: clamp(12px, 2vw, 24px); } @@ -195,6 +244,12 @@ } @media (prefers-color-scheme: dark) { + .breadcrumb::before { + background-image: + /* breadcrumb */ + url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTYgMTIgNC00LTQtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDdjZGJmIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=') + } + .tree details>summary { background-image: /* expand */ diff --git a/static/svg/dark/breadcrumb.svg b/static/svg/dark/breadcrumb.svg new file mode 100644 index 0000000..0e5fd08 --- /dev/null +++ b/static/svg/dark/breadcrumb.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/svg/light/breadcrumb.svg b/static/svg/light/breadcrumb.svg new file mode 100644 index 0000000..c5b39a9 --- /dev/null +++ b/static/svg/light/breadcrumb.svg @@ -0,0 +1,3 @@ + + + diff --git a/template/tree.hbs b/template/tree.hbs index 6fb6382..f818a81 100644 --- a/template/tree.hbs +++ b/template/tree.hbs @@ -33,6 +33,12 @@ fill="currentColor" /> + + {{#if breadcrumbs}} + + {{/if}}