add page breadcrumbs

This commit is contained in:
liquidex 2023-08-28 21:14:51 +02:00
parent 5b8f100121
commit 92ebc29daf
10 changed files with 162 additions and 23 deletions

View file

@ -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

View file

@ -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<PathBuf>,
}
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<Treehouse> {
fn parse_trees(&self, paths: &Paths<'_>) -> anyhow::Result<(Treehouse, Vec<ParsedTree>)> {
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<Item = ParsedTree>,
) -> 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();

View file

@ -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("<li class=\"breadcrumb\">");
{
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,
"<a href=\"{site}/{element}.html\">{element}</a>",
site = EscapeAttribute(&config.site),
element = EscapeAttribute(&element)
)
.unwrap();
}
s.push_str("</li>");
}
}
}
s
}

View file

@ -1,5 +1,6 @@
use std::fmt::{self, Display, Write};
pub mod breadcrumbs;
mod markdown;
pub mod navmap;
pub mod tree;

View file

@ -257,6 +257,7 @@ th {
nav {
display: flex;
align-items: center;
}
nav .logo {

View file

@ -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 */

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
<path d="M6 12L10 8L6 4" fill="none" stroke="#d7cdbf" stroke-width="2" />
</svg>

After

Width:  |  Height:  |  Size: 149 B

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
<path d="M6 12L10 8L6 4" fill="none" stroke="#55423e" stroke-width="2" />
</svg>

After

Width:  |  Height:  |  Size: 149 B

View file

@ -33,6 +33,12 @@
fill="currentColor" />
</svg>
</a>
{{#if breadcrumbs}}
<ol class="breadcrumbs">
{{{ breadcrumbs }}}
</ol>
{{/if}}
</nav>
<noscript>