drawlang-render 0.1.0

SVG/PNG/PDF backends for the drawlang DSL
Documentation
//! Theme palettes. Every color a diagram uses comes from here unless the
//! source gives an explicit `#hex`; `@tokens` resolve against the active
//! palette so one source renders well in both themes.

use drawlang_core::model::{Color, Theme};

#[derive(Debug, Clone)]
pub struct Palette {
    pub bg: &'static str,
    pub surface: &'static str,
    /// Fill for boxes nested inside another node (e.g. HBM inside a GPU).
    pub inner: &'static str,
    pub ink: &'static str,
    pub muted: &'static str,
    pub faint: &'static str,
    pub accent: &'static str,
    pub accent2: &'static str,
    pub hot: &'static str,
    pub ok: &'static str,
    pub warn: &'static str,
    /// Default node border.
    pub node_border: &'static str,
    /// Default edge stroke.
    pub edge: &'static str,
    /// Edge label text.
    pub edge_label: &'static str,
    /// Group border + label.
    pub group_border: &'static str,
    pub group_label: &'static str,
    /// Group interior wash opacity (over `ink`).
    pub group_wash_opacity: f64,
}

pub const PAPER: Palette = Palette {
    bg: "#FAF9F7",
    surface: "#FFFFFF",
    inner: "#F4F2ED",
    ink: "#21242D",
    muted: "#8B8D98",
    faint: "#E2DFD8",
    accent: "#2F6FED",
    accent2: "#8E4EC6",
    hot: "#E5484D",
    ok: "#30A46C",
    warn: "#B98900",
    node_border: "#3A3F4B",
    edge: "#6E7180",
    edge_label: "#4B4F5C",
    group_border: "#CDC9C0",
    group_label: "#7A7D89",
    group_wash_opacity: 0.025,
};

pub const DARK: Palette = Palette {
    bg: "#15171C",
    surface: "#20242C",
    inner: "#2A2F39",
    ink: "#E8EAF0",
    muted: "#9BA1AE",
    faint: "#32363F",
    accent: "#5C8DEF",
    accent2: "#B083F0",
    hot: "#F2555A",
    ok: "#3DD68C",
    warn: "#FFC53D",
    node_border: "#555B68",
    edge: "#878D9A",
    edge_label: "#B3B8C4",
    group_border: "#3B404B",
    group_label: "#9BA1AE",
    group_wash_opacity: 0.16,
};

pub fn palette(theme: Theme) -> &'static Palette {
    match theme {
        Theme::Paper => &PAPER,
        Theme::Dark => &DARK,
    }
}

impl Palette {
    pub fn token(&self, name: &str) -> &'static str {
        match name {
            "accent" => self.accent,
            "accent2" => self.accent2,
            "hot" => self.hot,
            "ok" => self.ok,
            "warn" => self.warn,
            "muted" => self.muted,
            "faint" => self.faint,
            "ink" => self.ink,
            "bg" => self.bg,
            "surface" => self.surface,
            // Analyze validates token names; this is a safety net.
            _ => self.ink,
        }
    }

    /// Resolve an optional model color with a palette default.
    pub fn resolve(&self, c: Option<&Color>, default: &'static str) -> String {
        match c {
            None => default.to_string(),
            Some(Color::Token(t)) => self.token(t).to_string(),
            Some(Color::Hex(h)) => format!("#{h}"),
        }
    }
}