From dc11155aac7f7160c5ef2305aeeef6c3779260dd Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Mon, 20 Jul 2026 20:05:32 +0200 Subject: [PATCH] Fix blog descriptions in list page titles --- crates/bds-core/src/engine/preview.rs | 83 +++++++++++++++++++++++++++ crates/bds-core/src/render/mod.rs | 2 +- crates/bds-core/src/render/routes.rs | 22 ++++++- crates/bds-core/src/render/site.rs | 18 +++--- 4 files changed, 115 insertions(+), 10 deletions(-) diff --git a/crates/bds-core/src/engine/preview.rs b/crates/bds-core/src/engine/preview.rs index be15bff..b949995 100644 --- a/crates/bds-core/src/engine/preview.rs +++ b/crates/bds-core/src/engine/preview.rs @@ -794,6 +794,89 @@ mod tests { assert!(html.contains("post-list")); } + #[test] + fn list_document_titles_match_blog_description_in_preview_and_generation() { + let db = Database::open_in_memory().unwrap(); + let mut metadata = make_metadata(); + metadata.name = "Fallback Blog".into(); + metadata.description = Some(" My Preview Blog ".into()); + metadata.max_posts_per_page = 1; + let mut first = make_post(); + first.post.categories = vec!["article".into()]; + first.post.tags = vec!["rust".into()]; + let mut second = first.clone(); + second.post.id = "post-2".into(); + second.post.slug = "second".into(); + second.post.title = "Second".into(); + let posts = vec![ + (first.post, first.body_markdown), + (second.post, second.body_markdown), + ]; + + let generated = crate::render::build_site_render_artifacts( + db.conn(), + Path::new("."), + "project-1", + &metadata, + &posts, + ) + .unwrap(); + + for path in [ + "/", + "/page/2", + "/category/article", + "/category/article/page/2", + "/tag/rust", + "/tag/rust/page/2", + "/2024", + "/2024/page/2", + "/2024/03", + "/2024/03/page/2", + "/2024/03/09", + "/2024/03/09/page/2", + ] { + let preview = build_preview_response( + db.conn(), + Path::new("."), + "project-1", + &metadata, + &posts, + path, + ) + .unwrap(); + assert_eq!(preview.status_code, 200, "missing preview route {path}"); + assert!( + preview.html.contains("My Preview Blog"), + "wrong preview title for {path}" + ); + + let generated_page = generated + .pages + .iter() + .find(|page| page.url_path == path) + .unwrap_or_else(|| panic!("missing generated route {path}")); + assert!( + generated_page + .html + .contains("My Preview Blog"), + "wrong generated title for {path}" + ); + } + + metadata.description = Some(" ".into()); + let fallback = build_preview_response( + db.conn(), + Path::new("."), + "project-1", + &metadata, + &posts, + "/", + ) + .unwrap(); + assert!(fallback.html.contains("Fallback Blog")); + } + #[test] fn preview_renders_single_post_for_canonical_path() { let db = Database::open_in_memory().unwrap(); diff --git a/crates/bds-core/src/render/mod.rs b/crates/bds-core/src/render/mod.rs index 4c211bd..f94591f 100644 --- a/crates/bds-core/src/render/mod.rs +++ b/crates/bds-core/src/render/mod.rs @@ -13,7 +13,7 @@ pub use generation::{ pub use markdown::render_markdown_to_html; pub(crate) use page_renderer::render_liquid_template_with_host; pub use page_renderer::{RenderError, render_liquid_template}; -pub(crate) use routes::{PostLanguageVariant, select_post_language_variant}; +pub(crate) use routes::{PostLanguageVariant, blog_page_title, select_post_language_variant}; pub use routes::{ RenderedPage, build_canonical_post_path, render_starter_list_page, render_starter_list_page_with_media_map, render_starter_single_post_page, diff --git a/crates/bds-core/src/render/routes.rs b/crates/bds-core/src/render/routes.rs index 282c0a5..222320f 100644 --- a/crates/bds-core/src/render/routes.rs +++ b/crates/bds-core/src/render/routes.rs @@ -52,6 +52,15 @@ pub(crate) fn select_post_language_variant( } } +pub(crate) fn blog_page_title(metadata: &ProjectMetadata) -> &str { + metadata + .description + .as_deref() + .map(str::trim) + .filter(|description| !description.is_empty()) + .unwrap_or_else(|| metadata.name.trim()) +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct RenderedPage { pub relative_path: String, @@ -257,7 +266,7 @@ pub fn render_starter_list_page_with_media_map( let context = ListTemplateContext { language: language.to_string(), language_prefix: language_prefix(language, main_language(metadata)), - page_title: metadata.name.clone(), + page_title: blog_page_title(metadata).to_string(), pico_stylesheet_href: Some(crate::model::pico_stylesheet_href( metadata.pico_theme.as_deref(), )), @@ -589,6 +598,17 @@ mod tests { assert_eq!(select_post_language_variant(&post, "fr", "de", true), None); } + #[test] + fn blog_page_title_prefers_trimmed_description_and_falls_back_to_name() { + let mut metadata = make_metadata(); + metadata.name = " Fallback Blog ".into(); + metadata.description = Some(" Blog Description ".into()); + assert_eq!(blog_page_title(&metadata), "Blog Description"); + + metadata.description = Some(" ".into()); + assert_eq!(blog_page_title(&metadata), "Fallback Blog"); + } + #[test] fn starter_single_post_renderer_uses_canonical_route_and_language_links() { let post = make_post(); diff --git a/crates/bds-core/src/render/site.rs b/crates/bds-core/src/render/site.rs index 10f59ef..99a2307 100644 --- a/crates/bds-core/src/render/site.rs +++ b/crates/bds-core/src/render/site.rs @@ -17,8 +17,9 @@ use crate::model::{ TemplateKind, TemplateStatus, }; use crate::render::{ - PostLanguageVariant, RenderCategorySettings, RenderTemplateLookup, build_canonical_post_path, - render_liquid_template_with_host, resolve_post_template, select_post_language_variant, + PostLanguageVariant, RenderCategorySettings, RenderTemplateLookup, blog_page_title, + build_canonical_post_path, render_liquid_template_with_host, resolve_post_template, + select_post_language_variant, }; use crate::scripting::{CoreHost, HostApi, UnavailableHost}; use crate::util::frontmatter::{read_script_file, read_template_file, read_translation_file}; @@ -625,12 +626,13 @@ fn build_language_routes( category_settings: &HashMap, ) -> Vec { let per_page = metadata.max_posts_per_page.max(1) as usize; + let page_title = blog_page_title(metadata).to_string(); let mut routes = Vec::new(); routes.extend(paginated_route_specs( posts, per_page, language_root_prefix(language, metadata), - metadata.name.clone(), + page_title.clone(), None, None, )); @@ -679,7 +681,7 @@ fn build_language_routes( "{}/category/{slug}", language_root_prefix(language, metadata) ), - category.clone(), + page_title.clone(), Some(json!({"kind": "category", "name": category})), category_settings .get(&category) @@ -698,7 +700,7 @@ fn build_language_routes( &records, per_page, format!("{}/tag/{slug}", language_root_prefix(language, metadata)), - display_name.clone(), + page_title.clone(), Some(json!({"kind": "tag", "name": display_name})), None, )); @@ -709,7 +711,7 @@ fn build_language_routes( &records, per_page, format!("{}/{year}", language_root_prefix(language, metadata)), - format!("{} {year}", metadata.name), + page_title.clone(), Some(json!({"kind": "year", "year": year})), None, )); @@ -723,7 +725,7 @@ fn build_language_routes( "{}/{year}/{month:02}", language_root_prefix(language, metadata) ), - format!("{} {year}-{month:02}", metadata.name), + page_title.clone(), Some(json!({"kind": "month", "year": year, "month": month})), None, )); @@ -737,7 +739,7 @@ fn build_language_routes( "{}/{year}/{month:02}/{day:02}", language_root_prefix(language, metadata) ), - format!("{} {year}-{month:02}-{day:02}", metadata.name), + page_title.clone(), Some(json!({"kind": "day", "year": year, "month": month, "day": day})), None, ));