diff --git a/blogr-cli/src/commands/theme.rs b/blogr-cli/src/commands/theme.rs index b6ede7b..82ce128 100644 --- a/blogr-cli/src/commands/theme.rs +++ b/blogr-cli/src/commands/theme.rs @@ -25,20 +25,55 @@ pub async fn handle_list() -> Result<()> { if all_themes.is_empty() { println!(" šŸ“¦ No themes available"); } else { + // Separate themes by type + let mut blog_themes = Vec::new(); + let mut personal_themes = Vec::new(); + for (name, theme) in all_themes { let info = theme.info(); - let is_active = current_theme.as_ref() == Some(&name); - let status_icon = if is_active { "āœ…" } else { "šŸ“¦" }; - let status_text = if is_active { " (active)" } else { "" }; + if info.site_type.to_string() == "blog" { + blog_themes.push((name, info)); + } else { + personal_themes.push((name, info)); + } + } - println!( - " {} {}{} - {}", - status_icon, name, status_text, info.description - ); - println!( - " šŸ‘¤ Author: {} | šŸ“¦ Version: {}", - info.author, info.version - ); + // Display blog themes + if !blog_themes.is_empty() { + println!("\nšŸ“ Blog Themes (for traditional blogs with posts):"); + for (name, info) in blog_themes { + let is_active = current_theme.as_ref() == Some(&name); + let status_icon = if is_active { "āœ…" } else { "šŸ“¦" }; + let status_text = if is_active { " (active)" } else { "" }; + + println!( + " {} {}{} - {}", + status_icon, name, status_text, info.description + ); + println!( + " šŸ‘¤ Author: {} | šŸ“¦ Version: {}", + info.author, info.version + ); + } + } + + // Display personal themes + if !personal_themes.is_empty() { + println!("\nšŸ‘¤ Personal Website Themes (for portfolios and personal sites):"); + for (name, info) in personal_themes { + let is_active = current_theme.as_ref() == Some(&name); + let status_icon = if is_active { "āœ…" } else { "šŸ“¦" }; + let status_text = if is_active { " (active)" } else { "" }; + + println!( + " {} {}{} - {}", + status_icon, name, status_text, info.description + ); + println!( + " šŸ‘¤ Author: {} | šŸ“¦ Version: {}", + info.author, info.version + ); + } } } @@ -111,6 +146,47 @@ pub async fn handle_set(name: String) -> Result<()> { // Load current configuration let mut config = project.load_config()?; + // Validate theme compatibility with site type + let theme_info = theme.info(); + let site_type = &config.site.site_type; + + if theme_info.site_type.to_string() != *site_type { + // Dynamically build theme lists by site type + let all_themes = get_all_themes(); + let mut blog_theme_names = Vec::new(); + let mut personal_theme_names = Vec::new(); + + for (theme_name, theme_obj) in all_themes { + let info = theme_obj.info(); + if info.site_type.to_string() == "blog" { + blog_theme_names.push(theme_name); + } else { + personal_theme_names.push(theme_name); + } + } + + let blog_themes = blog_theme_names.join(", "); + let personal_themes = personal_theme_names.join(", "); + + return Err(anyhow!( + "āŒ Theme '{}' is a {} theme, but your site is configured as a {} site.\n\n\ + {} themes: {}\n\ + {} themes: {}\n\n\ + šŸ’” To use this theme, either:\n\ + 1. Choose a compatible {} theme from the list above\n\ + 2. Change your site type in blogr.toml: [site] site_type = \"{}\"", + name, + theme_info.site_type, + site_type, + "Blog", + blog_themes, + "Personal", + personal_themes, + site_type, + theme_info.site_type + )); + } + // Update theme name config.theme.name = name.clone(); diff --git a/blogr-themes/src/dark_minimal/mod.rs b/blogr-themes/src/dark_minimal/mod.rs index d1adcee..d1edb3c 100644 --- a/blogr-themes/src/dark_minimal/mod.rs +++ b/blogr-themes/src/dark_minimal/mod.rs @@ -1,4 +1,4 @@ -use crate::{ConfigOption, Theme, ThemeInfo, ThemeTemplates}; +use crate::{ConfigOption, SiteType, Theme, ThemeInfo, ThemeTemplates}; use ratatui::style::{Color, Style}; use std::collections::HashMap; @@ -113,6 +113,7 @@ impl Theme for DarkMinimalTheme { author: "Blogr Team".to_string(), description: "A dark, minimal theme for personal websites with quirky interactions and clean aesthetics.".to_string(), config_schema, + site_type: SiteType::Personal, } } diff --git a/blogr-themes/src/lib.rs b/blogr-themes/src/lib.rs index bfbe358..4b3786a 100644 --- a/blogr-themes/src/lib.rs +++ b/blogr-themes/src/lib.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::fmt; pub mod dark_minimal; pub mod minimal_retro; @@ -17,6 +18,21 @@ pub use slate_portfolio::SlatePortfolioTheme; pub use terminal_candy::TerminalCandyTheme; pub use typewriter::TypewriterTheme; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum SiteType { + Blog, + Personal, +} + +impl fmt::Display for SiteType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SiteType::Blog => write!(f, "blog"), + SiteType::Personal => write!(f, "personal"), + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThemeInfo { pub name: String, @@ -24,6 +40,8 @@ pub struct ThemeInfo { pub author: String, pub description: String, pub config_schema: HashMap, + /// Type of site this theme supports + pub site_type: SiteType, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/blogr-themes/src/minimal_retro/mod.rs b/blogr-themes/src/minimal_retro/mod.rs index 2a35b0f..d5ab927 100644 --- a/blogr-themes/src/minimal_retro/mod.rs +++ b/blogr-themes/src/minimal_retro/mod.rs @@ -1,4 +1,4 @@ -use crate::{ConfigOption, Theme, ThemeInfo, ThemeTemplates}; +use crate::{ConfigOption, SiteType, Theme, ThemeInfo, ThemeTemplates}; use ratatui::style::{Color, Style}; use std::collections::HashMap; @@ -93,6 +93,7 @@ impl Theme for MinimalRetroTheme { author: "Blogr Team".to_string(), description: "An artistic, minimal theme focused on content with expandable posts and beautiful typography".to_string(), config_schema, + site_type: SiteType::Blog, } } diff --git a/blogr-themes/src/musashi/mod.rs b/blogr-themes/src/musashi/mod.rs index 1ecbf83..20fabb4 100644 --- a/blogr-themes/src/musashi/mod.rs +++ b/blogr-themes/src/musashi/mod.rs @@ -1,4 +1,4 @@ -use crate::{ConfigOption, Theme, ThemeInfo, ThemeTemplates}; +use crate::{ConfigOption, SiteType, Theme, ThemeInfo, ThemeTemplates}; use ratatui::style::{Color, Style}; use std::collections::HashMap; @@ -102,6 +102,7 @@ impl Theme for MusashiTheme { author: "Blogr Team".to_string(), description: "An elegant monochrome theme inspired by sumi-e ink wash painting. Soft whites, warm grays, and ink blacks. Peaceful, refined, embodying the warrior's disciplined way. Fully customizable from content.md.".to_string(), config_schema, + site_type: SiteType::Personal, } } diff --git a/blogr-themes/src/obsidian/mod.rs b/blogr-themes/src/obsidian/mod.rs index 631d217..50d8e17 100644 --- a/blogr-themes/src/obsidian/mod.rs +++ b/blogr-themes/src/obsidian/mod.rs @@ -1,4 +1,4 @@ -use crate::{ConfigOption, Theme, ThemeInfo, ThemeTemplates}; +use crate::{ConfigOption, SiteType, Theme, ThemeInfo, ThemeTemplates}; use ratatui::style::Style; use std::collections::HashMap; @@ -39,6 +39,7 @@ impl Theme for ObsidianTheme { author: "Blogr Team".to_string(), description: "Adopts Obsidian community themes to style Blogr content".to_string(), config_schema: schema, + site_type: SiteType::Blog, } } diff --git a/blogr-themes/src/slate_portfolio/mod.rs b/blogr-themes/src/slate_portfolio/mod.rs index ac90a27..e9c6d47 100644 --- a/blogr-themes/src/slate_portfolio/mod.rs +++ b/blogr-themes/src/slate_portfolio/mod.rs @@ -1,4 +1,4 @@ -use crate::{ConfigOption, Theme, ThemeInfo, ThemeTemplates}; +use crate::{ConfigOption, SiteType, Theme, ThemeInfo, ThemeTemplates}; use ratatui::style::{Color, Style}; use std::collections::HashMap; @@ -104,6 +104,7 @@ impl Theme for SlatePortfolioTheme { author: "Blogr Team".to_string(), description: "A sleek, modern dark portfolio theme with polished aesthetics and smooth interactions.".to_string(), config_schema, + site_type: SiteType::Personal, } } diff --git a/blogr-themes/src/terminal_candy/mod.rs b/blogr-themes/src/terminal_candy/mod.rs index 8c8445e..6ac1eea 100644 --- a/blogr-themes/src/terminal_candy/mod.rs +++ b/blogr-themes/src/terminal_candy/mod.rs @@ -1,4 +1,4 @@ -use crate::{ConfigOption, Theme, ThemeInfo, ThemeTemplates}; +use crate::{ConfigOption, SiteType, Theme, ThemeInfo, ThemeTemplates}; use ratatui::style::{Color, Style}; use std::collections::HashMap; @@ -102,6 +102,7 @@ impl Theme for TerminalCandyTheme { author: "Blogr Team".to_string(), description: "A quirky terminal-inspired theme with pastel colors, glitch effects, and playful animations. Perfect for personal websites.".to_string(), config_schema, + site_type: SiteType::Blog, } } diff --git a/blogr-themes/src/typewriter/mod.rs b/blogr-themes/src/typewriter/mod.rs index 0857aaf..ed94eab 100644 --- a/blogr-themes/src/typewriter/mod.rs +++ b/blogr-themes/src/typewriter/mod.rs @@ -1,4 +1,4 @@ -use crate::{ConfigOption, Theme, ThemeInfo, ThemeTemplates}; +use crate::{ConfigOption, SiteType, Theme, ThemeInfo, ThemeTemplates}; use ratatui::style::{Color, Style}; use std::collections::HashMap; @@ -93,6 +93,7 @@ impl Theme for TypewriterTheme { author: "Blogr Team".to_string(), description: "A vintage typewriter-inspired theme with nostalgic aesthetics and mechanical charm.".to_string(), config_schema, + site_type: SiteType::Personal, } }