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,
));