add page breadcrumbs
This commit is contained in:
parent
5b8f100121
commit
92ebc29daf
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
39
crates/treehouse/src/html/breadcrumbs.rs
Normal file
39
crates/treehouse/src/html/breadcrumbs.rs
Normal 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
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use std::fmt::{self, Display, Write};
|
||||
|
||||
pub mod breadcrumbs;
|
||||
mod markdown;
|
||||
pub mod navmap;
|
||||
pub mod tree;
|
||||
|
|
|
@ -257,6 +257,7 @@ th {
|
|||
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
nav .logo {
|
||||
|
|
|
@ -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 */
|
||||
|
|
3
static/svg/dark/breadcrumb.svg
Normal file
3
static/svg/dark/breadcrumb.svg
Normal 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 |
3
static/svg/light/breadcrumb.svg
Normal file
3
static/svg/light/breadcrumb.svg
Normal 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 |
|
@ -33,6 +33,12 @@
|
|||
fill="currentColor" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
{{#if breadcrumbs}}
|
||||
<ol class="breadcrumbs">
|
||||
{{{ breadcrumbs }}}
|
||||
</ol>
|
||||
{{/if}}
|
||||
</nav>
|
||||
|
||||
<noscript>
|
||||
|
|
Loading…
Reference in a new issue