#[cfg(feature = "syntax-css")] use crate::css::{generate_base_css, generate_syntax_css}; use crate::static_site::context::{KaTeXSource, StaticSiteContext}; use crate::theme::default_resolved_theme; use miette::IntoDiagnostic; use weaver_common::jacquard::client::AgentSession; #[derive(Debug, Clone, Copy)] pub enum CssMode { Linked, Inline, } pub async fn write_document_head( context: &StaticSiteContext, writer: &mut (impl tokio::io::AsyncWrite + Unpin), css_mode: CssMode, output_path: &std::path::Path, ) -> miette::Result<()> { use tokio::io::AsyncWriteExt; // Get title from frontmatter or current path let title = if let Some(path) = context .dir_contents .as_ref() .and_then(|contents| contents.get(context.position)) { context .titles .get(path) .map(|t| t.value().to_string()) .unwrap_or_else(|| { path.file_stem() .and_then(|s| s.to_str()) .unwrap_or("Untitled") .to_string() }) } else { "Untitled".to_string() }; // Calculate relative path to root based on output file depth let relative_to_root = if let Ok(rel) = output_path.strip_prefix(&context.destination) { let depth = rel.components().count() - 1; // -1 because the file itself doesn't count if depth <= 0 { "./".to_string() } else { "../".repeat(depth) } } else { "./".to_string() }; writer .write_all(b"\n") .await .into_diagnostic()?; writer .write_all(b"\n") .await .into_diagnostic()?; writer.write_all(b"\n").await.into_diagnostic()?; writer .write_all(b" \n") .await .into_diagnostic()?; writer .write_all(b" \n") .await .into_diagnostic()?; // Title writer.write_all(b" ").await.into_diagnostic()?; writer.write_all(title.as_bytes()).await.into_diagnostic()?; writer.write_all(b"\n").await.into_diagnostic()?; // CSS match css_mode { CssMode::Linked => { writer .write_all( format!( " \n", relative_to_root ) .as_bytes(), ) .await .into_diagnostic()?; writer .write_all( format!( " \n", relative_to_root ) .as_bytes(), ) .await .into_diagnostic()?; } #[cfg(feature = "syntax-css")] CssMode::Inline => { let default_theme = default_resolved_theme(); let theme = context.theme.as_deref().unwrap_or(&default_theme); writer.write_all(b" \n").await.into_diagnostic()?; writer.write_all(b" \n").await.into_diagnostic()?; } #[cfg(not(feature = "syntax-css"))] CssMode::Inline => { // CSS generation not available without syntax-css feature return Err(miette::miette!( "Inline CSS mode requires the 'syntax-css' feature" )); } } // KaTeX if enabled if let Some(ref katex) = context.katex_source { match katex { KaTeXSource::Cdn => { writer.write_all(b" \n").await.into_diagnostic()?; writer.write_all(b" \n").await.into_diagnostic()?; writer.write_all(b" \n").await.into_diagnostic()?; } KaTeXSource::Local(path) => { let path_str = path.to_string_lossy(); writer .write_all( format!( " \n", path_str ) .as_bytes(), ) .await .into_diagnostic()?; writer .write_all( format!( " \n", path_str ) .as_bytes(), ) .await .into_diagnostic()?; writer.write_all(format!(" \n", path_str).as_bytes()).await.into_diagnostic()?; } } } writer.write_all(b"\n").await.into_diagnostic()?; writer .write_all(b"\n") .await .into_diagnostic()?; writer .write_all(b"
\n") .await .into_diagnostic()?; Ok(()) } pub async fn write_document_footer( writer: &mut (impl tokio::io::AsyncWrite + Unpin), ) -> miette::Result<()> { use tokio::io::AsyncWriteExt; writer.write_all(b"
\n").await.into_diagnostic()?; writer.write_all(b"\n").await.into_diagnostic()?; writer.write_all(b"\n").await.into_diagnostic()?; Ok(()) }