Files
ElyOffice/crates/ely-web-content/src/tokens.rs
T
ZacharyZhang-NY b7fca0146d Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git),
ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
2026-10-08 21:20:08 -04:00

78 lines
2.6 KiB
Rust

//! The app's theme as CSS variables on the page's root.
use ely_gpui_component::theme::{ActiveTheme as _, Palette};
use gpui::{App, Hsla, Rgba};
fn css(color: Hsla) -> String {
let rgba = Rgba::from(color);
let byte = |part: f32| (part.clamp(0.0, 1.0) * 255.0).round() as u8;
format!(
"rgba({}, {}, {}, {:.3})",
byte(rgba.r),
byte(rgba.g),
byte(rgba.b),
rgba.a
)
}
/// `--ely-*` names and values, palette then type.
pub fn tokens(cx: &App) -> Vec<(String, String)> {
let theme = cx.theme();
let colors: &Palette = &theme.colors;
let mut out: Vec<(String, String)> = [
("bg", colors.bg),
("surface", colors.surface),
("sunken", colors.sunken),
("hover", colors.hover),
("border", colors.border),
("fg", colors.fg),
("fg-muted", colors.fg_muted),
("fg-subtle", colors.fg_subtle),
("accent", colors.accent),
("on-accent", colors.on_accent),
("focus", colors.focus),
("link", colors.link),
("selection", colors.selection),
("danger", colors.danger),
("warning", colors.warning),
("success", colors.success),
("keyword", colors.syntax.keyword),
("string", colors.syntax.string),
("number", colors.syntax.number),
("comment", colors.syntax.comment),
("function", colors.syntax.function),
("type", colors.syntax.type_name),
("tag", colors.syntax.tag),
("attribute", colors.syntax.attribute),
("punctuation", colors.syntax.punctuation),
]
.into_iter()
.map(|(name, color)| (format!("--ely-{name}"), css(color)))
.collect();
out.push(("--ely-font".into(), format!("\"{}\"", theme.font_family)));
out.push(("--ely-mono".into(), format!("\"{}\"", theme.mono_family)));
out.push(("--ely-font-scale".into(), theme.font_scale.to_string()));
let scheme = if theme.is_dark() { "dark" } else { "light" };
out.push(("color-scheme".into(), scheme.into()));
out
}
/// Script that sets `tokens` on `:root`.
pub fn script(tokens: &[(String, String)]) -> String {
let pairs = serde_json::to_string(tokens).expect("strings serialize");
format!("for (const [k, v] of {pairs}) document.documentElement.style.setProperty(k, v);")
}
#[cfg(test)]
mod tests {
#[test]
fn a_script_sets_each_token_as_json() {
let tokens = vec![("--ely-fg".to_string(), "rgba(1, 2, 3, 1.000)".to_string())];
let script = super::script(&tokens);
assert!(
script.contains(r#"[["--ely-fg","rgba(1, 2, 3, 1.000)"]]"#),
"{script}"
);
}
}