From 14d8a9b00cb2522ee0efc3aa3d42b8ef8e829724 Mon Sep 17 00:00:00 2001 From: rm-dr <96270320+rm-dr@users.noreply.github.com> Date: Sun, 2 Nov 2025 10:56:29 -0800 Subject: [PATCH] Page base --- crates/service-webpage/src/components/base.rs | 87 ++++ crates/service-webpage/src/components/mod.rs | 1 + crates/service-webpage/src/routes/betalupi.rs | 86 +--- crates/service-webpage/src/routes/index.rs | 479 ++++++++---------- crates/service-webpage/src/routes/links.rs | 96 ++-- 5 files changed, 349 insertions(+), 400 deletions(-) create mode 100644 crates/service-webpage/src/components/base.rs diff --git a/crates/service-webpage/src/components/base.rs b/crates/service-webpage/src/components/base.rs new file mode 100644 index 0000000..93790e7 --- /dev/null +++ b/crates/service-webpage/src/components/base.rs @@ -0,0 +1,87 @@ +use macro_sass::sass; +use maud::{DOCTYPE, Markup, PreEscaped, Render, html}; + +use crate::components::misc::FarLink; + +pub struct PageMetadata { + pub title: String, + pub author: Option, + pub description: Option, + pub image: Option, +} + +impl Render for PageMetadata { + fn render(&self) -> Markup { + let empty = String::new(); + let title = &self.title; + let author = &self.author.as_ref().unwrap_or(&empty); + let description = &self.description.as_ref().unwrap_or(&empty); + let image = &self.image.as_ref().unwrap_or(&empty); + + html!( + meta property="og:site_name" content=(title) {} + meta name="title" content=(title) {} + meta property="og:title" content=(title) {} + meta property="twitter:title" content=(title) {} + + meta name="author" content=(author) {} + + meta name="description" content=(description) {} + meta property="og:description" content=(description) {} + meta property="twitter:description" content=(description) {} + + + meta content=(image) property="og:image" {} + link rel="shortcut icon" href=(image) type="image/x-icon" {} + ) + } +} + +const CSS: &str = sass!("css/main.scss"); + +pub struct BasePage(pub PageMetadata, pub T); + +impl Render for BasePage { + fn render(&self) -> Markup { + let meta = &self.0; + + html! { + (DOCTYPE) + html { + head { + meta charset="UTF" {} + meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" {} + meta content="text/html; charset=UTF-8" http-equiv="content-type" {} + meta property="og:type" content="website" {} + + (meta) + title { (PreEscaped(meta.title.clone())) } + style { (PreEscaped(CSS)) } + } + + body { + div class="wrapper" { + main { (self.1) } + + footer { + hr class = "footline" {} + div class = "footContainer" { + p { + "This site was built by hand using " + (FarLink("https://rust-lang.org", "Rust")) + ", " + (FarLink("https://maud.lambda.xyz", "Maud")) + ", " + (FarLink("https://github.com/connorskees/grass", "Grass")) + ", and " + (FarLink("https://docs.rs/axum/latest/axum", "Axum")) + "." + } + } + } + } + } + } + } + } +} diff --git a/crates/service-webpage/src/components/mod.rs b/crates/service-webpage/src/components/mod.rs index bbc708c..bd279ec 100644 --- a/crates/service-webpage/src/components/mod.rs +++ b/crates/service-webpage/src/components/mod.rs @@ -1,3 +1,4 @@ +pub mod base; pub mod fa; pub mod mangle; pub mod md; diff --git a/crates/service-webpage/src/routes/betalupi.rs b/crates/service-webpage/src/routes/betalupi.rs index d583950..9844ced 100644 --- a/crates/service-webpage/src/routes/betalupi.rs +++ b/crates/service-webpage/src/routes/betalupi.rs @@ -1,17 +1,14 @@ use assetserver::Asset; -use macro_sass::sass; -use maud::{DOCTYPE, Markup, PreEscaped, html}; +use maud::{Markup, html}; use crate::{ - components::{md::Markdown, misc::FarLink}, - routes::{ - assets::{Image_Betalupi, Image_Icon}, - index::PageMetadata, + components::{ + base::{BasePage, PageMetadata}, + md::Markdown, }, + routes::assets::{Image_Betalupi, Image_Icon}, }; -const CSS: &str = sass!("css/main.scss"); - pub async fn betalupi() -> Markup { let meta = PageMetadata { title: "What's a \"betalupi?\"".into(), @@ -21,60 +18,27 @@ pub async fn betalupi() -> Markup { }; html! { - (DOCTYPE) - html { - head { - meta charset="UTF" {} - meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" {} - meta content="text/html; charset=UTF-8" http-equiv="content-type" {} - meta property="og:type" content="website" {} - - (meta) - title { (PreEscaped(meta.title)) } - style { (PreEscaped(CSS)) } - } - - body { - div class="wrapper" { - main { - - // TODO: no metadata class, generate backlink array - div { - a href="/" style="padding-left:4pt;padding-right:4pt;" {"home"} - "/" - span class="metaData" style="padding-left:4pt;padding-right:4pt;" { "whats-a-betalupi" } - } - - (Markdown(MD_A)) - - br {} - - (Markdown(MD_B)) - - br {} - - img alt="betalupi map" class="image" src=(Image_Betalupi::URL) {} - } - - footer { - hr class = "footline" {} - div class = "footContainer" { - p { - "This site was built by hand using " - (FarLink("https://rust-lang.org", "Rust")) - ", " - (FarLink("https://maud.lambda.xyz", "Maud")) - ", " - (FarLink("https://github.com/connorskees/grass", "Grass")) - ", and " - (FarLink("https://docs.rs/axum/latest/axum", "Axum")) - "." - } - } - } + (BasePage( + meta, + html!( + // TODO: no metadata class, generate backlink array + div { + a href="/" style="padding-left:4pt;padding-right:4pt;" {"home"} + "/" + span class="metaData" style="padding-left:4pt;padding-right:4pt;" { "whats-a-betalupi" } } - } - } + + (Markdown(MD_A)) + + br {} + + (Markdown(MD_B)) + + br {} + + img alt="betalupi map" class="image" src=(Image_Betalupi::URL) {} + ) + )) } } diff --git a/crates/service-webpage/src/routes/index.rs b/crates/service-webpage/src/routes/index.rs index da1f753..4362273 100644 --- a/crates/service-webpage/src/routes/index.rs +++ b/crates/service-webpage/src/routes/index.rs @@ -1,9 +1,9 @@ use assetserver::Asset; -use macro_sass::sass; -use maud::{DOCTYPE, Markup, PreEscaped, Render, html}; +use maud::{Markup, html}; use crate::{ components::{ + base::{BasePage, PageMetadata}, fa::FAIcon, mangle::{MangledBetaEmail, MangledGoogleEmail}, md::Markdown, @@ -12,43 +12,6 @@ use crate::{ routes::assets::{Image_Cover, Image_Icon}, }; -const CSS: &str = sass!("css/main.scss"); - -pub struct PageMetadata { - /// Text shown in tab - pub title: String, - pub author: Option, - pub description: Option, - pub image: Option, -} - -impl Render for PageMetadata { - fn render(&self) -> Markup { - let empty = String::new(); - let title = &self.title; - let author = &self.author.as_ref().unwrap_or(&empty); - let description = &self.description.as_ref().unwrap_or(&empty); - let image = &self.image.as_ref().unwrap_or(&empty); - - html!( - meta property="og:site_name" content=(title) {} - meta name="title" content=(title) {} - meta property="og:title" content=(title) {} - meta property="twitter:title" content=(title) {} - - meta name="author" content=(author) {} - - meta name="description" content=(description) {} - meta property="og:description" content=(description) {} - meta property="twitter:description" content=(description) {} - - - meta content=(image) property="og:image" {} - link rel="shortcut icon" href=(image) type="image/x-icon" {} - ) - } -} - pub async fn index() -> Markup { let meta = PageMetadata { title: "Betalupi: About".into(), @@ -58,258 +21,228 @@ pub async fn index() -> Markup { }; html! { - (DOCTYPE) - html { - head { - meta charset="UTF" {} - meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" {} - meta content="text/html; charset=UTF-8" http-equiv="content-type" {} - meta property="og:type" content="website" {} + (BasePage( + meta, + html!( + h2 id="about" { "About" } - (meta) - title { (PreEscaped(meta.title)) } - style { (PreEscaped(CSS)) } - } + div { + img + src=(Image_Cover::URL) + style="float:left;margin:10px 10px 10px 10px;display:block;width:25%;" + {} - body { - div class="wrapper" { - main { - h2 id="about" { "About" } + div style="margin:2ex 1ex 2ex 1ex;display:inline-block;overflow:hidden;width:60%;" { + "Welcome, you've reached Mark's main page. Here you'll find" + " links to various projects I've worked on." - div { - img - src=(Image_Cover::URL) - style="float:left;margin:10px 10px 10px 10px;display:block;width:25%;" - {} + ul { + li { (MangledBetaEmail {}) } + li { (MangledGoogleEmail {}) } - div style="margin:2ex 1ex 2ex 1ex;display:inline-block;overflow:hidden;width:60%;" { - "Welcome, you've reached Mark's main page. Here you'll find" - " links to various projects I've worked on." - - ul { - li { (MangledBetaEmail {}) } - li { (MangledGoogleEmail {}) } - - li { - ( - FarLink( - "https://github.com/rm-dr", - html!( - (FAIcon::Github) - "rm-dr" - ) - ) + li { + ( + FarLink( + "https://github.com/rm-dr", + html!( + (FAIcon::Github) + "rm-dr" ) - } - - li { - ( - FarLink( - "https://git.betalupi.com", - html!( - (FAIcon::Git) - "git.betalupi.com" - ) - ) - ) - } - } + ) + ) } - br style="clear:both;" {} + + li { + ( + FarLink( + "https://git.betalupi.com", + html!( + (FAIcon::Git) + "git.betalupi.com" + ) + ) + ) + } + } + } + br style="clear:both;" {} + } + + "Also see " + a href="/whats-a-betalupi" { "what's a \"betalupi?\"" } + + + (Markdown(concat!( + "## Pages\n", + " - [Handouts](/handouts): Math circle lessons I've written\n", + " - [Links](/links): Interesting parts of the internet" + ))) + + hr style="margin-top: 6rem; margin-bottom: 6rem" {} + + h2 { "Projects" } + ul { + li { + p { + b { "RedoxOS" } + ", a general-purpose, microkernel-based operating system written in Rust. " + + em { span style="color: var(--grey);" {"[enthusiast]"} } } - "Also see " - a href="/whats-a-betalupi" { "what's a \"betalupi?\"" } - - - (Markdown(concat!( - "## Pages\n", - " - [Handouts](/handouts): Math circle lessons I've written\n", - " - [Links](/links): Interesting parts of the internet" - ))) - - hr style="margin-top: 6rem; margin-bottom: 6rem" {} - - h2 { "Projects" } ul { li { - p { - b { "RedoxOS" } - ", a general-purpose, microkernel-based operating system written in Rust. " - - em { span style="color: var(--grey);" {"[enthusiast]"} } - } - - ul { - li { - span style="color: var(--grey);" {"Status: "} - span style="color: var(--yellow);" {"Passive"} - } - - - li { - span style="color: var(--grey);" {"Website: "} - ( - FarLink( - "https://www.redox-os.org", - html!( - (FAIcon::Link) - "redox-os.org" - ) - ) - ) - } - } - } - - li { - p { - b { "Tectonic" } - ", the LaTeX engine that is pleasant to use. Experimental, but fully functional. " - - em { span style="color: var(--grey);" {"[co-maintainer]"} } - } - - ul { - li { - span style="color: var(--grey);" {"Status: "} - span style="color: var(--yellow);" {"Passive. "} - (FarLink("https://github.com/typst/typst", "Typst")) - " is better." - } - - - li { - span style="color: var(--grey);" {"Main Repo: "} - ( - FarLink( - "https://github.com/tectonic-typesetting/tectonic", - html!( (FAIcon::Github) "Tectonic" ) - ) - ) - } - - li { - span style="color: var(--grey);" {"Bundle Tools: "} - ( - FarLink( - "https://github.com/tectonic-typesetting/tectonic-texlive-bundles", - html!( (FAIcon::Github) "tectonic-texlive-bundles" ) - ) - ) - } - } + span style="color: var(--grey);" {"Status: "} + span style="color: var(--yellow);" {"Passive"} } li { - p { - b { "Daisy" } - ", a pretty TUI scientific calculator. " - - em { span style="color: var(--grey);" {"[author]"} } - } - - ul { - li { - span style="color: var(--grey);" {"Status: "} - span style="color: var(--orange);" {"Done. "} - "Used this to learn Rust. " - (FarLink("https://numbat.dev", "Numbat")) - " is better." - } - - li { - span style="color: var(--grey);" {"Repository: "} - ( - FarLink( - "https://github.com/rm-dr/daisy", - html!( (FAIcon::Github) "rm-dr/daisy" ) - ) + span style="color: var(--grey);" {"Website: "} + ( + FarLink( + "https://www.redox-os.org", + html!( + (FAIcon::Link) + "redox-os.org" ) - } - - li { - span style="color: var(--grey);" {"Website: "} - ( - FarLink( - "https://daisy.betalupi.com", - html!( - (FAIcon::Link) - "daisy.betalupi.com" - ) - ) - ) - " (WASM demo)" - } - } - } - - li { - p { - b { "Lamb" } - ", a lambda calculus engine. " - - em { span style="color: var(--grey);" {"[author]"} } - } - - ul { - li { - span style="color: var(--grey);" {"Status: "} - span style="color: var(--orange);" {"Done. "} - "Fun little project." - } - - li { - span style="color: var(--grey);" {"Repository: "} - ( - FarLink( - "https://github.com/rm-dr/lamb", - html!( (FAIcon::Github) "rm-dr/lamb" ) - ) - ) - } - - li { - span style="color: var(--grey);" {"PyPi: "} - ( - FarLink( - "https://pypi.org/project/lamb-engine", - html!( - (FAIcon::Python) - "lamb-engine" - ) - ) - ) - } - } - } - - - } - - - - } - - footer { - hr class = "footline" {} - div class = "footContainer" { - p { - "This site was built by hand using " - (FarLink("https://rust-lang.org", "Rust")) - ", " - (FarLink("https://maud.lambda.xyz", "Maud")) - ", " - (FarLink("https://github.com/connorskees/grass", "Grass")) - ", and " - (FarLink("https://docs.rs/axum/latest/axum", "Axum")) - "." + ) + ) } } } + + li { + p { + b { "Tectonic" } + ", the LaTeX engine that is pleasant to use. Experimental, but fully functional. " + + em { span style="color: var(--grey);" {"[co-maintainer]"} } + } + + ul { + li { + span style="color: var(--grey);" {"Status: "} + span style="color: var(--yellow);" {"Passive. "} + (FarLink("https://github.com/typst/typst", "Typst")) + " is better." + } + + + li { + span style="color: var(--grey);" {"Main Repo: "} + ( + FarLink( + "https://github.com/tectonic-typesetting/tectonic", + html!( (FAIcon::Github) "Tectonic" ) + ) + ) + } + + li { + span style="color: var(--grey);" {"Bundle Tools: "} + ( + FarLink( + "https://github.com/tectonic-typesetting/tectonic-texlive-bundles", + html!( (FAIcon::Github) "tectonic-texlive-bundles" ) + ) + ) + } + } + } + + + li { + p { + b { "Daisy" } + ", a pretty TUI scientific calculator. " + + em { span style="color: var(--grey);" {"[author]"} } + } + + ul { + li { + span style="color: var(--grey);" {"Status: "} + span style="color: var(--orange);" {"Done. "} + "Used this to learn Rust. " + (FarLink("https://numbat.dev", "Numbat")) + " is better." + } + + li { + span style="color: var(--grey);" {"Repository: "} + ( + FarLink( + "https://github.com/rm-dr/daisy", + html!( (FAIcon::Github) "rm-dr/daisy" ) + ) + ) + } + + li { + span style="color: var(--grey);" {"Website: "} + ( + FarLink( + "https://daisy.betalupi.com", + html!( + (FAIcon::Link) + "daisy.betalupi.com" + ) + ) + ) + " (WASM demo)" + } + } + } + + li { + p { + b { "Lamb" } + ", a lambda calculus engine. " + + em { span style="color: var(--grey);" {"[author]"} } + } + + ul { + li { + span style="color: var(--grey);" {"Status: "} + span style="color: var(--orange);" {"Done. "} + "Fun little project." + } + + li { + span style="color: var(--grey);" {"Repository: "} + ( + FarLink( + "https://github.com/rm-dr/lamb", + html!( (FAIcon::Github) "rm-dr/lamb" ) + ) + ) + } + + li { + span style="color: var(--grey);" {"PyPi: "} + ( + FarLink( + "https://pypi.org/project/lamb-engine", + html!( + (FAIcon::Python) + "lamb-engine" + ) + ) + ) + } + } + } + + } - } - } + + + + + + ) + )) } } diff --git a/crates/service-webpage/src/routes/links.rs b/crates/service-webpage/src/routes/links.rs index 0ad0046..8c4066c 100644 --- a/crates/service-webpage/src/routes/links.rs +++ b/crates/service-webpage/src/routes/links.rs @@ -1,19 +1,18 @@ use assetserver::Asset; -use macro_sass::sass; -use maud::{DOCTYPE, Markup, PreEscaped, html}; +use maud::{Markup, html}; use crate::{ - components::{md::Markdown, misc::FarLink}, - routes::{assets::Image_Icon, index::PageMetadata}, + components::{ + base::{BasePage, PageMetadata}, + md::Markdown, + }, + routes::assets::Image_Icon, }; // TODO: emoji -// TODO: one page base // TODO: spellcheck // TODO: check links -const CSS: &str = sass!("css/main.scss"); - pub async fn links() -> Markup { let meta = PageMetadata { title: "Links".into(), @@ -23,66 +22,31 @@ pub async fn links() -> Markup { }; html! { - (DOCTYPE) - html { - head { - meta charset="UTF" {} - meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" {} - meta content="text/html; charset=UTF-8" http-equiv="content-type" {} - meta property="og:type" content="website" {} - - (meta) - title { (PreEscaped(meta.title)) } - style { (PreEscaped(CSS)) } - } - - body { - div class="wrapper" { - main { - - // TODO: no metadata class, generate backlink array - div { - a href="/" style="padding-left:4pt;padding-right:4pt;" {"home"} - "/" - span class="metaData" style="padding-left:4pt;padding-right:4pt;" { "links" } - } - - (Markdown(MD_A)) - - hr style="margin-top: 8rem; margin-bottom: 8rem" {} - - (Markdown(MD_B)) - - hr style="margin-top: 8rem; margin-bottom: 8rem" {} - - (Markdown(MD_C)) - - hr style="margin-top: 8rem; margin-bottom: 8rem" {} - - (Markdown(MD_D)) - - - } - - footer { - hr class = "footline" {} - div class = "footContainer" { - p { - "This site was built by hand using " - (FarLink("https://rust-lang.org", "Rust")) - ", " - (FarLink("https://maud.lambda.xyz", "Maud")) - ", " - (FarLink("https://github.com/connorskees/grass", "Grass")) - ", and " - (FarLink("https://docs.rs/axum/latest/axum", "Axum")) - "." - } - } - } + (BasePage( + meta, + html!( + // TODO: no metadata class, generate backlink array + div { + a href="/" style="padding-left:4pt;padding-right:4pt;" {"home"} + "/" + span class="metaData" style="padding-left:4pt;padding-right:4pt;" { "links" } } - } - } + + (Markdown(MD_A)) + + hr style="margin-top: 8rem; margin-bottom: 8rem" {} + + (Markdown(MD_B)) + + hr style="margin-top: 8rem; margin-bottom: 8rem" {} + + (Markdown(MD_C)) + + hr style="margin-top: 8rem; margin-bottom: 8rem" {} + + (Markdown(MD_D)) + ) + )) } }