#!/usr/bin/env python3 """ Script to run pywal and apply colors to iTerm2 automatically. Usage: python3 wal-iterm.py [path_to_image] """ import subprocess import sys import os import json import tempfile from pathlib import Path import plistlib HOME = Path.home() WAL_CACHE = HOME / ".cache" / "wal" ITERM_COLORS_DIR = WAL_CACHE / "itermcolors" COLORS_JSON = WAL_CACHE / "colors.json" def run_command(cmd, check=True): """Run a shell command and return the result.""" try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, check=check ) return result.stdout.strip(), result.stderr.strip(), result.returncode except subprocess.CalledProcessError as e: print(f"Error running command: {cmd}") print(f"Error: {e.stderr}") sys.exit(1) def hex_to_rgb(hex_color): """Convert hex color to RGB values (0-1 range).""" hex_color = hex_color.lstrip('#') r = int(hex_color[0:2], 16) / 255.0 g = int(hex_color[2:4], 16) / 255.0 b = int(hex_color[4:6], 16) / 255.0 return r, g, b def run_wal(image_path=None, backend="haishoku"): """Run pywal to generate colors.""" if image_path: if not os.path.exists(image_path): print(f"Error: Image file not found: {image_path}") sys.exit(1) print(f"Running wal on image: {image_path} (backend: {backend})") cmd = f'wal -i "{image_path}" --backend {backend}' else: print("Running wal to regenerate colors from last image...") cmd = "wal -n" stdout, stderr, returncode = run_command(cmd, check=False) if returncode != 0: print(f"\nError: wal failed to process the image.") if stderr: print(f"Details: {stderr}") if not image_path: print("Cannot retry without image path. Please specify an image.") sys.exit(1) # Try alternative backends as fallback fallback_backends = ["colorthief", "colorz", "wal"] for fallback_backend in fallback_backends: if fallback_backend == backend: continue print(f"\nTrying with {fallback_backend} backend...") cmd_fallback = f'wal -i "{image_path}" --backend {fallback_backend}' stdout, stderr, returncode = run_command(cmd_fallback, check=False) if returncode == 0: print(f"✓ Successfully generated colors using {fallback_backend} backend") break else: print(f" {fallback_backend} backend also failed") else: print(f"\nError: wal failed with all attempted backends.") print("This may be a pywal bug with this specific image.") print("Suggestions:") print(" - Try a different image") print(" - Update pywal: pip install --upgrade pywal") print(" - Try manually: wal -i --backend ") sys.exit(1) if stderr and "error" not in stderr.lower(): print(stderr) print("✓ Colors generated by wal") def create_dynamic_profile(): """Create/update a dynamic profile with pywal colors.""" print("Creating/updating dynamic profile...") if not COLORS_JSON.exists(): print(f"Error: colors.json not found at {COLORS_JSON}") sys.exit(1) # Read pywal colors with open(COLORS_JSON) as f: colors = json.load(f) colors_dict = colors.get("colors", {}) special = colors.get("special", {}) # Create dynamic profile structure dynamic_profile = { "Profiles": [ { "Name": "wal", "Guid": "wal-dynamic-profile-uuid", "Rewritable": True, } ] } profile = dynamic_profile["Profiles"][0] # Add ANSI colors (0-15) for i in range(16): color_key = f"color{i}" hex_color = colors_dict.get(color_key, "#000000") r, g, b = hex_to_rgb(hex_color) profile[f"Ansi {i} Color"] = { "Color Space": "sRGB", "Red Component": r, "Green Component": g, "Blue Component": b, "Alpha Component": 1.0, } # Add special colors profile["Background Color"] = { "Color Space": "sRGB", "Red Component": hex_to_rgb(special.get("background", "#000000"))[0], "Green Component": hex_to_rgb(special.get("background", "#000000"))[1], "Blue Component": hex_to_rgb(special.get("background", "#000000"))[2], "Alpha Component": 1.0, } profile["Foreground Color"] = { "Color Space": "sRGB", "Red Component": hex_to_rgb(special.get("foreground", "#ffffff"))[0], "Green Component": hex_to_rgb(special.get("foreground", "#ffffff"))[1], "Blue Component": hex_to_rgb(special.get("foreground", "#ffffff"))[2], "Alpha Component": 1.0, } profile["Cursor Color"] = { "Color Space": "sRGB", "Red Component": hex_to_rgb(special.get("cursor", "#ffffff"))[0], "Green Component": hex_to_rgb(special.get("cursor", "#ffffff"))[1], "Blue Component": hex_to_rgb(special.get("cursor", "#ffffff"))[2], "Alpha Component": 1.0, } # Write the dynamic profile dynamic_profiles_dir = Path.home() / "Library/Application Support/iTerm2/DynamicProfiles" dynamic_profiles_dir.mkdir(parents=True, exist_ok=True) profile_file = dynamic_profiles_dir / "wal-profile.json" with open(profile_file, 'w') as f: json.dump(dynamic_profile, f, indent=2) print(f"✓ Dynamic profile created/updated: {profile_file}") print("✓ iTerm2 will automatically detect the changes") print("\nTo use: Switch to the 'wal' profile in iTerm2") print("Future runs will update the 'wal' profile automatically") def main(): """Main function.""" image_path = sys.argv[1] if len(sys.argv) > 1 else None run_wal(image_path, backend="haishoku") create_dynamic_profile() print("\n✓ Done! Dynamic profile 'wal' updated with new colors.") if __name__ == "__main__": main()