feat: implement cohesive site structure

Add content type handling and multi-page generation:

- content.rs: Add ContentKind enum (Post, Page, Section, Project)
  and extend Frontmatter with weight/link_to fields
- templates.rs: Add render_page, render_homepage,
  render_blog_index, render_projects_index templates
- main.rs: Process all content types with dedicated handlers

Output structure:
- /index.html (homepage from _index.md)
- /blog/index.html (post listing, sorted by date)
- /blog/<slug>/index.html (individual posts)
- /about/index.html, /collab/index.html (standalone pages)
- /projects/index.html (project cards with external links)
This commit is contained in:
Timothy DeHerrera
2026-01-24 19:19:53 -07:00
parent 0cee5325d3
commit 06b7e0df64
3 changed files with 296 additions and 29 deletions

View File

@@ -1,7 +1,7 @@
//! HTML templates using maud.
use crate::content::Frontmatter;
use maud::{html, Markup, DOCTYPE};
use crate::content::{Content, Frontmatter};
use maud::{DOCTYPE, Markup, PreEscaped, html};
/// Render a blog post with the base layout.
pub fn render_post(frontmatter: &Frontmatter, content_html: &str) -> Markup {
@@ -26,7 +26,94 @@ pub fn render_post(frontmatter: &Frontmatter, content_html: &str) -> Markup {
}
}
section.content {
(maud::PreEscaped(content_html))
(PreEscaped(content_html))
}
}
},
)
}
/// Render a standalone page (about, collab, etc.)
pub fn render_page(frontmatter: &Frontmatter, content_html: &str) -> Markup {
base_layout(
&frontmatter.title,
html! {
article.page {
h1 { (frontmatter.title) }
section.content {
(PreEscaped(content_html))
}
}
},
)
}
/// Render the homepage.
pub fn render_homepage(frontmatter: &Frontmatter, content_html: &str) -> Markup {
base_layout(
&frontmatter.title,
html! {
section.hero {
h1 { (frontmatter.title) }
@if let Some(ref desc) = frontmatter.description {
p.tagline { (desc) }
}
}
section.content {
(PreEscaped(content_html))
}
},
)
}
/// Render the blog listing page.
pub fn render_blog_index(title: &str, posts: &[Content]) -> Markup {
base_layout(
title,
html! {
h1 { (title) }
ul.post-list {
@for post in posts {
li {
a href=(format!("/blog/{}/", post.slug)) {
span.title { (post.frontmatter.title) }
@if let Some(ref date) = post.frontmatter.date {
time.date { (date) }
}
}
@if let Some(ref desc) = post.frontmatter.description {
p.description { (desc) }
}
}
}
}
},
)
}
/// Render the projects page with cards.
pub fn render_projects_index(title: &str, projects: &[Content]) -> Markup {
base_layout(
title,
html! {
h1 { (title) }
ul.project-cards {
@for project in projects {
li.card {
@if let Some(ref link) = project.frontmatter.link_to {
a href=(link) target="_blank" rel="noopener" {
h2 { (project.frontmatter.title) }
@if let Some(ref desc) = project.frontmatter.description {
p { (desc) }
}
}
} @else {
h2 { (project.frontmatter.title) }
@if let Some(ref desc) = project.frontmatter.description {
p { (desc) }
}
}
}
}
}
},