pane_ui 0.1.0

A RON-driven, hot-reloadable wgpu UI library with spring animations and consistent scaling
Documentation
// ── Shader Registry ───────────────────────────────────────────────────────────
//
// In release builds (default), shaders are baked into the binary via include_str!.
// In dev builds (--features dev), shaders are loaded from disk at runtime,
// enabling hot reload of WGSL without recompiling.
//
// Users can add extra shader dirs via shader_dirs in their menu RON.
// Any .wgsl file found there is auto-registered by name (stem of filename).

pub const BUILTIN_SHADER_NAMES: &[&str] = &[
    "flat",
    "frosted_glass",
    "liquid_glass",
    "app_icon_glass",
    "retro",
    "outline",
    "textured",
    "emboss",
    "xmb_bg",
    "polygon",
];

// ── Release Mode — baked in ───────────────────────────────────────────────────

#[cfg(not(feature = "dev"))]
pub fn load_shader(name: &str) -> String {
    match name {
        "flat" => include_str!("../shaders/flat.wgsl").to_string(),
        "frosted_glass" => include_str!("../shaders/frosted_glass.wgsl").to_string(),
        "liquid_glass" => include_str!("../shaders/liquid_glass.wgsl").to_string(),
        "app_icon_glass" => include_str!("../shaders/app_icon_glass.wgsl").to_string(),
        "retro" => include_str!("../shaders/retro.wgsl").to_string(),
        "outline" => include_str!("../shaders/outline.wgsl").to_string(),
        "textured" => include_str!("../shaders/textured.wgsl").to_string(),
        "emboss" => include_str!("../shaders/emboss.wgsl").to_string(),
        "xmb_bg" => include_str!("../shaders/xmb_bg.wgsl").to_string(),
        "polygon" => include_str!("../shaders/polygon.wgsl").to_string(),
        other => panic!("[pane_ui] Unknown built-in shader '{other}'"),
    }
}

// ── Dev Mode — runtime loaded ─────────────────────────────────────────────────

#[cfg(feature = "dev")]
pub fn load_shader(name: &str) -> String {
    let path = format!("shaders/{}.wgsl", name);
    std::fs::read_to_string(&path)
        .unwrap_or_else(|e| panic!("[pane_ui] Failed to load shader '{}': {}", path, e))
}

// ── User Dir Scanner ──────────────────────────────────────────────────────────
// Always available — scans a directory for .wgsl files and returns (name, source) pairs.
// Used at runtime for user-supplied shader dirs declared in menu RON.

pub fn scan_shader_dir(dir: &str) -> Vec<(String, String)> {
    scan_dir(dir, "wgsl")
}

/// Scan a directory for files with the given extension.
/// Returns (stem, source) pairs. Logs and skips unreadable files.
pub fn scan_dir(dir: &str, ext: &str) -> Vec<(String, String)> {
    let entries = match std::fs::read_dir(dir) {
        Ok(e) => e,
        Err(e) => {
            println!("[pane_ui] Could not scan dir '{dir}': {e}");
            return Vec::new();
        }
    };
    let mut results = Vec::new();
    for entry in entries.filter_map(std::result::Result::ok) {
        let path = entry.path();
        if path.extension().is_some_and(|x| x == ext) {
            let name = path.file_stem().unwrap().to_string_lossy().to_string();
            match std::fs::read_to_string(&path) {
                Ok(src) => results.push((name, src)),
                Err(e) => println!("[pane_ui] Failed to read '{}': {e}", path.display()),
            }
        }
    }
    results
}