Enhance documentation and configuration for static site generator

- Update README for clarity and quick start instructions
- Refine default config.toml with hostname and base path
- Adjust index.md files for consistent heading levels
- Simplify CSS for code block styling and remove unnecessary theme switching
- Refactor SiteConfig to derive full base URL from hostname and base path
- Update MarkdownRenderer to remove light theme handling
This commit is contained in:
yhirose
2026-03-02 00:48:14 -05:00
parent fdb589d97e
commit 2e124cde02
10 changed files with 225 additions and 162 deletions

View File

@@ -55,7 +55,7 @@ struct Page {
pub fn build(src: &Path, out: &Path) -> Result<()> {
let config = SiteConfig::load(src)?;
let renderer = MarkdownRenderer::new(config.highlight_theme(), config.highlight_theme_light());
let renderer = MarkdownRenderer::new(config.highlight_theme());
// Build Tera: start with embedded defaults, then override with user templates
let tera = build_tera(src)?;
@@ -125,7 +125,7 @@ pub fn build(src: &Path, out: &Path) -> Result<()> {
ctx.insert("site", &SiteContext {
title: config.site.title.clone(),
version: config.site.version.clone(),
base_url: config.site.base_url.clone(),
base_url: config.site.base_url(),
base_path: config.site.base_path.clone(),
langs: config.i18n.langs.clone(),
nav: config.nav.clone(),
@@ -351,7 +351,7 @@ fn generate_root_redirect(out: &Path, config: &SiteConfig) -> Result<()> {
<p>Redirecting to <a href="{base_path}/{default_lang}/">{base_path}/{default_lang}/</a>...</p>
</body>
</html>"#,
default_lang = config.i18n.default_lang,
default_lang = config.i18n.default_lang(),
base_path = base_path,
);

View File

@@ -27,21 +27,39 @@ pub struct NavLink {
pub struct Site {
pub title: String,
pub version: Option<String>,
pub base_url: String,
/// Optional hostname (e.g. "https://example.github.io"). Combined with
/// base_path to form the full base URL.
pub hostname: Option<String>,
#[serde(default)]
pub base_path: String,
}
impl Site {
/// Returns the full base URL derived from hostname + base_path.
/// Falls back to base_path alone if hostname is not set.
pub fn base_url(&self) -> String {
match &self.hostname {
Some(h) => format!("{}{}", h.trim_end_matches('/'), self.base_path),
None => self.base_path.clone(),
}
}
}
#[derive(Debug, Deserialize)]
pub struct I18n {
pub default_lang: String,
pub langs: Vec<String>,
}
impl I18n {
/// Returns the default language, which is the first entry in langs.
pub fn default_lang(&self) -> &str {
self.langs.first().map(|s| s.as_str()).unwrap_or("en")
}
}
#[derive(Debug, Deserialize)]
pub struct Highlight {
pub theme: Option<String>,
pub theme_light: Option<String>,
}
impl SiteConfig {
@@ -51,6 +69,12 @@ impl SiteConfig {
std::fs::read_to_string(&path).with_context(|| format!("Failed to read {}", path.display()))?;
let mut config: SiteConfig =
toml::from_str(&content).with_context(|| format!("Failed to parse {}", path.display()))?;
// Validate required fields
if config.i18n.langs.is_empty() {
anyhow::bail!("[i18n] langs must not be empty. Please specify at least one language.");
}
// Normalize base_path: strip trailing slash (but keep empty for root)
let bp = config.site.base_path.trim_end_matches('/').to_string();
config.site.base_path = bp;
@@ -63,10 +87,4 @@ impl SiteConfig {
.and_then(|h| h.theme.as_deref())
.unwrap_or("base16-ocean.dark")
}
pub fn highlight_theme_light(&self) -> Option<&str> {
self.highlight
.as_ref()
.and_then(|h| h.theme_light.as_deref())
}
}

View File

@@ -17,16 +17,14 @@ pub struct MarkdownRenderer {
syntax_set: SyntaxSet,
theme_set: ThemeSet,
theme_name: String,
theme_light_name: Option<String>,
}
impl MarkdownRenderer {
pub fn new(theme_name: &str, theme_light_name: Option<&str>) -> Self {
pub fn new(theme_name: &str) -> Self {
Self {
syntax_set: SyntaxSet::load_defaults_newlines(),
theme_set: ThemeSet::load_defaults(),
theme_name: theme_name.to_string(),
theme_light_name: theme_light_name.map(|s| s.to_string()),
}
}
@@ -95,17 +93,7 @@ impl MarkdownRenderer {
.find_syntax_by_token(lang)
.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
let dark_html = self.highlight_with_theme(code, syntax, &self.theme_name);
if let Some(ref light_name) = self.theme_light_name {
let light_html = self.highlight_with_theme(code, syntax, light_name);
format!(
"<div class=\"code-dark\">{}</div><div class=\"code-light\">{}</div>",
dark_html, light_html
)
} else {
dark_html
}
self.highlight_with_theme(code, syntax, &self.theme_name)
}
fn highlight_with_theme(