# theme-common Shared theme loading logic for TOML-based theme files. Parses theme metadata and color values from `.toml` files on disk. Used by GoingsOn and Balanced Breakfast (Tauri apps that load themes at runtime). audiofiles uses a different compile-time embedding approach and does not use this crate. ## Usage ```rust use theme_common::{load_theme, list_themes_from_dirs, dev_themes_dir}; use std::path::PathBuf; // Set up theme directories (later entries override earlier ones) let bundled = PathBuf::from("/path/to/MNW/shared/themes"); let custom = PathBuf::from("/path/to/user/custom-themes"); let dirs = vec![(bundled, false), (custom, true)]; // List available themes (sorted by name) let themes = list_themes_from_dirs(&dirs); for t in &themes { println!("{} ({}, {})", t.name, t.id, t.variant); } // Load a specific theme by ID let theme = load_theme(&dirs, "catppuccin-mocha").unwrap(); println!("Name: {}", theme.meta.name); // "Catppuccin Mocha" println!("Variant: {}", theme.meta.variant); // "dark" println!("BG: {}", theme.colors["background.primary"]); // "#181825" // Dev helper: find MNW/shared/themes/ relative to CARGO_MANIFEST_DIR if let Some(dev_dir) = dev_themes_dir(Path::new(env!("CARGO_MANIFEST_DIR")), 3) { // dev_dir = .../Code/MNW/shared/themes } ``` ## Theme File Format Theme files are TOML with four color sections. See `../themes/` for the 16 bundled themes. ```toml # Attribution comment (optional, for credit) # Based on Catppuccin by Catppuccin Org -- MIT License [meta] name = "Theme Name" # Display name (required) variant = "dark" # "dark", "light", or "high-contrast" (default: "dark") [background] primary = "#181825" # Main background secondary = "#11111b" # Sidebar / panel background (optional) tertiary = "#313244" # Hover / selection background (optional) surface = "#1e1e2e" # Card / elevated surface (optional) [foreground] primary = "#cdd6f4" # Main text secondary = "#bac2de" # Secondary text (optional) muted = "#9399b2" # Placeholder / disabled text (optional) [accent] red = "#f38ba8" # Error, destructive actions green = "#a6e3a1" # Success, positive actions blue = "#89b4fa" # Links, primary accent yellow = "#f9e2af" # Warnings purple = "#cba6f7" # Tags, special elements cyan = "#89dceb" # Info, secondary accent [border] default = "#45475a" # Default border color ``` ### Color Key Flattening Colors are loaded into a flat `HashMap` with dotted keys: ``` "background.primary" -> "#181825" "foreground.muted" -> "#9399b2" "accent.blue" -> "#89b4fa" "border.default" -> "#45475a" ``` Apps map these keys to CSS variables or egui color values. ### Theme ID The theme ID is the filename without `.toml` (e.g., `catppuccin-mocha.toml` has ID `catppuccin-mocha`). IDs must contain only alphanumeric characters, hyphens, and underscores. Path traversal characters are rejected. ## API | Function | Description | |----------|-------------| | `list_themes_from_dirs(dirs)` | Scan directories for `.toml` files, return sorted `Vec` | | `load_theme(dirs, id)` | Load a theme by ID, returning `ThemeColors` (metadata + color map) | | `find_theme_path(dirs, id)` | Find the file path for a theme ID (highest-priority directory wins) | | `parse_meta(id, table, is_custom)` | Parse `[meta]` from a TOML table into `ThemeMeta` | | `extract_colors(table)` | Flatten color sections into a `HashMap` | | `validate_theme_id(id)` | Check that an ID contains only safe characters | | `dev_themes_dir(manifest_dir, levels)` | Walk up parent directories to find `MNW/shared/themes/` (dev convenience) | ## Directory Priority `list_themes_from_dirs` and `load_theme` accept a list of `(PathBuf, bool)` pairs. Later directories override earlier ones by theme ID. The `bool` marks whether the directory contains user-custom themes (`is_custom` on `ThemeMeta`). Typical setup for a Tauri app: 1. Bundled themes from `MNW/shared/themes/` (is_custom = false) 2. User themes from an app data directory (is_custom = true) ## License PolyForm Noncommercial 1.0.0