feat: generate css from theme
This commit is contained in:
parent
2b0c852e99
commit
2bb0a5c099
@ -8,7 +8,10 @@ use std::{
|
|||||||
|
|
||||||
use toml::{Table, Value};
|
use toml::{Table, Value};
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use typst::foundations::{Content, Func, NativeFunc, func};
|
use typst::{
|
||||||
|
foundations::{Content, Func, NativeElement, NativeFunc, func},
|
||||||
|
text::TextElem,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::AppConfig;
|
use crate::AppConfig;
|
||||||
|
|
||||||
@ -90,7 +93,7 @@ impl ThemeRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
struct Color(String);
|
struct Color(String);
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
@ -111,7 +114,7 @@ impl Color {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
struct Style {
|
struct Style {
|
||||||
fg: Option<Color>,
|
fg: Option<Color>,
|
||||||
bold: bool,
|
bold: bool,
|
||||||
@ -214,3 +217,84 @@ pub fn init(config: &AppConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn theme_css_func() -> Func {
|
||||||
|
theme_css::func()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope_to_selector(scope: &str) -> String {
|
||||||
|
format!(".hl-{}", scope.replace('.', "-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style_declarations(style: &Style) -> String {
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
if let Some(Color(hex)) = &style.fg {
|
||||||
|
parts.push(format!("color:{hex}"));
|
||||||
|
}
|
||||||
|
parts.push(format!(
|
||||||
|
"font-weight:{}",
|
||||||
|
if style.bold { "bold" } else { "normal" }
|
||||||
|
));
|
||||||
|
parts.push(format!(
|
||||||
|
"font-style:{}",
|
||||||
|
if style.italic { "italic" } else { "normal" }
|
||||||
|
));
|
||||||
|
parts.push(format!(
|
||||||
|
"text-decoration:{}",
|
||||||
|
if style.underlined {
|
||||||
|
"underline"
|
||||||
|
} else {
|
||||||
|
"none"
|
||||||
|
}
|
||||||
|
));
|
||||||
|
parts.join(";")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_css(scopes: &[&str], theme: &Theme) -> String {
|
||||||
|
let mut groups: HashMap<&Style, Vec<&str>> = HashMap::new();
|
||||||
|
for &scope in scopes {
|
||||||
|
if let Some(style) = theme.styles.get(scope) {
|
||||||
|
groups.entry(style).or_default().push(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groups
|
||||||
|
.iter()
|
||||||
|
.map(|(style, scopes)| {
|
||||||
|
let selectors = scopes
|
||||||
|
.iter()
|
||||||
|
.map(|s| scope_to_selector(s))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",");
|
||||||
|
format!("{}{{{}}}", selectors, style_declarations(style))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[func]
|
||||||
|
fn theme_css(light: String, dark: String) -> String {
|
||||||
|
let Some(registry) = REGISTRY.get() else {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
let Some(light_theme) = registry.themes.get(&light) else {
|
||||||
|
warn!("Unknown light theme: {light}");
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
let Some(dark_theme) = registry.themes.get(&dark) else {
|
||||||
|
warn!("Unknown dark theme: {dark}");
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut shared: Vec<&str> = light_theme
|
||||||
|
.styles
|
||||||
|
.keys()
|
||||||
|
.filter(|k| dark_theme.styles.contains_key(*k))
|
||||||
|
.map(String::as_str)
|
||||||
|
.collect();
|
||||||
|
shared.sort();
|
||||||
|
|
||||||
|
let light_css = generate_css(&shared, light_theme);
|
||||||
|
let dark_css = generate_css(&shared, dark_theme);
|
||||||
|
|
||||||
|
format!("{light_css}\n@media (prefers-color-scheme:dark){{{dark_css}}}")
|
||||||
|
}
|
||||||
|
|||||||
@ -30,6 +30,12 @@ impl TypstContext {
|
|||||||
Value::Func(crate::syntax::highlight_func()),
|
Value::Func(crate::syntax::highlight_func()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if config.themes_root.is_some() {
|
||||||
|
inputs.insert(
|
||||||
|
"theme_css".into(),
|
||||||
|
Value::Func(crate::themes::theme_css_func()),
|
||||||
|
);
|
||||||
|
}
|
||||||
let library = Library::builder()
|
let library = Library::builder()
|
||||||
.with_inputs(inputs)
|
.with_inputs(inputs)
|
||||||
.with_features([typst::Feature::Html].into_iter().collect())
|
.with_features([typst::Feature::Html].into_iter().collect())
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user