// Theme System - Modern editor themes for Windjammer UI
// Supports dark/light modes and custom color palettes
use super::traits::Renderable
// Editor color palette
pub struct ColorPalette {
// Backgrounds
pub bg_primary: string,
pub bg_secondary: string,
pub bg_tertiary: string,
pub bg_elevated: string,
// Text
pub text_primary: string,
pub text_secondary: string,
pub text_muted: string,
// Accent colors
pub accent_primary: string,
pub accent_secondary: string,
pub accent_success: string,
pub accent_warning: string,
pub accent_error: string,
// Borders
pub border_subtle: string,
pub border_default: string,
// Axis colors (for 3D editors)
pub axis_x: string,
pub axis_y: string,
pub axis_z: string,
}
impl ColorPalette {
// Default dark theme (inspired by modern game engines)
pub fn dark() -> ColorPalette {
ColorPalette {
bg_primary: "#0a0a1a".to_string(),
bg_secondary: "#16213e".to_string(),
bg_tertiary: "#1a2744".to_string(),
bg_elevated: "#1e2d4d".to_string(),
text_primary: "#e0e0e0".to_string(),
text_secondary: "#a0a0a0".to_string(),
text_muted: "#666666".to_string(),
accent_primary: "#e94560".to_string(),
accent_secondary: "#0f3460".to_string(),
accent_success: "#4ade80".to_string(),
accent_warning: "#facc15".to_string(),
accent_error: "#ef4444".to_string(),
border_subtle: "rgba(255,255,255,0.05)".to_string(),
border_default: "rgba(255,255,255,0.1)".to_string(),
axis_x: "#e94560".to_string(),
axis_y: "#4ade80".to_string(),
axis_z: "#60a5fa".to_string(),
}
}
// Light theme
pub fn light() -> ColorPalette {
ColorPalette {
bg_primary: "#f5f5f5".to_string(),
bg_secondary: "#ffffff".to_string(),
bg_tertiary: "#fafafa".to_string(),
bg_elevated: "#ffffff".to_string(),
text_primary: "#1a1a2e".to_string(),
text_secondary: "#666666".to_string(),
text_muted: "#999999".to_string(),
accent_primary: "#e94560".to_string(),
accent_secondary: "#3b82f6".to_string(),
accent_success: "#22c55e".to_string(),
accent_warning: "#eab308".to_string(),
accent_error: "#dc2626".to_string(),
border_subtle: "rgba(0,0,0,0.05)".to_string(),
border_default: "rgba(0,0,0,0.1)".to_string(),
axis_x: "#dc2626".to_string(),
axis_y: "#16a34a".to_string(),
axis_z: "#2563eb".to_string(),
}
}
// High contrast theme (accessibility)
pub fn high_contrast() -> ColorPalette {
ColorPalette {
bg_primary: "#000000".to_string(),
bg_secondary: "#1a1a1a".to_string(),
bg_tertiary: "#2a2a2a".to_string(),
bg_elevated: "#333333".to_string(),
text_primary: "#ffffff".to_string(),
text_secondary: "#cccccc".to_string(),
text_muted: "#888888".to_string(),
accent_primary: "#ffcc00".to_string(),
accent_secondary: "#00ffcc".to_string(),
accent_success: "#00ff00".to_string(),
accent_warning: "#ffff00".to_string(),
accent_error: "#ff0000".to_string(),
border_subtle: "rgba(255,255,255,0.2)".to_string(),
border_default: "rgba(255,255,255,0.4)".to_string(),
axis_x: "#ff6666".to_string(),
axis_y: "#66ff66".to_string(),
axis_z: "#6666ff".to_string(),
}
}
// Monokai-inspired theme
pub fn monokai() -> ColorPalette {
ColorPalette {
bg_primary: "#272822".to_string(),
bg_secondary: "#3e3d32".to_string(),
bg_tertiary: "#49483e".to_string(),
bg_elevated: "#75715e".to_string(),
text_primary: "#f8f8f2".to_string(),
text_secondary: "#a6a997".to_string(),
text_muted: "#75715e".to_string(),
accent_primary: "#f92672".to_string(),
accent_secondary: "#66d9ef".to_string(),
accent_success: "#a6e22e".to_string(),
accent_warning: "#fd971f".to_string(),
accent_error: "#f92672".to_string(),
border_subtle: "rgba(255,255,255,0.05)".to_string(),
border_default: "rgba(255,255,255,0.1)".to_string(),
axis_x: "#f92672".to_string(),
axis_y: "#a6e22e".to_string(),
axis_z: "#66d9ef".to_string(),
}
}
// Nord theme
pub fn nord() -> ColorPalette {
ColorPalette {
bg_primary: "#2e3440".to_string(),
bg_secondary: "#3b4252".to_string(),
bg_tertiary: "#434c5e".to_string(),
bg_elevated: "#4c566a".to_string(),
text_primary: "#eceff4".to_string(),
text_secondary: "#d8dee9".to_string(),
text_muted: "#81a1c1".to_string(),
accent_primary: "#88c0d0".to_string(),
accent_secondary: "#81a1c1".to_string(),
accent_success: "#a3be8c".to_string(),
accent_warning: "#ebcb8b".to_string(),
accent_error: "#bf616a".to_string(),
border_subtle: "rgba(255,255,255,0.05)".to_string(),
border_default: "rgba(255,255,255,0.1)".to_string(),
axis_x: "#bf616a".to_string(),
axis_y: "#a3be8c".to_string(),
axis_z: "#5e81ac".to_string(),
}
}
}
// Full editor theme configuration
pub struct EditorTheme {
pub name: string,
pub palette: ColorPalette,
pub font_family: string,
pub font_size_base: i32,
pub border_radius: i32,
pub spacing_unit: i32,
pub animation_duration: i32,
}
impl EditorTheme {
pub fn default() -> EditorTheme {
EditorTheme {
name: "Windjammer Dark".to_string(),
palette: ColorPalette::dark(),
font_family: "'Inter', 'SF Pro', -apple-system, sans-serif".to_string(),
font_size_base: 13,
border_radius: 6,
spacing_unit: 8,
animation_duration: 150,
}
}
pub fn light() -> EditorTheme {
EditorTheme {
name: "Windjammer Light".to_string(),
palette: ColorPalette::light(),
font_family: "'Inter', 'SF Pro', -apple-system, sans-serif".to_string(),
font_size_base: 13,
border_radius: 6,
spacing_unit: 8,
animation_duration: 150,
}
}
pub fn monokai() -> EditorTheme {
EditorTheme {
name: "Monokai Pro".to_string(),
palette: ColorPalette::monokai(),
font_family: "'Fira Code', 'JetBrains Mono', monospace".to_string(),
font_size_base: 13,
border_radius: 4,
spacing_unit: 8,
animation_duration: 100,
}
}
pub fn nord() -> EditorTheme {
EditorTheme {
name: "Nord".to_string(),
palette: ColorPalette::nord(),
font_family: "'Source Sans Pro', 'Nunito', sans-serif".to_string(),
font_size_base: 14,
border_radius: 8,
spacing_unit: 10,
animation_duration: 200,
}
}
}
impl Renderable for EditorTheme {
pub fn render(self) -> string {
let css = ":root {
--bg-primary: " + self.palette.bg_primary.as_str() + ";
--bg-secondary: " + self.palette.bg_secondary.as_str() + ";
--accent-primary: " + self.palette.accent_primary.as_str() + ";
--text-primary: " + self.palette.text_primary.as_str() + ";
}
body {
font-family: " + self.font_family.as_str() + ";
color: var(--text-primary);
background: var(--bg-primary);
}"
css
}
}
// Theme switcher component
pub struct ThemeSwitcher {
pub current_theme: string,
pub themes: Vec<string>,
pub on_change: string,
}
impl ThemeSwitcher {
pub fn new() -> ThemeSwitcher {
let mut themes = Vec::new()
themes.push("dark".to_string())
themes.push("light".to_string())
themes.push("monokai".to_string())
themes.push("nord".to_string())
themes.push("high-contrast".to_string())
ThemeSwitcher {
current_theme: "dark".to_string(),
themes,
on_change: "".to_string(),
}
}
pub fn on_change(self, handler: string) -> ThemeSwitcher {
self.on_change = handler
self
}
}
impl Renderable for ThemeSwitcher {
pub fn render(self) -> string {
let mut options = "".to_string()
for t in self.themes {
let selected = if t.as_str() == self.current_theme.as_str() { "selected" } else { "" }
options = options + format!("<option value='{}' {}>{}</option>", t, selected, t).as_str()
}
format!("
<div class='theme-switcher'>
<label>🎨 Theme</label>
<select onchange='{}(this.value)'>
{}
</select>
</div>
", self.on_change, options)
}
}