Some checks failed
CI / Check typos (push) Successful in 15s
CI / Check links (push) Failing after 1m37s
CI / Clippy (push) Successful in 3m44s
CI / Build and test (push) Successful in 12m33s
CI / Build container (push) Successful in 10m24s
CI / Deploy on waypoint (push) Successful in 49s
213 lines
4.4 KiB
Rust
213 lines
4.4 KiB
Rust
use chrono::TimeDelta;
|
|
use maud::{Markup, PreEscaped, html};
|
|
use reqwest::StatusCode;
|
|
use servable::{HtmlPage, PageMetadata, RenderContext};
|
|
use std::sync::LazyLock;
|
|
|
|
use crate::{
|
|
components::{
|
|
fa::FAIcon,
|
|
md::{Markdown, meta_from_markdown},
|
|
misc::FarLink,
|
|
},
|
|
routes::{IMG_ICON, MAIN_CSS},
|
|
};
|
|
|
|
mod handouts;
|
|
mod index;
|
|
|
|
pub use handouts::HANDOUTS;
|
|
pub use index::INDEX;
|
|
|
|
//
|
|
// MARK: md
|
|
//
|
|
|
|
fn page_from_markdown(md: impl Into<String>, default_image: Option<String>) -> HtmlPage {
|
|
let md: String = md.into();
|
|
let md = Markdown::parse(&md);
|
|
|
|
let mut meta = meta_from_markdown(&md)
|
|
.unwrap_or(Some(PageMetadata {
|
|
title: "Invalid frontmatter!".into(),
|
|
..Default::default()
|
|
}))
|
|
.unwrap_or_default();
|
|
|
|
if meta.image.is_none() {
|
|
meta.image = default_image
|
|
}
|
|
|
|
let html = PreEscaped(md.render());
|
|
|
|
HtmlPage::default()
|
|
.with_script_inline(LAZY_IMAGE_JS)
|
|
.with_style_linked(MAIN_CSS.route())
|
|
.with_meta(meta)
|
|
.with_render(move |_page, ctx| {
|
|
let html = html.clone();
|
|
|
|
Box::pin(async move {
|
|
html! {
|
|
div class="wrapper" style="margin-top:3ex;" {
|
|
@if let Some(backlinks) = backlinks(ctx) {
|
|
(backlinks)
|
|
}
|
|
|
|
(html)
|
|
|
|
(footer())
|
|
}
|
|
}
|
|
})
|
|
})
|
|
.with_ttl(Some(TimeDelta::days(1)))
|
|
}
|
|
|
|
//
|
|
// MARK: components
|
|
//
|
|
|
|
const LAZY_IMAGE_JS: &str = "
|
|
window.onload = function() {
|
|
var imgs = document.querySelectorAll('.img-placeholder');
|
|
|
|
imgs.forEach(img => {
|
|
img.style.border = 'none';
|
|
img.style.filter = 'blur(10px)';
|
|
img.style.transition = 'filter 0.3s';
|
|
|
|
var lg = new Image();
|
|
lg.src = img.dataset.large;
|
|
lg.onload = function () {
|
|
img.src = img.dataset.large;
|
|
img.style.filter = 'blur(0px)';
|
|
};
|
|
})
|
|
}
|
|
";
|
|
|
|
/*
|
|
const MAIN_TEMPLATE: PageTemplate = PageTemplate {
|
|
// Order matters, base htmx goes first
|
|
scripts: &[
|
|
ScriptSource::Linked(&"/assets/htmx-2.0.8.js"),
|
|
ScriptSource::Linked(&"/assets/htmx-json-1.19.12.js"),
|
|
],
|
|
|
|
extra_meta: &[(
|
|
"viewport",
|
|
"width=device-width,initial-scale=1,user-scalable=no",
|
|
)],
|
|
};
|
|
*/
|
|
|
|
pub fn backlinks(ctx: &RenderContext) -> Option<Markup> {
|
|
let mut backlinks = vec![("/", "home")];
|
|
|
|
let mut segments = ctx.route.split("/").skip(1).collect::<Vec<_>>();
|
|
let last = segments.pop();
|
|
|
|
let mut end = 0;
|
|
for s in segments {
|
|
end += s.len();
|
|
backlinks.push((&ctx.route[0..=end], s));
|
|
end += 1; // trailing slash
|
|
}
|
|
|
|
last.map(|last| {
|
|
html! {
|
|
div {
|
|
@for (url, text) in backlinks {
|
|
a href=(url) style="padding-left:5pt;padding-right:5pt;" { (text) }
|
|
"/"
|
|
}
|
|
|
|
span style="color:var(--metaColor);padding-left:5pt;padding-right:5pt;" { (last) }
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn footer() -> Markup {
|
|
html!(
|
|
footer style="margin-top:10rem;" {
|
|
hr class = "footline";
|
|
div class = "footContainer" {
|
|
p {
|
|
"This site was built by hand with "
|
|
(FarLink("https://rust-lang.org", "Rust"))
|
|
", "
|
|
(FarLink("https://maud.lambda.xyz", "Maud"))
|
|
", and "
|
|
(FarLink("https://docs.rs/axum/latest/axum", "Axum"))
|
|
". "
|
|
|
|
(
|
|
FarLink(
|
|
"https://git.betalupi.com/Mark/webpage",
|
|
html!(
|
|
(FAIcon::Git)
|
|
"Source here!"
|
|
)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
//
|
|
// MARK: pages
|
|
//
|
|
|
|
pub const LINKS: LazyLock<HtmlPage> = LazyLock::new(|| {
|
|
/*
|
|
Dead links:
|
|
|
|
https://www.commitstrip.com/en/
|
|
http://www.3dprintmath.com/
|
|
*/
|
|
|
|
page_from_markdown(include_str!("links.md"), Some(IMG_ICON.route().into()))
|
|
});
|
|
|
|
pub const BETALUPI: LazyLock<HtmlPage> = LazyLock::new(|| {
|
|
page_from_markdown(include_str!("betalupi.md"), Some(IMG_ICON.route().into()))
|
|
});
|
|
|
|
pub const HTWAH_TYPESETTING: LazyLock<HtmlPage> = LazyLock::new(|| {
|
|
page_from_markdown(
|
|
include_str!("htwah-typesetting.md"),
|
|
Some(IMG_ICON.route().into()),
|
|
)
|
|
});
|
|
|
|
pub static NOT_FOUND: LazyLock<HtmlPage> = LazyLock::new(|| {
|
|
HtmlPage::default()
|
|
.with_style_linked(MAIN_CSS.route())
|
|
.with_meta(PageMetadata {
|
|
title: "Page not found".into(),
|
|
author: None,
|
|
description: None,
|
|
image: Some(IMG_ICON.route().into()),
|
|
})
|
|
.with_render(
|
|
move |_page, _ctx| {
|
|
Box::pin(async {
|
|
html! {
|
|
div class="wrapper" {
|
|
div style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh" {
|
|
p style="font-weight:bold;font-size:50pt;margin:0;" { "404" }
|
|
p style="font-size:13pt;margin:0;color:var(--grey);" { "(page not found)" }
|
|
a style="font-size:12pt;margin:10pt;padding:5px;" href="/" {"<- Back to site"}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
)
|
|
.with_code(StatusCode::NOT_FOUND)
|
|
});
|