diff --git a/README.md b/README.md
index d2e5bf5..6e50330 100644
--- a/README.md
+++ b/README.md
@@ -69,7 +69,7 @@ The core project is structured in simple modules:
- `wayland.rs` Handles the Wayland integration. It creates a Wayland surface, binds to the layer shell protocol, and sets up the layer surface for the wallpaper.
- `vulkan.rs` Manages the Vulkan rendering pipeline. Initializes Vulkan instance, selects physical device (preferring discrete GPU), creates swapchain, and renders the wallpaper.
- `filters.rs` – Implements image filters and post‑processing effects, including dynamic background blur, scaling algorithms, and other visual transformations.
-- `colors.rs` – Auto detects and generates light and dark color palettes from the wallpaper’s dominant color using k‑means clustering.
+- `colors.rs` – Auto detects and generates light and dark color palettes based on google's material design from the wallpaper’s dominant color using k‑means clustering.
###### *
// HyDE
*

diff --git a/src/colors.rs b/src/colors.rs
index 1442718..a0f5526 100644
--- a/src/colors.rs
+++ b/src/colors.rs
@@ -400,12 +400,12 @@ pub fn dcol(img: &DynamicImage, palette: &str) {
// --------------------------------------------------------------------- / generate palette
fn generate_palette(source_argb: u32, palette: &str) {
- let (src_hue, src_chroma, src_tone) = argb_to_hct(source_argb, &VC);
+ let (src_h, src_c, src_t) = argb_to_hct(source_argb, &VC);
// material palette specs
- let pri_c = src_chroma.max(48.0);
+ let pri_c = src_c.max(48.0);
let sec_c = 16.0;
- let ter_h = (src_hue + 60.0).rem_euclid(360.0);
+ let ter_h = (src_h + 60.0).rem_euclid(360.0);
let ter_c = 24.0;
let neu_c = 4.0;
let nev_c = 8.0;
@@ -415,34 +415,34 @@ fn generate_palette(source_argb: u32, palette: &str) {
// group, name, hue, chroma, dark_tone, light_tone
type Role = (&'static str, &'static str, f64, f64, f64, f64);
let roles: &[Role] = &[
- ("Primary", "Primary", src_hue, pri_c, 80.0, 40.0),
- ("Primary", "On Primary", src_hue, pri_c, 20.0, 100.0),
- ("Primary", "Primary Container", src_hue, pri_c, 30.0, 90.0),
- ("Primary", "On Primary Cont.", src_hue, pri_c, 90.0, 10.0),
- ("Secondary", "Secondary", src_hue, sec_c, 80.0, 40.0),
- ("Secondary", "On Secondary", src_hue, sec_c, 20.0, 100.0),
- ("Secondary", "Secondary Container", src_hue, sec_c, 30.0, 90.0),
- ("Secondary", "On Secondary Cont.", src_hue, sec_c, 90.0, 10.0),
- ("Tertiary", "Tertiary", ter_h, ter_c, 80.0, 40.0),
- ("Tertiary", "On Tertiary", ter_h, ter_c, 20.0, 100.0),
- ("Tertiary", "Tertiary Container", ter_h, ter_c, 30.0, 90.0),
- ("Tertiary", "On Tertiary Cont.", ter_h, ter_c, 90.0, 10.0),
- ("Error", "Error", err_h, err_c, 80.0, 40.0),
- ("Error", "On Error", err_h, err_c, 20.0, 100.0),
- ("Error", "Error Container", err_h, err_c, 30.0, 90.0),
- ("Error", "On Error Cont.", err_h, err_c, 90.0, 10.0),
- ("Surface", "Background", src_hue, neu_c, 6.0, 98.0),
- ("Surface", "On Background", src_hue, neu_c, 90.0, 10.0),
- ("Surface", "Surface", src_hue, neu_c, 6.0, 98.0),
- ("Surface", "On Surface", src_hue, neu_c, 90.0, 10.0),
- ("Surface", "Surface Variant", src_hue, nev_c, 30.0, 90.0),
- ("Surface", "On Surface Variant", src_hue, nev_c, 80.0, 30.0),
- ("Surface", "Outline", src_hue, nev_c, 60.0, 50.0),
- ("Surface", "Outline Variant", src_hue, nev_c, 30.0, 80.0),
- ("Surface", "Shadow", src_hue, neu_c, 0.0, 0.0),
- ("Surface", "Inverse Surface", src_hue, neu_c, 90.0, 20.0),
- ("Surface", "Inverse On Surface", src_hue, neu_c, 20.0, 95.0),
- ("Surface", "Inverse Primary", src_hue, pri_c, 80.0, 40.0),
+ ("Primary", "Primary", src_h, pri_c, 80.0, 40.0),
+ ("Primary", "On Primary", src_h, pri_c, 20.0, 100.0),
+ ("Primary", "Primary Container", src_h, pri_c, 30.0, 90.0),
+ ("Primary", "On Primary Container", src_h, pri_c, 90.0, 10.0),
+ ("Secondary", "Secondary", src_h, sec_c, 80.0, 40.0),
+ ("Secondary", "On Secondary", src_h, sec_c, 20.0, 100.0),
+ ("Secondary", "Secondary Container", src_h, sec_c, 30.0, 90.0),
+ ("Secondary", "On Secondary Container", src_h, sec_c, 90.0, 10.0),
+ ("Tertiary", "Tertiary", ter_h, ter_c, 80.0, 40.0),
+ ("Tertiary", "On Tertiary", ter_h, ter_c, 20.0, 100.0),
+ ("Tertiary", "Tertiary Container", ter_h, ter_c, 30.0, 90.0),
+ ("Tertiary", "On Tertiary Container", ter_h, ter_c, 90.0, 10.0),
+ ("Error", "Error", err_h, err_c, 80.0, 40.0),
+ ("Error", "On Error", err_h, err_c, 20.0, 100.0),
+ ("Error", "Error Container", err_h, err_c, 30.0, 90.0),
+ ("Error", "On Error Container", err_h, err_c, 90.0, 10.0),
+ ("Surface", "Background", src_h, neu_c, 6.0, 98.0),
+ ("Surface", "On Background", src_h, neu_c, 90.0, 10.0),
+ ("Surface", "Surface", src_h, neu_c, 6.0, 98.0),
+ ("Surface", "On Surface", src_h, neu_c, 90.0, 10.0),
+ ("Surface", "Surface Variant", src_h, nev_c, 30.0, 90.0),
+ ("Surface", "On Surface Variant", src_h, nev_c, 80.0, 30.0),
+ ("Surface", "Outline", src_h, nev_c, 60.0, 50.0),
+ ("Surface", "Outline Variant", src_h, nev_c, 30.0, 80.0),
+ ("Surface", "Shadow", src_h, neu_c, 0.0, 0.0),
+ ("Surface", "Inverse Surface", src_h, neu_c, 90.0, 20.0),
+ ("Surface", "Inverse On Surface", src_h, neu_c, 20.0, 95.0),
+ ("Surface", "Inverse Primary", src_h, pri_c, 80.0, 40.0),
];
// display dominant color
@@ -452,7 +452,7 @@ fn generate_palette(source_argb: u32, palette: &str) {
print!("\x1b[48;2;{};{};{}m \x1b[0m", r, g, b);
println!(
" #{:06X} :: HyDE-{:<5} :: H:{:.1}° C:{:.1} T:{:.1}",
- source_argb & 0xFFFFFF, palette, src_hue, src_chroma, src_tone
+ source_argb & 0xFFFFFF, palette, src_h, src_c, src_t
);
// display generated palette
@@ -468,12 +468,107 @@ fn generate_palette(source_argb: u32, palette: &str) {
// --------------------------------------------------------------------- / print palette
fn print_palette(colors: Vec) {
- for entry in colors {
+ for entry in &colors {
let r = ((entry.argb >> 16) & 0xFF) as u8;
let g = ((entry.argb >> 8) & 0xFF) as u8;
let b = ( entry.argb & 0xFF) as u8;
print!("\x1b[48;2;{};{};{}m \x1b[0m", r, g, b);
println!(" #{:06X} :: {:<10} :: {}", entry.argb & 0xFFFFFF, entry.group, entry.name);
}
+ deploy_palette(&colors);
+}
+
+
+// --------------------------------------------------------------------- / deploy palette
+
+fn deploy_palette(colors: &[ColorPalette]) {
+
+ // eval XDG config dir
+ let xdg_dir = std::env::var("XDG_CONFIG_HOME").ok().or_else(|| {
+ std::env::var("HOME").ok().map(|home| format!("{}/.config", home))
+ }).map(|base| format!("{}/wallbash", base)).unwrap_or_default();
+
+ let templates = match std::fs::read_dir(&xdg_dir) {
+ Ok(templates) => templates,
+ Err(_) => return,
+ };
+
+ let mut deployments = Vec::new();
+ let mut hashpaths = std::collections::HashSet::new();
+
+ // scan template files (*.t2)
+ for entry in templates.flatten() {
+ let path = entry.path();
+ if path.extension().and_then(|s| s.to_str()) != Some("t2") {
+ continue;
+ }
+ let template = match std::fs::read_to_string(&path) {
+ Ok(t) => t,
+ Err(_) => continue,
+ };
+
+ // parse header
+ let (out, cmd, content) = if let Some(newline_pos) = template.find('\n') {
+ let header = &template[..newline_pos];
+ if header.starts_with("[::HyDE::]") {
+ let parts: Vec<&str> = header.split('|').collect();
+ let out = parts.get(1).filter(|s| !s.is_empty()).map(|s| s.to_string());
+ let cmd = parts.get(2).filter(|s| !s.is_empty()).map(|s| s.to_string());
+ (out, cmd, &template[newline_pos + 1..])
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ };
+
+ // skip duplicate outputs
+ if let Some(ref dest) = out {
+ if !hashpaths.insert(dest.clone()) {
+ continue;
+ }
+ }
+
+ // inject palette colors
+ let mut rendered = content.to_string();
+ for color in colors {
+ let tag = format!("[::{}::]", color.name);
+ let hex = format!("#{:06X}", color.argb & 0xFFFFFF);
+ rendered = rendered.replace(&tag, &hex);
+ }
+ deployments.push((out, cmd, rendered));
+ }
+
+ // spawn thread for each template
+ let mut handles = Vec::new();
+ for (out, cmd, rendered) in deployments {
+ handles.push(std::thread::spawn(move || {
+
+ // generate shell script
+ let mut script = String::new();
+ if let Some(target) = &out {
+ script.push_str(&format!("if [ -f \"{}\" ]; then\n", target));
+ script.push_str(&format!("cat > \"{}\" << 'EOF'\n", target));
+ script.push_str(&rendered);
+ script.push_str("\nEOF\n");
+ script.push_str(&format!("echo \"[shell] deployed -> {}\"\n", target));
+ if let Some(cmd) = &cmd {
+ script.push_str(&format!("{}\n", cmd));
+ }
+ script.push_str("else\n");
+ script.push_str(&format!("echo '[shell] skipped (file not found) -> {}'\n", target));
+ script.push_str("fi\n");
+ }
+
+ // execute shell script
+ if !script.is_empty() {
+ let _ = std::process::Command::new("sh").arg("-c").arg(&script).status();
+ }
+ }));
+ }
+
+ for handle in handles {
+ let _ = handle.join();
+ }
}