diff --git a/flake.nix b/flake.nix index 36a5162..baad849 100644 --- a/flake.nix +++ b/flake.nix @@ -134,6 +134,7 @@ modules = [ ./hosts/eva-03/configuration.nix inputs.home-manager.nixosModules.default + comfyui-nix.nixosModules.default ]; }; diff --git a/home-manager/programs/hypr/waybar/Jerry-waybar.nix b/home-manager/programs/hypr/waybar/Jerry-waybar.nix new file mode 100644 index 0000000..32bbdff --- /dev/null +++ b/home-manager/programs/hypr/waybar/Jerry-waybar.nix @@ -0,0 +1,376 @@ +{ + pkgs, + config, + lib, + ... +}: let + terminal = "kitty"; + base00 = "0F1419"; + base01 = "131721"; + base03 = "3E4B59"; + base05 = "E6E1CF"; + base06 = "E6E1CF"; + base07 = "F3F4F5"; + base08 = "F07178"; + base09 = "FF8F40"; + base0A = "FFB454"; + base0B = "B8CC52"; + base0C = "95E6CB"; + base0D = "59C2FF"; + base0E = "D2A6FF"; + base0F = "E6B673"; + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); +in + with lib; { + home.file = builtins.listToAttrs (map + (name: { + name = ".config/waybar/scripts/" + name; + value = { + source = "${scriptsDir}/${name}"; + executable = true; + }; + }) + scripts); + # Configure & Theme Waybar + programs.waybar = { + enable = true; + package = pkgs.waybar; + settings = [ + { + layer = "top"; + position = "top"; + + modules-center = ["network" "pulseaudio" "cpu" "hyprland/workspaces" "memory" "disk" "clock" "custom/weather"]; + modules-left = ["custom/startmenu" "hyprland/window"]; # Eternal: [ "hyprland/workspaces" "cpu" "memory" "network" ] + modules-right = ["tray" "idle_inhibitor" "custom/notification" "battery" "custom/power"]; # Eternal: [ "idle_inhibitor" "pulseaudio" "clock" "custom/notification" "tray" ] + + "hyprland/workspaces" = { + format = "{name}"; + format-icons = { + default = " "; + active = " "; + urgent = " "; + }; + on-scroll-up = "hyprctl dispatch workspace e+1"; + on-scroll-down = "hyprctl dispatch workspace e-1"; + }; + "clock" = { + format = '' {:%H:%M}''; + /* + ''{: %I:%M %p}''; + */ + tooltip = true; + tooltip-format = "{:%A, %d.%B %Y }{calendar}"; + }; + "hyprland/window" = { + max-length = 60; + separate-outputs = false; + }; + "memory" = { + interval = 5; + format = " {}%"; + tooltip = true; + on-click = "${terminal} -e btop"; + }; + "cpu" = { + interval = 5; + format = " {usage:2}%"; + tooltip = true; + on-click = "${terminal} -e btop"; + }; + "disk" = { + format = " {free}"; + tooltip = true; + on-click = "${terminal} -e sh -c df -h ; read"; + }; + "network" = { + format-icons = ["󰤯" "󰤟" "󰤢" "󰤥" "󰤨"]; + format-ethernet = " {bandwidthDownBits}"; + format-wifi = " {bandwidthDownBits}"; + format-disconnected = "󰤮"; + tooltip = false; + on-click = "${terminal} -e btop"; + }; + "tray" = { + spacing = 12; + }; + "pulseaudio" = { + format = "{icon} {volume}% {format_source}"; + format-bluetooth = "{volume}% {icon} {format_source}"; + format-bluetooth-muted = " {icon} {format_source}"; + format-muted = " {format_source}"; + format-source = " {volume}%"; + format-source-muted = ""; + format-icons = { + headphone = ""; + hands-free = ""; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = ["" "" ""]; + }; + on-click = "pavucontrol"; + }; + "custom/power" = { + tooltip = true; + "tooltip-format" = "Left Click: Power Menu (qs-wlogout)\nRight Click: Rofi Power Menu"; + format = " "; + on-click = "qs-wlogout"; + "on-click-right" = "~/.config/waybar/scripts/power-menu.sh"; + }; + "custom/startmenu" = { + tooltip = true; + "tooltip-format" = "App menu"; + format = ""; + # on-click = "rofi -show drun"; + on-click = "launch-nwg-menu"; + "on-click-right" = "nwg-drawer -mr 225 -ml 225 -mt 200 -mb 200 -is 48 --spacing 15"; + }; + "idle_inhibitor" = { + format = "{icon}"; + format-icons = { + activated = " "; + deactivated = " "; + }; + tooltip = "true"; + }; + "custom/notification" = { + tooltip = false; + format = "{icon} {}"; + format-icons = { + notification = ""; + none = ""; + dnd-notification = ""; + dnd-none = ""; + inhibited-notification = ""; + inhibited-none = ""; + dnd-inhibited-notification = ""; + dnd-inhibited-none = ""; + }; + return-type = "json"; + exec-if = "which swaync-client"; + exec = "swaync-client -swb"; + on-click = "pavucontrol"; + escape = true; + }; + "custom/weather" = { + return-type = "json"; + exec = "sh -lc 'WEATHER_ICON_STYLE=emoji WEATHER_TOOLTIP_MARKUP=1 ~/.local/bin/weather'"; + interval = 600; + tooltip = true; + }; + "battery" = { + states = { + warning = 30; + critical = 15; + }; + format = "{icon} {capacity}%"; + format-charging = "󰂄 {capacity}%"; + format-plugged = "󱘖 {capacity}%"; + format-icons = ["󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹"]; + on-click = ""; + tooltip = false; + }; + } + ]; + style = concatStrings [ + '' + * { + font-size: 16px; + font-family: JetBrainsMono Nerd Font, Font Awesome, sans-serif; + font-weight: bold; + } + window#waybar { + /* + + background-color: rgba(26,27,38,0); + border-bottom: 1px solid rgba(26,27,38,0); + border-radius: 0px; + color: #${base0F}; + */ + + background-color: rgba(26,27,38,0); + border-bottom: 1px solid rgba(26,27,38,0); + border-radius: 0px; + color: #${base0F}; + } + #workspaces { + /* + Eternal + background: linear-gradient(180deg, #${base00}, #${base01}); + margin: 5px 5px 5px 0px; + padding: 0px 10px; + border-radius: 0px 15px 50px 0px; + border: 0px; + font-style: normal; + color: #${base00}; + */ + background: linear-gradient(45deg, #${base01}, #${base01}); + margin: 5px; + padding: 0px 1px; + border-radius: 15px; + border: 0px; + font-style: normal; + color: #${base00}; + } + #workspaces button { + padding: 0px 5px; + margin: 4px 3px; + border-radius: 15px; + border: 0px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 0.5; + transition: all 0.3s ease-in-out; + } + #workspaces button.active { + padding: 0px 5px; + margin: 4px 3px; + border-radius: 15px; + border: 0px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 1.0; + min-width: 40px; + transition: all 0.3s ease-in-out; + } + #workspaces button:hover { + border-radius: 15px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 0.8; + } + tooltip { + background: #${base00}; + border: 1px solid #${base0E}; + border-radius: 10px; + } + tooltip label { + color: #${base07}; + } + #window { + /* + Eternal + color: #${base05}; + background: #${base00}; + border-radius: 15px; + margin: 5px; + padding: 2px 20px; + */ + margin: 5px; + padding: 2px 20px; + color: #${base05}; + background: #${base01}; + border-radius: 50px 15px 50px 15px; + } + #memory { + color: #${base0F}; + /* + Eternal + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + */ + background: #${base01}; + margin: 5px; + padding: 2px 20px; + border-radius: 15px 50px 15px 50px; + } + #clock { + color: #${base0B}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #idle_inhibitor { + color: #${base0A}; + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + } + #cpu { + color: #${base07}; + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + } + #disk { + color: #${base03}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #battery { + color: #${base08}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #network { + color: #${base09}; + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + } + #tray { + color: #${base05}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #pulseaudio { + color: #${base0D}; + /* + Eternal + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + */ + background: #${base01}; + margin: 4px; + padding: 2px 20px; + border-radius: 50px 15px 50px 15px; + } + #custom-notification { + color: #${base0C}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #custom-startmenu { + color: #${base03}; + background: #${base00}; + border-radius: 0px 15px 50px 0px; + margin: 5px 5px 5px 0px; + padding: 2px 20px; + } + #idle_inhibitor { + color: #${base09}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #custom-power { + color: #${base08}; /* Red color for power menu */ + background: #${base00}; + border-radius: 15px 0px 0px 50px; + margin: 5px 0px 5px 5px; + padding: 2px 20px; + } + '' + ]; + }; + } diff --git a/home-manager/programs/hypr/waybar/scripts/Weather.py b/home-manager/programs/hypr/waybar/scripts/Weather.py new file mode 100644 index 0000000..1783fed --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/Weather.py @@ -0,0 +1,719 @@ +#!/usr/bin/env python3 +# /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ # +# Rewritten to use Open-Meteo APIs (worldwide, no API key) for robust weather data. +# Outputs Waybar-compatible JSON and a simple text cache. + +import json +import os +import sys +import time +import html +import subprocess +from typing import Any, Dict, List, Optional, Tuple +from datetime import datetime + +import requests + +# =============== Configuration =============== +# You can configure behavior via environment variables OR the constants below. +# Examples (zsh): +# # One-off run +# # WEATHER_UNITS can be "metric" or "imperial" +# WEATHER_UNITS=imperial WEATHER_PLACE="Concord, NH" python3 ~/.config/waybar/scripts/Weather.py +# +# # Persist in current shell session +# export WEATHER_UNITS=imperial +# export WEATHER_LAT=43.2229 +# export WEATHER_LON=-71.332 +# export WEATHER_PLACE="Concord, NH" +# export WEATHER_TOOLTIP_MARKUP=1 # 1 to enable Pango markup, 0 to disable +# export WEATHER_LOC_ICON="📍" # or "*" for ASCII-only +# +CACHE_DIR = os.path.expanduser("~/.cache") +API_CACHE_PATH = os.path.join(CACHE_DIR, "open_meteo_cache.json") +SIMPLE_TEXT_CACHE_PATH = os.path.join(CACHE_DIR, ".weather_cache") +CACHE_TTL_SECONDS = int(os.getenv("WEATHER_CACHE_TTL", "600")) # default 10 minutes + +# Units: metric or imperial (default imperial for ddubsos branch) +UNITS = os.getenv("WEATHER_UNITS", "imperial").strip().lower() # metric|imperial + +# Optional manual coordinates +ENV_LAT = os.getenv("WEATHER_LAT") +ENV_LON = os.getenv("WEATHER_LON") +# Optional manual place override for tooltip +ENV_PLACE = os.getenv("WEATHER_PLACE") +# Manual place name set inside this file. If set (non-empty), this takes top priority. +# Example: MANUAL_PLACE = "Concord, NH, US" +MANUAL_PLACE: Optional[str] = None + +# Location icon in tooltip (default to a standard emoji to avoid missing glyphs) +LOC_ICON = os.getenv("WEATHER_LOC_ICON", "📍") +# Enable/disable Pango markup in tooltip (1/0, true/false) +TOOLTIP_MARKUP = os.getenv("WEATHER_TOOLTIP_MARKUP", "1").lower() not in ("0", "false", "no") +# Optional debug logging to stderr (set WEATHER_DEBUG=1 to enable) +DEBUG = os.getenv("WEATHER_DEBUG", "0").lower() not in ("0", "false", "no") + +# HTTP settings +UA = ( + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/128.0 Safari/537.36" +) +TIMEOUT = 8 + +SESSION = requests.Session() +SESSION.headers.update({"User-Agent": UA}) + +# =============== Icon and status mapping =============== +# Icon style: 'emoji' (colorful), 'nerd' (mono glyphs), or 'gnome' (symbolic names, useful for other bars) +ICON_STYLE = os.getenv("WEATHER_ICON_STYLE", "emoji").strip().lower() # emoji|nerd|gnome + +# Nerd Font weather glyphs (mono) +WEATHER_ICONS_NERD = { + "sunnyDay": "󰖙", + "clearNight": "󰖔", + "cloudyFoggyDay": "", + "cloudyFoggyNight": "", + "rainyDay": "", + "rainyNight": "", + "snowyIcyDay": "", + "snowyIcyNight": "", + "severe": "", + "default": "", +} + +# Colored emoji set (renders with system emoji font, gives the "3D" look) +WEATHER_ICONS_EMOJI = { + "sunnyDay": "☀️", + "clearNight": "🌙", + "cloudyFoggyDay": "⛅️", + "cloudyFoggyNight": "☁️🌙", + "rainyDay": "🌦️", + "rainyNight": "🌧️", + "snowyIcyDay": "🌨️", + "snowyIcyNight": "❄️🌙", + "severe": "⛈️", + "default": "🌥️", +} + +# GNOME symbolic icon names (not used directly by Waybar, here for parity) +WEATHER_ICONS_GNOME = { + "sunnyDay": "Sun-symbolic", + "clearNight": "Moon-symbolic", + "cloudyFoggyDay": "CloudSun-symbolic", + "cloudyFoggyNight": "CloudyMoon-symbolic", + "rainyDay": "CloudRain-symbolic", + "rainyNight": "CloudRain-symbolic", + "snowyIcyDay": "CloudSnowfall-symbolic", + "snowyIcyNight": "CloudSnowfall-symbolic", + "severe": "CloudBolt-symbolic", + "default": "Cloud-symbolic", +} + +# Optional: allow disabling automatic fallback via env +DISABLE_EMOJI_FALLBACK = os.getenv("WEATHER_DISABLE_EMOJI_FALLBACK", "0").lower() in ("1", "true", "yes") + +# Basic detection of color emoji font availability via fontconfig (fc-list) +EMOJI_FONT_CANDIDATES = [ + "Noto Color Emoji", + "Noto Emoji", + "Apple Color Emoji", + "Segoe UI Emoji", + "Twitter Color Emoji", + "Twemoji", + "JoyPixels", +] + +def has_color_emoji_font() -> bool: + try: + # Quick check: list installed fonts; search for any candidate name + proc = subprocess.run( + ["fc-list"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + timeout=1.5, + check=False, + ) + out = proc.stdout or "" + out_lower = out.lower() + for name in EMOJI_FONT_CANDIDATES: + if name.lower() in out_lower: + return True + except Exception: + # If detection fails, don't block emoji; assume not available to be safe on minimal setups + return False + return False + +# Determine the active icon style with fallback from emoji -> nerd when no color emoji fonts are present +if ICON_STYLE == "emoji" and not DISABLE_EMOJI_FALLBACK and not has_color_emoji_font(): + ACTIVE_ICON_STYLE = "nerd" +else: + ACTIVE_ICON_STYLE = ICON_STYLE + +# Choose active set +WEATHER_ICONS = ( + WEATHER_ICONS_EMOJI if ACTIVE_ICON_STYLE == "emoji" else WEATHER_ICONS_NERD if ACTIVE_ICON_STYLE == "nerd" else WEATHER_ICONS_GNOME +) + +WMO_STATUS = { + 0: "Clear sky", + 1: "Mainly clear", + 2: "Partly cloudy", + 3: "Overcast", + 45: "Fog", + 48: "Depositing rime fog", + 51: "Light drizzle", + 53: "Moderate drizzle", + 55: "Dense drizzle", + 56: "Freezing drizzle", + 57: "Freezing drizzle", + 61: "Light rain", + 63: "Moderate rain", + 65: "Heavy rain", + 66: "Freezing rain", + 67: "Freezing rain", + 71: "Slight snow", + 73: "Moderate snow", + 75: "Heavy snow", + 77: "Snow grains", + 80: "Rain showers", + 81: "Rain showers", + 82: "Violent rain showers", + 85: "Snow showers", + 86: "Heavy snow showers", + 95: "Thunderstorm", + 96: "Thunderstorm w/ hail", + 99: "Thunderstorm w/ hail", +} + + +def wmo_to_icon(code: int, is_day: int) -> str: + day = bool(is_day) + if code == 0: + return WEATHER_ICONS["sunnyDay" if day else "clearNight"] + if code in (1, 2, 3, 45, 48): + return WEATHER_ICONS["cloudyFoggyDay" if day else "cloudyFoggyNight"] + if code in (51, 53, 55, 61, 63, 65, 80, 81, 82): + return WEATHER_ICONS["rainyDay" if day else "rainyNight"] + if code in (56, 57, 66, 67, 71, 73, 75, 77, 85, 86): + return WEATHER_ICONS["snowyIcyDay" if day else "snowyIcyNight"] + if code in (95, 96, 99): + return WEATHER_ICONS["severe"] + return WEATHER_ICONS["default"] + + +def wmo_to_status(code: int) -> str: + return WMO_STATUS.get(code, "Unknown") + + +# =============== Utilities =============== + +def esc(s: Optional[str]) -> str: + return html.escape(s, quote=False) if s else "" + +def log_debug(msg: str) -> None: + if DEBUG: + print(msg, file=sys.stderr) + + +def temp_color(temp_val: Optional[float]) -> str: + """Choose a color for the temperature number based on value and unit set.""" + if not isinstance(temp_val, (int, float)): + return "#cdd6f4" # default text + # Work in Fahrenheit for thresholds if imperial, else Celsius + t = float(temp_val) + if UNITS == "metric": + if t <= -5: + return "#89dceb" # sky + if t <= 5: + return "#74c7ec" # sapphire + if t <= 15: + return "#a6e3a1" # green + if t <= 25: + return "#f9e2af" # yellow + if t <= 32: + return "#fab387" # peach + return "#f38ba8" # red + else: + if t <= 25: + return "#89dceb" + if t <= 41: + return "#74c7ec" + if t <= 60: + return "#a6e3a1" + if t <= 77: + return "#f9e2af" + if t <= 90: + return "#fab387" + return "#f38ba8" + +def ensure_cache_dir() -> None: + try: + os.makedirs(CACHE_DIR, exist_ok=True) + except Exception as e: + print(f"Error creating cache dir: {e}", file=sys.stderr) + + +def read_api_cache() -> Optional[Dict[str, Any]]: + try: + if not os.path.exists(API_CACHE_PATH): + return None + with open(API_CACHE_PATH, "r", encoding="utf-8") as f: + data = json.load(f) + if (time.time() - data.get("timestamp", 0)) <= CACHE_TTL_SECONDS: + return data + return None + except Exception as e: + print(f"Error reading cache: {e}", file=sys.stderr) + return None + + +def write_api_cache(payload: Dict[str, Any]) -> None: + try: + ensure_cache_dir() + payload["timestamp"] = time.time() + with open(API_CACHE_PATH, "w", encoding="utf-8") as f: + json.dump(payload, f) + except Exception as e: + print(f"Error writing API cache: {e}", file=sys.stderr) + + +def write_simple_text_cache(text: str) -> None: + try: + ensure_cache_dir() + with open(SIMPLE_TEXT_CACHE_PATH, "w", encoding="utf-8") as f: + f.write(text) + except Exception as e: + print(f"Error writing simple cache: {e}", file=sys.stderr) + + +def get_coords() -> Tuple[float, float]: + # 1) Explicit env + if ENV_LAT and ENV_LON: + try: + return float(ENV_LAT), float(ENV_LON) + except ValueError: + print("Invalid WEATHER_LAT/WEATHER_LON; falling back to IP geolocation", file=sys.stderr) + + # 2) Try cached coordinates from last successful forecast + try: + cached = read_api_cache() + if cached and isinstance(cached, dict): + fc = cached.get("forecast") or {} + lat = fc.get("latitude") + lon = fc.get("longitude") + if isinstance(lat, (int, float)) and isinstance(lon, (int, float)): + return float(lat), float(lon) + except Exception as e: + print(f"Reading cached coords failed: {e}", file=sys.stderr) + + # 3) IP-based geolocation with multiple providers (prefer ipwho.is, ipapi.co; ipinfo.io as fallback) + # ipwho.is + try: + resp = SESSION.get("https://ipwho.is/", timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + if data.get("success"): + lat = data.get("latitude") + lon = data.get("longitude") + if isinstance(lat, (int, float)) and isinstance(lon, (int, float)): + return float(lat), float(lon) + except Exception as e: + print(f"ipwho.is failed: {e}", file=sys.stderr) + + # ipapi.co + try: + resp = SESSION.get("https://ipapi.co/json", timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + lat = data.get("latitude") + lon = data.get("longitude") + if isinstance(lat, (int, float)) and isinstance(lon, (int, float)): + return float(lat), float(lon) + except Exception as e: + print(f"ipapi.co failed: {e}", file=sys.stderr) + + # ipinfo.io (fallback) + try: + resp = SESSION.get("https://ipinfo.io/json", timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + loc = data.get("loc") + if loc and "," in loc: + lat_s, lon_s = loc.split(",", 1) + return float(lat_s), float(lon_s) + except Exception as e: + print(f"ipinfo.io failed: {e}", file=sys.stderr) + + # 4) Last resort + print("IP geolocation failed: no providers succeeded", file=sys.stderr) + return 0.0, 0.0 + + +def units_params(units: str) -> Dict[str, str]: + if units == "imperial": + return { + "temperature_unit": "fahrenheit", + "wind_speed_unit": "mph", + "precipitation_unit": "inch", + } + # default metric + return { + "temperature_unit": "celsius", + "wind_speed_unit": "kmh", + "precipitation_unit": "mm", + } + + +def format_visibility(meters: Optional[float]) -> str: + if meters is None: + return "" + try: + if UNITS == "imperial": + miles = meters / 1609.344 + return f"{miles:.1f} mi" + else: + km = meters / 1000.0 + return f"{km:.1f} km" + except Exception: + return "" + + +# =============== API Fetching =============== + +def fetch_open_meteo(lat: float, lon: float) -> Dict[str, Any]: + base = "https://api.open-meteo.com/v1/forecast" + params = { + "latitude": lat, + "longitude": lon, + "current": "temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_direction_10m,weather_code,visibility,precipitation,pressure_msl,is_day", + "hourly": "precipitation_probability,weather_code", + "daily": "temperature_2m_max,temperature_2m_min", + "timezone": "auto", + } + params.update(units_params(UNITS)) + resp = SESSION.get(base, params=params, timeout=TIMEOUT) + resp.raise_for_status() + return resp.json() + + +def fetch_aqi(lat: float, lon: float) -> Optional[Dict[str, Any]]: + try: + base = "https://air-quality-api.open-meteo.com/v1/air-quality" + params = { + "latitude": lat, + "longitude": lon, + "current": "european_aqi", + "timezone": "auto", + } + resp = SESSION.get(base, params=params, timeout=TIMEOUT) + resp.raise_for_status() + return resp.json() + except Exception as e: + print(f"AQI fetch failed: {e}", file=sys.stderr) + return None + + +def fetch_place(lat: float, lon: float) -> Optional[str]: + """Reverse geocode lat/lon to an approximate place. Tries Nominatim first, then Open-Meteo.""" + lang = os.getenv("WEATHER_LANG", "en") + + # 1) Nominatim (OpenStreetMap) + try: + base = "https://nominatim.openstreetmap.org/reverse" + params = { + "lat": lat, + "lon": lon, + "format": "jsonv2", + "accept-language": lang, + } + headers = {"User-Agent": UA + " Weather.py/1.0"} + resp = SESSION.get(base, params=params, headers=headers, timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + address = data.get("address", {}) + name = data.get("name") or address.get("city") or address.get("town") or address.get("village") or address.get("hamlet") + admin1 = address.get("state") + country = address.get("country") + parts = [part for part in [name, admin1, country] if part] + if parts: + return ", ".join(parts) + except Exception as e: + log_debug(f"Reverse geocoding (Nominatim) failed: {e}") + + # 2) Open-Meteo reverse (fallback) + try: + base = "https://geocoding-api.open-meteo.com/v1/reverse" + params = { + "latitude": lat, + "longitude": lon, + "language": lang, + "format": "json", + } + resp = SESSION.get(base, params=params, timeout=TIMEOUT) + resp.raise_for_status() + data = resp.json() + results = data.get("results") or [] + if results: + p = results[0] + name = p.get("name") + admin1 = p.get("admin1") + country = p.get("country") + parts = [part for part in [name, admin1, country] if part] + if parts: + return ", ".join(parts) + except Exception as e: + log_debug(f"Reverse geocoding (Open-Meteo) failed: {e}") + + return None + + +# =============== Build Output =============== + +def safe_get(dct: Dict[str, Any], *keys, default=None): + cur: Any = dct + for k in keys: + if isinstance(cur, dict): + if k not in cur: + return default + cur = cur[k] + elif isinstance(cur, list): + try: + cur = cur[k] # type: ignore[index] + except Exception: + return default + else: + return default + return cur + + +def build_hourly_precip(forecast: Dict[str, Any]) -> str: + try: + times: List[str] = safe_get(forecast, "hourly", "time", default=[]) or [] + probs: List[Optional[float]] = safe_get( + forecast, "hourly", "precipitation_probability", default=[] + ) or [] + cur_time: Optional[str] = safe_get(forecast, "current", "time") + idx = times.index(cur_time) if cur_time in times else 0 + window = probs[idx : idx + 6] + if not window: + return "" + parts = [f"{int(p)}%" if p is not None else "-" for p in window] + return " (next 6h) " + " ".join(parts) + except Exception: + return "" + + +def build_output(lat: float, lon: float, forecast: Dict[str, Any], aqi: Optional[Dict[str, Any]], place: Optional[str] = None) -> Tuple[Dict[str, Any], str]: + cur = forecast.get("current", {}) + cur_units = forecast.get("current_units", {}) + daily = forecast.get("daily", {}) + daily_units = forecast.get("daily_units", {}) + + temp_val = cur.get("temperature_2m") + temp_unit = cur_units.get("temperature_2m", "") + temp_str = f"{int(round(temp_val))}{temp_unit}" if isinstance(temp_val, (int, float)) else "N/A" + temp_col = temp_color(temp_val) + + feels_val = cur.get("apparent_temperature") + feels_unit = cur_units.get("apparent_temperature", "") + feels_str = f"Feels like {int(round(feels_val))}{feels_unit}" if isinstance(feels_val, (int, float)) else "" + + is_day = int(cur.get("is_day", 1) or 1) + code = int(cur.get("weather_code", -1) or -1) + + unavailable = False + + # Fallbacks: if current weather_code is missing/invalid + if code == -1: + try: + times: List[str] = safe_get(forecast, "hourly", "time", default=[]) or [] + codes: List[Optional[int]] = safe_get(forecast, "hourly", "weather_code", default=[]) or [] + cur_time: Optional[str] = safe_get(forecast, "current", "time") + + idx = 0 + if cur_time and times: + # Find nearest index by absolute time difference + try: + ct = datetime.fromisoformat(cur_time) + diffs = [] + for t in times: + try: + diffs.append(abs((datetime.fromisoformat(t) - ct).total_seconds())) + except Exception: + diffs.append(float("inf")) + idx = min(range(len(diffs)), key=lambda i: diffs[i]) if diffs else 0 + except Exception: + # If parsing fails, try exact match fallback + idx = times.index(cur_time) if cur_time in times else 0 + + # Prefer nearest code if valid, else first non-null code in window + hcode = None + if isinstance(codes, list) and codes: + if idx < len(codes) and isinstance(codes[idx], (int, float)): + hcode = int(codes[idx]) + else: + for c in codes: + if isinstance(c, (int, float)): + hcode = int(c) + break + if isinstance(hcode, int): + code = hcode + log_debug(f"Fallback hourly weather_code used: code={code} idx={idx} cur_time={cur_time}") + except Exception as e: + log_debug(f"Hourly code fallback failed: {e}") + + # Final fallback: no valid code at all + if not isinstance(code, int) or code < 0: + unavailable = True + log_debug("Weather code invalid; setting status to 'Condition Unavailable'") + + # Compute icon/status (override if unavailable) + if unavailable: + icon = WEATHER_ICONS["default"] + status = "Condition Unavailable" + code_for_class = "unavailable" + else: + icon = wmo_to_icon(code, is_day) + status = wmo_to_status(code) + code_for_class = f"wmo-{code} {'day' if is_day else 'night'}" + + # min/max today (index 0) + tmin_val = safe_get(daily, "temperature_2m_min", 0) + tmax_val = safe_get(daily, "temperature_2m_max", 0) + dtemp_unit = daily_units.get("temperature_2m_min", temp_unit) + tmin_str = f"{int(round(tmin_val))}{dtemp_unit}" if isinstance(tmin_val, (int, float)) else "" + tmax_str = f"{int(round(tmax_val))}{dtemp_unit}" if isinstance(tmax_val, (int, float)) else "" + min_max = f" {tmin_str}\t\t {tmax_str}" if tmin_str and tmax_str else "" + + wind_val = cur.get("wind_speed_10m") + wind_unit = cur_units.get("wind_speed_10m", "") + wind_text = f" {int(round(wind_val))}{wind_unit}" if isinstance(wind_val, (int, float)) else "" + + hum_val = cur.get("relative_humidity_2m") + humidity_text = f" {int(hum_val)}%" if isinstance(hum_val, (int, float)) else "" + + vis_val = cur.get("visibility") + visibility_text = f" {format_visibility(vis_val)}" if isinstance(vis_val, (int, float)) else "" + + aqi_val = safe_get(aqi or {}, "current", "european_aqi") + aqi_text = f"AQI {int(aqi_val)}" if isinstance(aqi_val, (int, float)) else "AQI N/A" + + hourly_precip = build_hourly_precip(forecast) + prediction = f"\n\n{hourly_precip}" if hourly_precip else "" + + # Build place string (priority: MANUAL_PLACE > ENV_PLACE > reverse geocode > lat,lon) + place_str = (MANUAL_PLACE or ENV_PLACE or place or f"{lat:.3f}, {lon:.3f}") + location_text = f"{LOC_ICON} {place_str}" + + # Build tooltip (markup or plain) + if TOOLTIP_MARKUP: + # Escape dynamic text to avoid breaking Pango markup + tooltip_text = str.format( + "\t\t{}\t\t\n{}\n{}\n{}\n{}\n\n{}\n{}\n{}{}", + f'{esc(temp_str)}', + f" {icon}", + f"{esc(status)}", + esc(location_text), + f"{esc(feels_str)}" if feels_str else "", + f"{esc(min_max)}" if min_max else "", + f"{esc(wind_text)}\t{esc(humidity_text)}", + f"{esc(visibility_text)}\t{esc(aqi_text)}", + f" {esc(prediction)}" if prediction else "", + ) + else: + lines = [ + f"{icon} {temp_str}", + status, + location_text, + ] + if feels_str: + lines.append(feels_str) + if min_max: + lines.append(min_max) + lines.append(f"{wind_text} {humidity_text}".strip()) + lines.append(f"{visibility_text} {aqi_text}".strip()) + if prediction: + lines.append(hourly_precip) + tooltip_text = "\n".join([ln for ln in lines if ln]) + + # Build main text with colorful icon and colored temp; prefer larger icon if emoji + if ACTIVE_ICON_STYLE == "emoji": + icon_text = f"{icon}" + else: + # Colorize mono icons subtly + icon_text = f"{icon}" + + temp_text = f"{esc(temp_str)}" + + out_data = { + "text": f"{icon_text} {temp_text}", + "alt": status, + "tooltip": tooltip_text, + "class": code_for_class, + } + + simple_weather = ( + f"{icon} {status}\n" + + f" {temp_str} ({feels_str})\n" + + (f"{wind_text} \n" if wind_text else "") + + (f"{humidity_text} \n" if humidity_text else "") + + f"{visibility_text} {aqi_text}\n" + ) + + return out_data, simple_weather + + +def main() -> None: + lat, lon = get_coords() + + # Try cache first + cached = read_api_cache() + if cached and isinstance(cached, dict): + forecast = cached.get("forecast") + aqi = cached.get("aqi") + cached_place = cached.get("place") if isinstance(cached.get("place"), str) else None + place_effective = MANUAL_PLACE or ENV_PLACE or cached_place + try: + out, simple = build_output(lat, lon, forecast, aqi, place_effective) + print(json.dumps(out, ensure_ascii=False)) + write_simple_text_cache(simple) + return + except Exception as e: + print(f"Cached data build failed, refetching: {e}", file=sys.stderr) + + # Fetch fresh + try: + forecast = fetch_open_meteo(lat, lon) + aqi = fetch_aqi(lat, lon) + # Use manual/env place if provided; otherwise reverse geocode + place_effective = MANUAL_PLACE or ENV_PLACE or fetch_place(lat, lon) + write_api_cache({"forecast": forecast, "aqi": aqi, "place": place_effective}) + out, simple = build_output(lat, lon, forecast, aqi, place_effective) + print(json.dumps(out, ensure_ascii=False)) + write_simple_text_cache(simple) + except Exception as e: + print(f"Open-Meteo fetch failed: {e}", file=sys.stderr) + # Last resort: try stale cache without TTL + try: + if os.path.exists(API_CACHE_PATH): + with open(API_CACHE_PATH, "r", encoding="utf-8") as f: + stale = json.load(f) + out, simple = build_output(lat, lon, stale.get("forecast", {}), stale.get("aqi"), stale.get("place") if isinstance(stale.get("place"), str) else None) + print(json.dumps(out, ensure_ascii=False)) + write_simple_text_cache(simple) + return + except Exception as e2: + print(f"Failed to use stale cache: {e2}", file=sys.stderr) + # Fallback minimal output + fallback = { + "text": f"{WEATHER_ICONS['default']} N/A", + "alt": "Unavailable", + "tooltip": "Weather unavailable", + "class": "unavailable", + } + print(json.dumps(fallback, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/home-manager/programs/hypr/waybar/scripts/battery-level.sh b/home-manager/programs/hypr/waybar/scripts/battery-level.sh new file mode 100755 index 0000000..ddde6ea --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/battery-level.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +# Original script by Eric Murphy +# https://github.com/ericmurphyxyz/dotfiles/blob/master/.local/bin/battery-alert +# +# Modified by Jesse Mirabel (@sejjy) +# https://github.com/sejjy/mechabar + +# This script sends a notification when the battery is full, low, or critical. +# icon theme used: tela-circle-icon-theme-dracula +# +# (see the bottom of the script for more information) + +export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus" + +# battery levels +WARNING_LEVEL=20 +CRITICAL_LEVEL=10 + +# get the battery state and percentage using upower (waybar dependency) +BAT_PATH=$(upower -e | grep BAT | head -n 1) +BATTERY_STATE=$(upower -i "$BAT_PATH" | awk '/state:/ {print $2}') +BATTERY_LEVEL=$(upower -i "$BAT_PATH" | awk '/percentage:/ {print $2}' | tr -d '%') + +# prevent multiple notifications +FILE_FULL=/tmp/battery-full +FILE_WARNING=/tmp/battery-warning +FILE_CRITICAL=/tmp/battery-critical + +# remove the files if the battery is no longer in that state +if [ "$BATTERY_STATE" == "discharging" ]; then + rm -f $FILE_FULL +elif [ "$BATTERY_STATE" == "charging" ]; then + rm -f "$FILE_WARNING" "$FILE_CRITICAL" +fi + +# if the battery is full and is plugged in +if [ "$BATTERY_LEVEL" -eq 100 ] && [ "$BATTERY_STATE" == "fully-charged" ] && [ ! -f $FILE_FULL ]; then + notify-send -a "state" "Battery Charged (${BATTERY_LEVEL}%)" "You might want to unplug your PC." -i "battery-full" -r 9991 + touch $FILE_FULL + +# if the battery is low and is discharging +elif [ "$BATTERY_LEVEL" -le $WARNING_LEVEL ] && [ "$BATTERY_STATE" == "discharging" ] && [ ! -f $FILE_WARNING ]; then + notify-send -a "state" "Battery Low (${BATTERY_LEVEL}%)" "You might want to plug in your PC." -u critical -i "battery-caution" -r 9991 -h string:fgcolor:\#fab387 -h string:frcolor:\#fab387 + touch $FILE_WARNING + +# if the battery is critical and is discharging +elif [ "$BATTERY_LEVEL" -le $CRITICAL_LEVEL ] && [ "$BATTERY_STATE" == "discharging" ] && [ ! -f $FILE_CRITICAL ]; then + notify-send -a "state" "Battery Critical (${BATTERY_LEVEL}%)" "Plug in your PC now." -u critical -i "battery-empty" -r 9991 + touch $FILE_CRITICAL +fi + +# systemd service +# Add the following to ~/.config/systemd/user/battery-level.service: + +# [Unit] +# Description=Battery Level Checker +# After=graphical.target +# +# [Service] +# ExecStart=%h/.config/waybar/scripts/battery-level.sh +# Type=oneshot + +# systemd timer +# Add the following to ~/.config/systemd/user/battery-level.timer: + +# [Unit] +# Description=Run Battery Level Checker +# +# [Timer] +# OnBootSec=1min +# OnUnitActiveSec=1min +# Unit=battery-level.service +# +# [Install] +# WantedBy=timers.target + +# enable the timer by running the following commands: +# systemctl --user daemon-reload +# systemctl --user enable --now battery-level.timer diff --git a/home-manager/programs/hypr/waybar/scripts/battery-state.sh b/home-manager/programs/hypr/waybar/scripts/battery-state.sh new file mode 100755 index 0000000..d7cf5fc --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/battery-state.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +# Original script by Eric Murphy +# https://github.com/ericmurphyxyz/dotfiles/blob/master/.local/bin/battery-alert +# +# Modified by Jesse Mirabel (@sejjy) +# https://github.com/sejjy/mechabar + +# This script sends a notification when the battery is charging or discharging. +# icon theme used: tela-circle-icon-theme-dracula +# +# (see the bottom of the script for more information) + +export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus" + +# get the battery state from the udev rule +BATTERY_STATE=$1 + +# get the battery percentage using upower (waybar dependency) +BAT_PATH=$(upower -e | grep BAT | head -n 1) +BATTERY_LEVEL=$(upower -i "$BAT_PATH" | awk '/percentage:/ {print $2}' | tr -d '%') + +# set the battery charging state and icon +case "$BATTERY_STATE" in +"charging") + BATTERY_CHARGING="Charging" + BATTERY_ICON="090-charging" + ;; +"discharging") + BATTERY_CHARGING="Disharging" + BATTERY_ICON="090" + ;; +esac + +# send the notification +notify-send -a "state" "Battery ${BATTERY_CHARGING} (${BATTERY_LEVEL}%)" -u normal -i "battery-${BATTERY_ICON}" -r 9991 + +# udev rule +# Add the following to /etc/udev/rules.d/60-power.rules: + +# ACTION=="change", SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", ENV{DISPLAY}=":0", RUN+="/usr/bin/su -c '$HOME/.config/waybar/scripts/battery-state.sh discharging'" +# ACTION=="change", SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", ENV{DISPLAY}=":0", RUN+="/usr/bin/su -c '$HOME/.config/waybar/scripts/battery-state.sh charging'" + +# the number 60 in the udev rule can be changed to any number between 0 and 99. +# the lower the number, the higher the priority. +# +# $USER does not work, so you have to replace "" with your username. + +# reload udev rules by running the following command: +# sudo udevadm control --reload-rules diff --git a/home-manager/programs/hypr/waybar/scripts/brightness-control.sh b/home-manager/programs/hypr/waybar/scripts/brightness-control.sh new file mode 100755 index 0000000..28fa223 --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/brightness-control.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# Print error message for invalid arguments +print_error() { + cat <<"EOF" +Usage: ./brightnesscontrol.sh +Valid actions are: + i -- ncrease brightness [+2%] + d -- ecrease brightness [-2%] +EOF +} + +# Send a notification with brightness info +send_notification() { + brightness=$(brightnessctl info | grep -oP "(?<=\()\d+(?=%)") + notify-send -a "state" -r 91190 -i "gpm-brightness-lcd" -h int:value:"$brightness" "Brightness: ${brightness}%" -u low +} + +# Get the current brightness percentage and device name +get_brightness() { + brightness=$(brightnessctl -m | grep -o '[0-9]\+%' | head -c-2) + device=$(brightnessctl -m | head -n 1 | awk -F',' '{print $1}' | sed 's/_/ /g; s/\<./\U&/g') # Get device name + current_brightness=$(brightnessctl -m | head -n 1 | awk -F',' '{print $3}') # Get current brightness + max_brightness=$(brightnessctl -m | head -n 1 | awk -F',' '{print $5}') # Get max brightness +} +get_brightness + +# Handle options +while getopts o: opt; do + case "${opt}" in + o) + case $OPTARG in + i) # Increase brightness + if [[ $brightness -lt 10 ]]; then + brightnessctl set +1% + else + brightnessctl set +2% + fi + send_notification + ;; + d) # Decrease brightness + if [[ $brightness -le 1 ]]; then + brightnessctl set 1% + elif [[ $brightness -le 10 ]]; then + brightnessctl set 1%- + else + brightnessctl set 2%- + fi + send_notification + ;; + *) + print_error + ;; + esac + ;; + *) + print_error + ;; + esac +done + +# Determine the icon based on brightness level +get_icon() { + if ((brightness <= 5)); then + icon="" + elif ((brightness <= 15)); then + icon="" + elif ((brightness <= 30)); then + icon="" + elif ((brightness <= 45)); then + icon="" + elif ((brightness <= 55)); then + icon="" + elif ((brightness <= 65)); then + icon="" + elif ((brightness <= 80)); then + icon="" + elif ((brightness <= 95)); then + icon="" + else + icon="" + fi +} + +# Backlight module and tooltip +get_icon +module="${icon} ${brightness}%" + +tooltip="Device Name: ${device}" +tooltip+="\nBrightness: ${current_brightness} / ${max_brightness}" + +echo "{\"text\": \"${module}\", \"tooltip\": \"${tooltip}\"}" diff --git a/home-manager/programs/hypr/waybar/scripts/cpu-temp.sh b/home-manager/programs/hypr/waybar/scripts/cpu-temp.sh new file mode 100755 index 0000000..b6f2355 --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/cpu-temp.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +model=$(awk -F ': ' '/model name/{print $2}' /proc/cpuinfo | head -n 1 | sed 's/@.*//; s/ *\((R)\|(TM)\)//g; s/^[ \t]*//; s/[ \t]*$//') + +# get CPU clock speeds +get_cpu_frequency() { + freqlist=$(awk '/cpu MHz/ {print $4}' /proc/cpuinfo) + maxfreq=$(sed 's/...$//' /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq) + if [ -z "$freqlist" ] || [ -z "$maxfreq" ]; then + echo "--" + return + fi + average_freq=$(echo "$freqlist" | tr ' ' '\n' | awk "{sum+=\$1} END {printf \"%.0f/%s MHz\", sum/NR, $maxfreq}") + echo "$average_freq" +} + +# get CPU temp +get_cpu_temperature() { + temp=$(sensors | awk '/Package id 0/ {print $4}' | awk -F '[+.]' '{print $2}') + if [[ -z "$temp" ]]; then + temp=$(sensors | awk '/Tctl/ {print $2}' | tr -d '+°C') + fi + if [[ -z "$temp" ]]; then + temp="--" + temp_f="--" + else + temp=${temp%.*} + temp_f=$(awk "BEGIN {printf \"%.1f\", ($temp * 9 / 5) + 32}") + fi + # Celsius and Fahrenheit + echo "${temp:---} ${temp_f:---}" +} + +get_temperature_icon() { + temp_value=$1 + if [ "$temp_value" = "--" ]; then + icon="󱔱" # none + elif [ "$temp_value" -ge 80 ]; then + icon="󰸁" # high + elif [ "$temp_value" -ge 70 ]; then + icon="󱃂" # medium + elif [ "$temp_value" -ge 60 ]; then + icon="󰔏" # normal + else + icon="󱃃" # low + fi + echo "$icon" +} + +cpu_frequency=$(get_cpu_frequency) +read -r temp_info < <(get_cpu_temperature) +temp=$(echo "$temp_info" | awk '{print $1}') +temp_f=$(echo "$temp_info" | awk '{print $2}') +thermo_icon=$(get_temperature_icon "$temp") + +# high temp warning +if [ "$temp" == "--" ] || [ "$temp" -ge 80 ]; then + text_output="${thermo_icon} ${temp}°C" +else + text_output="${thermo_icon} ${temp}°C" +fi + +tooltip=":: ${model}\n" +tooltip+="Clock Speed: ${cpu_frequency}\nTemperature: ${temp_f}°F" + +# module and tooltip +echo "{\"text\": \"$text_output\", \"tooltip\": \"$tooltip\"}" diff --git a/home-manager/programs/hypr/waybar/scripts/power-menu.sh b/home-manager/programs/hypr/waybar/scripts/power-menu.sh new file mode 100755 index 0000000..d330c3d --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/power-menu.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Use a dedicated narrow dark theme for the power menu +config="$HOME/.config/rofi/power-menu.rasi" + +actions=$(echo -e " Lock\n Shutdown\n Reboot\n Suspend\n Hibernate\n󰞘 Logout") + +# Display logout menu +selected_option=$(echo -e "$actions" | rofi -dmenu -i -p "Power Menu" -config "${config}" || pkill -x rofi) + +# Perform actions based on the selected option +case "$selected_option" in +*Lock) + loginctl lock-session + ;; +*Shutdown) + systemctl poweroff + ;; +*Reboot) + systemctl reboot + ;; +*Suspend) + systemctl suspend + ;; +*Hibernate) + systemctl hibernate + ;; +*Logout) + loginctl kill-session "$XDG_SESSION_ID" + ;; +esac diff --git a/home-manager/programs/hypr/waybar/scripts/volume-control.sh b/home-manager/programs/hypr/waybar/scripts/volume-control.sh new file mode 100755 index 0000000..76bb2f7 --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/volume-control.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash + +# Define functions +print_error() { + cat <<"EOF" +Usage: ./volumecontrol.sh -[device] +...valid devices are... + i -- input device + o -- output device + p -- player application +...valid actions are... + i -- increase volume [+2] + d -- decrease volume [-2] + m -- mute [x] +EOF + exit 1 +} + +icon() { + vol=$(pactl get-sink-volume @DEFAULT_SINK@ | awk '{print $5}' | sed 's/%//') + mute=$(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}') + + if [ "$mute" = "yes" ] || [ "$vol" -eq 0 ]; then + icon="volume-level-muted" + elif [ "$vol" -lt 33 ]; then + icon="volume-level-low" + elif [ "$vol" -lt 66 ]; then + icon="volume-level-medium" + else + icon="volume-level-high" + fi +} + +send_notification() { + icon + notify-send -a "state" -r 91190 -i "$icon" -h int:value:"$vol" "Volume: ${vol}%" -u low +} + +notify_mute() { + mute=$(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}') + if [ "$mute" = "yes" ]; then + notify-send -a "state" -r 91190 -i "volume-level-muted" "Volume: Muted" -u low + else + icon + notify-send -a "state" -r 91190 -i "$icon" "Volume: Unmuted" -u low + fi +} + +action_volume() { + case "${1}" in + i) + # Increase volume if below 100 + current_vol=$(pactl get-sink-volume @DEFAULT_SINK@ | awk '{print $5}' | sed 's/%//') + if [ "$current_vol" -lt 100 ]; then + new_vol=$((current_vol + 2)) + [ "$new_vol" -gt 100 ] && new_vol=100 + pactl set-sink-volume @DEFAULT_SINK@ "${new_vol}%" + fi + ;; + d) + # Decrease volume if above 0 + current_vol=$(pactl get-sink-volume @DEFAULT_SINK@ | awk '{print $5}' | sed 's/%//') + new_vol=$((current_vol - 2)) + [ "$new_vol" -lt 0 ] && new_vol=0 + pactl set-sink-volume @DEFAULT_SINK@ "${new_vol}%" + ;; + esac +} + +select_output() { + if [ "$@" ]; then + desc="$*" + device=$(pactl list sinks | grep -C2 -F "Description: $desc" | grep Name | cut -d: -f2 | xargs) + if pactl set-default-sink "$device"; then + notify-send -r 91190 "Activated: $desc" + else + notify-send -r 91190 "Error activating $desc" + fi + else + pactl list sinks | grep -ie "Description:" | awk -F ': ' '{print $2}' | sort + fi +} + +# Evaluate device option +while getopts iops: DeviceOpt; do + case "${DeviceOpt}" in + i) + nsink=$(pactl list sources short | awk '{print $2}') + [ -z "${nsink}" ] && echo "ERROR: Input device not found..." && exit 0 + srce="--default-source" + ;; + o) + nsink=$(pactl list sinks short | awk '{print $2}') + [ -z "${nsink}" ] && echo "ERROR: Output device not found..." && exit 0 + srce="" + ;; + p) + nsink=$(playerctl --list-all | grep -w "${OPTARG}") + [ -z "${nsink}" ] && echo "ERROR: Player ${OPTARG} not active..." && exit 0 + # shellcheck disable=SC2034 + srce="${nsink}" + ;; + s) + # Select an output device + select_output "$@" + exit + ;; + *) print_error ;; + esac +done + +# Set default variables +shift $((OPTIND - 1)) + +# Execute action +case "${1}" in +i) action_volume i ;; +d) action_volume d ;; +m) pactl set-sink-mute @DEFAULT_SINK@ toggle && notify_mute && exit 0 ;; +*) print_error ;; +esac + +send_notification diff --git a/home-manager/programs/hypr/waybar/scripts/wifi-status.sh b/home-manager/programs/hypr/waybar/scripts/wifi-status.sh new file mode 100755 index 0000000..2485268 --- /dev/null +++ b/home-manager/programs/hypr/waybar/scripts/wifi-status.sh @@ -0,0 +1,177 @@ +#!/usr/bin/env bash + +# This script gathers detailed Wi-Fi connection information. +# It collects the following fields: +# +# - SSID (Service Set Identifier): The name of the Wi-Fi network you +# are currently connected to. Example: "My_Network" +# +# - IP Address: The IP address assigned to the device by the router. +# This is typically a private IP within the local network. Example: +# "192.168.1.29/24" (with subnet mask) +# +# - Router (Gateway): The IP address of the router (default gateway) +# that your device uses to communicate outside the local network. +# Example: "192.168.1.1" +# +# - MAC Address: The unique Media Access Control address of the local +# device's Wi-Fi adapter. Example: "F8:34:41:07:1B:65" +# +# - Security: The encryption protocol being used to secure your Wi-Fi +# connection. Common security protocols include: +# - WPA2 (Wi-Fi Protected Access 2): The most commonly used security +# standard, offering strong encryption (AES). +# - WPA3: The latest version, providing even stronger security, +# especially in public or open networks. +# - WEP (Wired Equivalent Privacy): An outdated and insecure protocol +# that should not be used. +# Example: "WPA2" indicates that the connection is secured using WPA2 +# with AES encryption. +# +# - BSSID (Basic Service Set Identifier): The MAC address of the Wi-Fi +# access point you are connected to. Example: "A4:22:49:DA:91:A0" +# +# - Channel: The wireless channel your Wi-Fi network is using. This is +# associated with the frequency band. Example: "100 (5500 MHz)" +# indicates the channel number (100) and the frequency (5500 MHz), +# which is within the 5 GHz band. +# +# - RSSI (Received Signal Strength Indicator): The strength of the +# Wi-Fi signal, typically in dBm (decibels relative to 1 milliwatt). +# Closer to 0 means stronger signal, with values like -40 dBm being +# very good. Example: "-40 dBm" +# +# - Signal: The signal quality, which is represented as a percentage, +# where higher numbers mean better signal. Example: "100" +# indicates perfect signal strength. +# +# - Rx Rate (Receive Rate): The maximum data rate (in Mbit/s) at which +# the device can receive data from the Wi-Fi access point. Example: +# "866.7 MBit/s" indicates a high-speed connection on a modern +# standard. +# +# - Tx Rate (Transmit Rate): The maximum data rate (in Mbit/s) at +# which the device can send data to the Wi-Fi access point. Example: +# "866.7 MBit/s" +# +# - PHY Mode (Physical Layer Mode): The Wi-Fi protocol or standard in +# use. Common modes include 802.11n, 802.11ac, and 802.11ax (Wi-Fi +# 6). Example: "802.11ac" indicates you're using the 5 GHz band with +# a modern high-speed standard. + +if ! command -v nmcli &>/dev/null; then + echo "{\"text\": \"󰤫\", \"tooltip\": \"nmcli utility is missing\"}" + exit 1 +fi + +# Check if Wi-Fi is enabled +wifi_status=$(nmcli radio wifi) + +if [ "$wifi_status" = "disabled" ]; then + echo "{\"text\": \"󰤮\", \"tooltip\": \"Wi-Fi Disabled\"}" + exit 0 +fi + +wifi_info=$(nmcli -t -f active,ssid,signal,security dev wifi | grep "^yes") + +# If no ESSID is found, set a default value +if [ -z "$wifi_info" ]; then + essid="No Connection" + signal=0 + tooltip="No Connection" +else + # Some defaults + ip_address="127.0.0.1" + # gateway="127.0.0.1" + # mac_address="N/A" + security=$(echo "$wifi_info" | awk -F: '{print $4}') + # bssid="N/A" + chan="N/A" + # rssi="N/A" + # rx_bitrate="" + # tx_bitrate="" + # phy_mode="" + signal=$(echo "$wifi_info" | awk -F: '{print $3}') + + active_device=$(nmcli -t -f DEVICE,STATE device status | + grep -w "connected" | + grep -v -E "^(dummy|lo:|virbr0)" | + awk -F: '{print $1}' | + head -n 1) + + if [ -n "$active_device" ]; then + output=$(nmcli -e no -g ip4.address,ip4.gateway,general.hwaddr device show "$active_device") + + ip_address=$(echo "$output" | sed -n '1p') + # gateway=$(echo "$output" | sed -n '2p') + # mac_address=$(echo "$output" | sed -n '3p') + + line=$(nmcli -e no -t -f active,bssid,chan,freq device wifi | grep "^yes") + + # bssid=$(echo "$line" | awk -F':' '{print $2":"$3":"$4":"$5":"$6":"$7}') + chan=$(echo "$line" | awk -F':' '{print $8}') + freq=$(echo "$line" | awk -F':' '{print $9}') + chan="$chan ($freq)" + + # if command -v iw &>/dev/null; then + # iw_output=$(iw dev "$active_device" station dump) + # rssi=$(echo "$iw_output" | grep "signal:" | awk '{print $2 " dBm"}') + + # Upload speed + # rx_bitrate=$(echo "$iw_output" | grep "rx bitrate:" | awk '{print $3 " " $4}') + + # Download speed + # tx_bitrate=$(echo "$iw_output" | grep "tx bitrate:" | awk '{print $3 " " $4}') + + # Physical Layer Mode + # if echo "$iw_output" | grep -E -q "rx bitrate:.* VHT"; then + # phy_mode="802.11ac" # Wi-Fi 5 + # elif echo "$iw_output" | grep -E -q "rx bitrate:.* HT"; then + # phy_mode="802.11n" # Wi-Fi 4 + # elif echo "$iw_output" | grep -E -q "rx bitrate:.* HE"; then + # phy_mode="802.11ax" # Wi-Fi 6 + # fi + # fi + + # Get the current Wi-Fi ESSID + essid=$(echo "$wifi_info" | awk -F: '{print $2}') + + tooltip=":: ${essid}" + tooltip+="\nIP Address: ${ip_address}" + # tooltip+="\nRouter: ${gateway}" + # tooltip+="\nMAC Address: ${mac_address}" + tooltip+="\nSecurity: ${security}" + # tooltip+="\nBSSID: ${bssid}" + tooltip+="\nChannel: ${chan}" + # tooltip+="\nRSSI: ${rssi}" + tooltip+="\nStrength: ${signal} / 100" + + # if [ -n "$rx_bitrate" ]; then + # tooltip+="\nRx Rate: ${rx_bitrate}" + # fi + + # if [ -n "$tx_bitrate" ]; then + # tooltip+="\nTx Rate: ${tx_bitrate}" + # fi + + # if [ -n "$phy_mode" ]; then + # tooltip+="\nPHY Mode: ${phy_mode}" + # fi + fi +fi + +# Determine Wi-Fi icon based on signal strength +if [ "$signal" -ge 80 ]; then + icon="󰤨" # Strong signal +elif [ "$signal" -ge 60 ]; then + icon="󰤥" # Good signal +elif [ "$signal" -ge 40 ]; then + icon="󰤢" # Weak signal +elif [ "$signal" -ge 20 ]; then + icon="󰤟" # Very weak signal +else + icon="󰤯" # No signal +fi + +# Module and tooltip +echo "{\"text\": \"${icon}\", \"tooltip\": \"${tooltip}\"}" diff --git a/home-manager/programs/hypr/waybar/waybar-TheBlackDon.nix b/home-manager/programs/hypr/waybar/waybar-TheBlackDon.nix new file mode 100644 index 0000000..60df038 --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-TheBlackDon.nix @@ -0,0 +1,563 @@ +{ + pkgs, + lib, + host, + config, + ... +}: let + inherit (import ../../../hosts/${host}/variables.nix) clock24h; + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); +in + with lib; { + # Install any helper scripts shipped in modules/home/waybar/scripts into ~/.config/waybar/scripts + home.file = builtins.listToAttrs (map + (name: { + name = ".config/waybar/scripts/" + name; + value = { + source = "${scriptsDir}/${name}"; + executable = true; + }; + }) + scripts); + + programs.waybar = { + enable = true; + package = pkgs.waybar; + + settings = [ + { + layer = "top"; + position = "top"; + exclusive = true; + passthrough = false; + "fixed-center" = true; + ipc = true; + + height = 34; + spacing = 0; + margin = 2; + "margin-top" = 0; + "margin-bottom" = 0; + "margin-left" = 0; + "margin-right" = 0; + + modules-left = [ + "custom/startmenu" + "custom/qs_wallpapers_apply" + "custom/qs_vid_wallpapers_apply" + "hyprland/workspaces" + "idle_inhibitor" + "tray" + "mpd#2" + "mpd#3" + "mpd#4" + "mpd" + ]; + modules-center = ["cpu" "cpu#2" "memory" "memory#2" "disk" "disk#2"]; + modules-right = [ + "custom/themes" + "pulseaudio" + "pulseaudio#2" + "backlight" + "backlight#2" + "battery" + "battery#2" + "bluetooth" + "bluetooth#2" + "network" + "network#2" + "clock" + "clock#2" + "custom/power" + ]; + + # --- Modules (ported from provided BlackDog config) --- + backlight = { + interval = 2; + format = "{icon}"; + "format-icons" = ["" "" "" "" "" "" ""]; + "on-scroll-up" = "light -A 5%"; + "on-scroll-down" = "light -U 5%"; + "smooth-scrolling-threshold" = 1; + }; + "backlight#2" = { + interval = 2; + format = "{percent}%"; + "on-scroll-up" = "light -A 5%"; + "on-scroll-down" = "light -U 5%"; + "smooth-scrolling-threshold" = 1; + }; + + battery = { + interval = 60; + "full-at" = 100; + "design-capacity" = false; + states = { + good = 95; + warning = 30; + critical = 15; + }; + format = "{icon}"; + "format-charging" = ""; + "format-plugged" = "ﮣ"; + "format-full" = ""; + "format-icons" = ["" "" "" "" "" "" "" "" ""]; + "format-time" = "{H}h {M}min"; + tooltip = true; + }; + "battery#2" = { + interval = 60; + "full-at" = 100; + "design-capacity" = false; + states = { + good = 95; + warning = 30; + critical = 15; + }; + format = "{capacity}%"; + "format-charging" = "{capacity}%"; + "format-plugged" = "{capacity}%"; + "format-full" = "Full"; + "format-alt" = "{time}"; + "format-time" = "{H}h {M}min"; + tooltip = true; + }; + + bluetooth = { + format = ""; + "format-on" = ""; + "format-off" = ""; + "format-disabled" = ""; + "format-connected" = ""; + "format-connected-battery" = ""; + tooltip = true; + "tooltip-format" = "{controller_alias}\t{controller_address}"; + "tooltip-format-connected" = "{controller_alias}\t{controller_address}\n\n{device_enumerate}"; + "tooltip-format-enumerate-connected" = "{device_alias}\t{device_address}"; + "on-click" = "$HOME/.config/hypr/scripts/rofi_bluetooth"; + "on-click-right" = "blueman-manager"; + }; + "bluetooth#2" = { + format = "{status}"; + "format-on" = "{status}"; + "format-off" = "{status}"; + "format-disabled" = "{status}"; + "format-connected" = "{device_alias}"; + "format-connected-battery" = "{device_alias}, {device_battery_percentage}%"; + tooltip = true; + "tooltip-format" = "{controller_alias}\t{controller_address}"; + "tooltip-format-connected" = "{controller_alias}\t{controller_address}\n\n{device_enumerate}"; + "tooltip-format-enumerate-connected" = "{device_alias}\t{device_address}"; + "on-click" = "$HOME/.config/hypr/scripts/rofi_bluetooth"; + "on-click-right" = "blueman-manager"; + }; + + clock = { + "tooltip-format" = "{:%B %Y}\n{calendar}"; + format = ""; + }; + "clock#2" = { + interval = 60; + "tooltip-format" = "{:%B %Y}\n{calendar}"; + format = + if clock24h + then "{:%H:%M}" + else "{:%I:%M %p}"; + "format-alt" = "{:%a %b %d, %G}"; + }; + + cpu = { + interval = 5; + format = ""; + }; + "cpu#2" = { + interval = 5; + format = "{usage}%"; + }; + + "custom/themes" = { + format = ""; + tooltip = false; + "on-click" = "$HOME/.config/hypr/theme/theme.sh --pywal"; + "on-click-right" = "$HOME/.config/hypr/theme/theme.sh --default"; + }; + + # Start menu aligned to project standard + "custom/startmenu" = { + tooltip = true; + "tooltip-format" = "App menu"; + format = ""; + on-click = "launch-nwg-menu"; + "on-click-right" = "nwg-drawer -mr 225 -ml 225 -mt 200 -mb 200 -is 48 --spacing 15"; + }; + + # Power menu aligned to project standard (qs-wlogout + rofi fallback) + "custom/power" = { + tooltip = true; + "tooltip-format" = "Left Click: Power Menu (qs-wlogout)\nRight Click: Rofi Power Menu"; + format = " ⏻ "; + on-click = "qs-wlogout"; + "on-click-right" = "~/.config/waybar/scripts/power-menu.sh"; + }; + + disk = { + interval = 30; + format = ""; + }; + "disk#2" = { + interval = 30; + format = "{free}"; + }; + + memory = { + interval = 10; + format = ""; + }; + "memory#2" = { + interval = 10; + format = "{used:0.1f}G"; + }; + + # Optional Spotify module (kept from source; may require script) + "custom/spotify" = { + exec = "$HOME/.config/hypr/waybar/spotify"; + interval = 1; + format = "{}"; + tooltip = true; + "max-length" = 40; + "on-click" = "playerctl play-pause"; + "on-click-middle" = "playerctl previous"; + "on-click-right" = "playerctl next"; + "on-scroll-up" = "playerctl position 05+"; + "on-scroll-down" = "playerctl position 05-"; + "smooth-scrolling-threshold" = 1; + }; + + # MPD transport cluster + mpd = { + interval = 2; + "unknown-tag" = "N/A"; + format = "{artist} - {title} | 祥 {elapsedTime:%M:%S}"; + "format-disconnected" = "Disconnected"; + "format-paused" = "{artist} - {title}"; + "format-stopped" = "Stopped"; + "tooltip-format" = "MPD (connected)"; + "tooltip-format-disconnected" = "MPD (disconnected)"; + "on-click" = "mpc toggle"; + "on-scroll-up" = "mpc seek +00:00:01"; + "on-scroll-down" = "mpc seek -00:00:01"; + "smooth-scrolling-threshold" = 1; + }; + "mpd#2" = { + format = "玲"; + "format-disconnected" = "玲"; + "format-paused" = "玲"; + "format-stopped" = "玲"; + "on-click" = "mpc prev"; + }; + "mpd#3" = { + interval = 1; + format = "{stateIcon}"; + "format-disconnected" = ""; + "format-paused" = "{stateIcon}"; + "format-stopped" = ""; + "state-icons" = { + paused = ""; + playing = ""; + }; + "on-click" = "mpc toggle"; + }; + "mpd#4" = { + format = "怜"; + "format-disconnected" = "怜"; + "format-paused" = "怜"; + "format-stopped" = "怜"; + "on-click" = "mpc next"; + }; + + network = { + interval = 5; + "format-wifi" = "直"; + "format-ethernet" = ""; + "format-linked" = ""; + "format-disconnected" = "睊"; + "format-disabled" = "睊"; + "tooltip-format" = " {ifname} via {gwaddr}"; + "on-click" = "$HOME/.config/hypr/scripts/rofi_network"; + }; + "network#2" = { + interval = 5; + "format-wifi" = "{essid}"; + "format-ethernet" = "{ipaddr}/{cidr}"; + "format-linked" = "{ifname} (No IP)"; + "format-disconnected" = "Disconnected"; + "format-disabled" = "Disabled"; + "format-alt" = " {bandwidthUpBits} |  {bandwidthDownBits}"; + "tooltip-format" = " {ifname} via {gwaddr}"; + }; + + pulseaudio = { + format = "{icon}"; + "format-muted" = ""; + "format-bluetooth" = ""; + "format-bluetooth-muted" = ""; + "format-source" = ""; + "format-source-muted" = ""; + "format-icons" = { + headphone = ""; + "hands-free" = "ﳌ"; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = ["" "" ""]; + }; + "scroll-step" = 5.0; + "on-click" = "pulsemixer --toggle-mute"; + "on-click-right" = "pulsemixer --toggle-mute"; + "smooth-scrolling-threshold" = 1; + }; + "pulseaudio#2" = { + format = "{volume}%"; + "format-muted" = "Mute"; + "format-bluetooth" = "{volume}%"; + "format-bluetooth-muted" = "Mute"; + "format-source" = "{volume}%"; + "scroll-step" = 5.0; + "on-click" = "pulsemixer --toggle-mute"; + "on-click-right" = "pulsemixer --toggle-mute"; + "smooth-scrolling-threshold" = 1; + }; + + idle_inhibitor = { + format = "{icon}"; + "format-icons" = { + activated = ""; + deactivated = ""; + }; + timeout = 30; + }; + + "hyprland/workspaces" = { + format = "{icon}"; + "sort-by-number" = true; + "active-only" = false; + "format-icons" = { + "1" = "1"; + "2" = "2"; + "3" = "3"; + "4" = "4"; + "5" = "5"; + "6" = "6"; + "7" = "7"; + "8" = "8"; + "9" = "9"; + "10" = "10"; + urgent = ""; + focused = ""; + default = ""; + }; + on-click = "activate"; + }; + + tray = { + "icon-size" = 16; + spacing = 10; + }; + + # ddubs wallpaper quick actions + "custom/qs_wallpapers_apply" = { + format = ""; + tooltip = true; + "tooltip-format" = "Set image wallpaper"; + on-click = "qs-wallpapers-apply"; + }; + "custom/qs_vid_wallpapers_apply" = { + format = ""; + tooltip = true; + "tooltip-format" = "Set video wallpaper"; + on-click = "qs-vid-wallpapers-apply"; + }; + } + ]; + + # --- CSS (colors + styles from provided BlackDog theme, with startmenu selector updated) --- + style = concatStrings [ + '' + /** Colors (from colors.css) **/ + @define-color background #0a0a0a; + @define-color background-alt1 #181818; + @define-color background-alt2 #232323; + @define-color foreground #ddd8d0; + @define-color selected #E29E5D; + @define-color black #0a0a0a; + @define-color red #C53A31; + @define-color green #867971; + @define-color yellow #8F847B; + @define-color blue #E29E5D; + @define-color magenta #7C7987; + @define-color cyan #A19993; + @define-color white #ddd8d0; + + /** Fonts **/ + * { + font-family: "JetBrains Mono", "Symbols Nerd Font", Iosevka, archcraft, sans-serif; + font-size: 14px; + } + + /** Waybar Window **/ + window#waybar { + background-color: @background; + color: @foreground; + border-bottom: 2px solid @background-alt1; + transition-property: background-color; + transition-duration: .5s; + } + window#waybar.hidden { opacity: 0.5; } + + /** Custom **/ + #custom-startmenu { + background-color: @background-alt1; + color: @magenta; + font-size: 18px; + border-radius: 0px 14px 0px 0px; + margin: 0px 0px 0px 0px; + padding: 2px 8px 2px 8px; + } + #custom-themes { background-color: @selected; } + #custom-power { + background-color: @red; + font-size: 16px; + } + #custom-power, #custom-themes { + color: @background; + border-radius: 10px; + margin: 6px 6px 6px 0px; + padding: 2px 8px 2px 8px; + } + + /** Idle Inhibitor **/ + #idle_inhibitor { + background-color: @green; + color: @background; + border-radius: 10px; + margin: 6px 0px 6px 6px; + padding: 4px 6px; + } + #idle_inhibitor.deactivated { background-color: @red; } + + /** Tray **/ + #tray { + background-color: @background-alt1; + border-radius: 10px; + margin: 6px 0px 6px 6px; + padding: 4px 6px; + } + #tray > .passive { -gtk-icon-effect: dim; } + #tray > .needs-attention { -gtk-icon-effect: highlight; } + + /** MPD **/ + @keyframes gradient { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } + } + #mpd { color: @foreground; font-size: 12px; font-weight: bold; } + #mpd.disconnected, #mpd.stopped { color: @red; } + #mpd.playing { color: @cyan; } + #mpd.2 { border-radius: 10px 0px 0px 10px; margin: 6px 0px 6px 6px; padding: 4px 6px 4px 10px; } + #mpd.3 { margin: 6px 0px 6px 0px; padding: 4px; } + #mpd.4 { border-radius: 0px 10px 10px 0px; margin: 6px 6px 6px 0px; padding: 4px 10px 4px 6px; } + #mpd.2,#mpd.3,#mpd.4 { background-color: @background-alt1; font-size: 14px; } + + /** Spotify **/ + #custom-spotify { + background-color: @background-alt1; + color: @foreground; + border-radius: 10px; + margin: 6px 0px 6px 6px; + padding: 4px 8px; + font-size: 12px; + font-weight: bold; + } + #custom-spotify.paused { color: @foreground; } + #custom-spotify.playing { + background: linear-gradient(90deg, @magenta 25%, @red 50%, @yellow 75%, @cyan 100%); + background-size: 300% 300%; + animation: gradient 10s ease infinite; + color: @background; + } + #custom-spotify.offline { color: @red; } + + /** CPU/Memory/Disk **/ + #cpu { color: @red; } + #memory { color: @green; } + #disk { color: @yellow; } + + /** Pulseaudio **/ + #pulseaudio { color: @blue; } + #pulseaudio.bluetooth { color: @cyan; } + #pulseaudio.muted { color: @red; } + + /** Backlight **/ + #backlight { color: @magenta; } + + /** Battery **/ + #battery { color: @cyan; } + @keyframes blink { to { color: @foreground; } } + #battery.critical:not(.charging) { background-color: @background-alt2; } + #battery.2.critical:not(.charging) { + background-color: @background-alt1; + color: @red; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; + } + + /** Network **/ + #network { color: @yellow; } + #network.disconnected, #network.disabled { color: @red; } + + /** Bluetooth **/ + #bluetooth { color: @green; } + #bluetooth.disabled, #bluetooth.off { color: @red; } + + /** Clock **/ + #clock { color: @blue; } + + /** Workspaces **/ + #workspaces { + background-color: @background; + border-radius: 10px; + margin-left: 6px; + margin-bottom: 2px; + padding: 0px; + } + #workspaces button { color: @foreground; } + #workspaces button.active { color: @red; } + #workspaces button.urgent { color: @green; } + #workspaces button.hidden { color: @yellow; } + + /** Common style **/ + #backlight, #battery, #clock, #cpu, #disk, #memory, #pulseaudio, #network, #bluetooth { + background-color: @background-alt2; + border-radius: 10px 0px 0px 10px; + margin: 6px 0px 6px 0px; + padding: 4px 6px; + } + #backlight.2, #battery.2, #clock.2, #cpu.2, #disk.2, #memory.2, #pulseaudio.2, #network.2, #bluetooth.2 { + background-color: @background-alt1; + color: @foreground; + font-size: 12px; + font-weight: bold; + border-radius: 0px 10px 10px 0px; + margin: 6px 6px 6px 0px; + padding: 5px 6px 4px 6px; + } + '' + ]; + }; + } diff --git a/home-manager/programs/hypr/waybar/waybar-ddubs-2.nix b/home-manager/programs/hypr/waybar/waybar-ddubs-2.nix new file mode 100644 index 0000000..2cce101 --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-ddubs-2.nix @@ -0,0 +1,356 @@ +{ + pkgs, + config, + lib, + ... +}: let + terminal = "kitty"; + base00 = "0F1419"; + base01 = "131721"; + base03 = "3E4B59"; + base05 = "E6E1CF"; + base06 = "E6E1CF"; + base07 = "F3F4F5"; + base08 = "F07178"; + base09 = "FF8F40"; + base0A = "FFB454"; + base0B = "B8CC52"; + base0C = "95E6CB"; + base0D = "59C2FF"; + base0E = "D2A6FF"; + base0F = "E6B673"; +in + with lib; { + # Configure & Theme Waybar + programs.waybar = { + enable = true; + package = pkgs.waybar; + settings = [ + { + layer = "top"; + position = "top"; + + modules-left = ["custom/startmenu" "tray" "hyprland/window"]; + modules-center = ["hyprland/workspaces"]; + modules-right = ["idle_inhibitor" "custom/notification" "pulseaudio" "battery" "clock" "custom/exit"]; + + "hyprland/workspaces" = { + format = "{name}"; + format-icons = { + default = " "; + active = " "; + urgent = " "; + }; + on-scroll-up = "hyprctl dispatch workspace e+1"; + on-scroll-down = "hyprctl dispatch workspace e-1"; + }; + "clock" = { + format = '' {:%H:%M}''; + /* + ''{: %I:%M %p}''; + */ + tooltip = true; + tooltip-format = "{:%A, %d.%B %Y }{calendar}"; + }; + "hyprland/window" = { + max-length = 60; + separate-outputs = false; + }; + "memory" = { + interval = 5; + format = " {}%"; + tooltip = true; + on-click = "${terminal} -e btop"; + }; + "cpu" = { + interval = 5; + format = " {usage:2}%"; + tooltip = true; + on-click = "${terminal} -e btop"; + }; + "disk" = { + format = " {free}"; + tooltip = true; + # Not working with zaneyos window open then closes + #on-click = "${terminal} -e sh -c df -h ; read"; + }; + "network" = { + format-icons = ["󰤯" "󰤟" "󰤢" "󰤥" "󰤨"]; + format-ethernet = " {bandwidthDownBits}"; + format-wifi = " {bandwidthDownBits}"; + format-disconnected = "󰤮"; + tooltip = false; + on-click = "${terminal} -e btop"; + }; + "tray" = { + spacing = 12; + }; + "pulseaudio" = { + format = "{icon} {volume}% {format_source}"; + format-bluetooth = "{volume}% {icon} {format_source}"; + format-bluetooth-muted = " {icon} {format_source}"; + format-muted = " {format_source}"; + format-source = " {volume}%"; + format-source-muted = ""; + format-icons = { + headphone = ""; + hands-free = ""; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = ["" "" ""]; + }; + on-click = "pavucontrol"; + }; + "custom/exit" = { + tooltip = false; + format = "⏻"; + on-click = "sleep 0.1 && wlogout"; + }; + "custom/startmenu" = { + tooltip = false; + format = " "; + # exec = "rofi -show drun"; + on-click = "rofi -show drun"; + }; + "idle_inhibitor" = { + format = "{icon}"; + format-icons = { + activated = " "; + deactivated = " "; + }; + tooltip = "true"; + }; + "custom/notification" = { + tooltip = false; + format = "{icon} {}"; + format-icons = { + notification = ""; + none = ""; + dnd-notification = ""; + dnd-none = ""; + inhibited-notification = ""; + inhibited-none = ""; + dnd-inhibited-notification = ""; + dnd-inhibited-none = ""; + }; + return-type = "json"; + exec-if = "which swaync-client"; + exec = "swaync-client -swb"; + on-click = "swaync-client -t"; + escape = true; + }; + "battery" = { + states = { + warning = 30; + critical = 15; + }; + format = "{icon} {capacity}%"; + format-charging = "󰂄 {capacity}%"; + format-plugged = "󱘖 {capacity}%"; + format-icons = ["󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹"]; + on-click = ""; + tooltip = false; + }; + } + ]; + style = concatStrings [ + '' + * { + font-size: 16pt; + font-family: JetBrainsMono Nerd Font, Font Awesome, sans-serif; + font-weight: bold; + } + window#waybar { + /* + + background-color: rgba(26,27,38,0); + border-bottom: 1px solid rgba(26,27,38,0); + border-radius: 0px; + color: #${base0F}; + */ + + background-color: rgba(26,27,38,0); + border-bottom: 1px solid rgba(26,27,38,0); + border-radius: 0px; + color: #${base0F}; + } + #workspaces { + /* + Eternal + background: linear-gradient(180deg, #${base00}, #${base01}); + margin: 5px 5px 5px 0px; + padding: 0px 10px; + border-radius: 0px 15px 15px 0px; + border: 0px; + font-style: normal; + color: #${base00}; + */ + background: linear-gradient(45deg, #${base01}, #${base01}); + margin: 5px; + padding: 0px 1px; + border-radius: 15px; + border: 0px; + font-style: normal; + color: #${base00}; + } + #workspaces button { + padding: 0px 5px; + margin: 4px 3px; + border-radius: 15px; + border: 0px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 0.5; + transition: all 0.3s ease-in-out; + } + #workspaces button.active { + padding: 0px 5px; + margin: 4px 3px; + border-radius: 15px; + border: 0px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 1.0; + min-width: 40px; + transition: all 0.3s ease-in-out; + } + #workspaces button:hover { + border-radius: 15px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 0.8; + } + tooltip { + background: #${base00}; + border: 1px solid #${base0E}; + border-radius: 10px; + } + tooltip label { + color: #${base07}; + } + #window { + /* + Eternal + color: #${base05}; + background: #${base00}; + border-radius: 15px; + margin: 5px; + padding: 2px 20px; + */ + margin: 5px; + padding: 2px 20px; + color: #${base05}; + background: #${base01}; + border-radius: 15px 15px 15px 15px; + } + #memory { + color: #${base0F}; + /* + Eternal + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + */ + background: #${base01}; + margin: 5px; + padding: 2px 20px; + border-radius: 15px 15px 15px 15px; + } + #clock { + color: #${base0B}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + } + #idle_inhibitor { + color: #${base0A}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 3px; + padding: 2px 20px; + } + #cpu { + color: #${base07}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + } + #disk { + color: #${base0F}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + } + #battery { + color: #${base08}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + } + #network { + color: #${base09}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + } + #tray { + color: #${base05}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 15px; + } + #pulseaudio { + color: #${base0D}; + /* + Eternal + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + */ + background: #${base01}; + margin: 4px; + padding: 2px 20px; + border-radius: 15px 15px 15px 15px; + } + #custom-notification { + color: #${base0C}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + } + #custom-startmenu { + color: #${base0E}; + background: #${base00}; + border-radius: 0px 15px 15px 0px; + margin: 5px 5px 5px 0px; + padding: 2px 20px; + } + #idle_inhibitor { + color: #${base09}; + background: #${base00}; + border-radius: 15px 15px 15px 15px; + margin: 5px; + padding: 2px 20px; + } + #custom-exit { + color: #${base0E}; + background: #${base00}; + border-radius: 15px 0px 0px 15px; + margin: 5px 0px 5px 5px; + padding: 2px 20px; + } + '' + ]; + }; + } diff --git a/home-manager/programs/hypr/waybar/waybar-jak-catppuccin.nix b/home-manager/programs/hypr/waybar/waybar-jak-catppuccin.nix new file mode 100644 index 0000000..aaa5e29 --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-jak-catppuccin.nix @@ -0,0 +1,959 @@ +{pkgs, ...}: let + # Install any helper scripts shipped in modules/home/waybar/scripts into ~/.config/waybar/scripts + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); + + # Inline, improved Cava script packaged via Nix so we don't rely on an external bash file + waybarCava = pkgs.writeShellScriptBin "WaybarCava" '' + set -euo pipefail + + # Ensure cava exists + if ! command -v cava >/dev/null 2>&1; then + echo "cava not found in PATH" >&2 + exit 1 + fi + + # Characters for vertical bars (0..7) + bar="▁▂▃▄▅▆▇█" + + # Build sed script that: + # - strips semicolons (cava RAW ASCII delimiter) + # - maps digits 0..7 to the corresponding glyph in $bar + dict="s/;//g" + bar_length=''${#bar} + for ((i = 0; i < bar_length; i++)); do + dict+=";s/$i/''${bar:$i:1}/g" + done + + # Single-instance guard (kill prior instance cleanly) + RUNTIME_DIR="''${XDG_RUNTIME_DIR:-/tmp}" + pidfile="$RUNTIME_DIR/waybar-cava.pid" + if [ -f "$pidfile" ]; then + oldpid=$(cat "$pidfile" || true) + if [ -n "''${oldpid:-}" ] && kill -0 "$oldpid" 2>/dev/null; then + kill "$oldpid" 2>/dev/null || true + # Give the old pipeline a moment to exit + sleep 0.1 || true + fi + fi + echo $$ > "$pidfile" + + # Use a unique temporary config and clean it up on exit + config_file=$(mktemp "''${RUNTIME_DIR}/waybar-cava.XXXXXX.conf") + cleanup() { + rm -f "$config_file" "$pidfile" + } + trap cleanup EXIT INT TERM + + cat >"$config_file" <" = " "; + "title<.*reddit.*>" = " "; + "title<.*[Hh]elium.*>" = " "; + "class" = " "; + "class" = "󰰷 "; + "class" = " "; + "class" = " "; + "class" = " "; + "class" = " "; + "class" = "🦁 "; + "class" = " "; + "class" = "🦊 "; + "class" = " "; + "class" = " "; + "class" = " 󰊠"; + "class" = " "; + "class" = "󰰭 "; + "class<[Tt]hunderbird|[Tt]hunderbird-esr>" = " "; + "class" = " "; + "title<.*gmail.*>" = "󰊫 "; + "class<[Tt]elegram-desktop|org.telegram.desktop|io.github.tdesktop_x64.TDesktop>" = " "; + "class" = " "; + "title<.*whatsapp.*>" = " "; + "title<.*zapzap.*>" = " "; + "title<.*messenger.*>" = " "; + "title<.*facebook.*>" = " "; + "class<[Ss]ignal|signal-desktop|org.signal.Signal>" = "󰍩 "; + "title<.*Signal.*>" = "󰍩 "; + "title<.*ChatGPT.*>" = "󰚩 "; + "title<.*deepseek.*>" = "󰚩 "; + "title<.*qwen.*>" = "󰚩 "; + "class" = "󰅳 "; + "class" = " "; + "class" = " "; + "class" = " "; + "class" = "󰎆 "; + "title<.*Picture-in-Picture.*>" = " "; + "title<.*youtube.*>" = " "; + "class" = "󰕼 "; + "class<[Kk]denlive|org.kde.kdenlive>" = "🎬 "; + "title<.*Kdenlive.*>" = "🎬 "; + "title<.*cmus.*>" = " "; + "class<[Ss]potify>" = " "; + "class" = " "; + "class<.virt-manager-wrapped>" = " "; + "class" = " "; + "class" = "💽 "; + "title" = "💽 "; + "class" = "🖥️ "; + "class" = "󰨞 "; + "class" = "󰵁"; + "class" = "󰅩 "; + "title<.*github.*>" = " "; + "class" = " "; + "class" = " "; + "class" = "󰏆 "; + "class" = " "; + "title<.*nvim ~.*>" = " "; + "title<.*vim.*>" = " "; + "title<.*nvim.*>" = " "; + "title<.*Discord.*>" = " "; + "title<.*figma.*>" = " "; + "title<.*jira.*>" = " "; + "class" = " "; + "class" = " "; + "class" = "󰒃 "; + "class" = " "; + "class<[Pp]avucontrol|org.pulseaudio.pavucontrol>" = "󱡫 "; + "class" = " "; + "class" = "󰝰 "; + "class" = ""; + "class" = " "; + "class" = "📱 "; + "class" = " "; + "class" = "󰓃"; + "class" = ""; + "class" = "󰹛"; + + "class" = " "; + "title<^Bazaar$>" = " "; + + "class" = " "; + "title<^satty$>" = " "; + + "class<[Bb]ox[Bb]uddy|io.github.dvlv.boxbuddy|io.github.dvlv.BoxBuddy>" = " "; + "title<.*BoxBuddy.*>" = " "; + + # qs-* apps + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title<^Wallpapers$>" = " "; + "title<^Video Wallpapers$>" = " "; + "title<^qs-wlogout$>" = " "; + }; + }; + + # ---------- Groups (from ModulesGroups) ---------- + "group/app_drawer" = { + orientation = "inherit"; + drawer = { + "transition-duration" = 500; + "children-class" = "custom/menu"; + "transition-left-to-right" = true; + }; + modules = [ + "custom/menu" + "custom/file_manager" + "custom/tty" + "custom/browser" + ]; + }; + + "group/mobo_drawer" = { + orientation = "inherit"; + drawer = { + "transition-duration" = 500; + "children-class" = "cpu"; + "transition-left-to-right" = true; + }; + modules = [ + "temperature" + "cpu" + "power-profiles-daemon" + "memory" + "disk" + ]; + }; + + "group/laptop" = { + orientation = "inherit"; + modules = ["battery"]; + }; + + "group/audio" = { + orientation = "inherit"; + drawer = { + "transition-duration" = 500; + "children-class" = "pulseaudio"; + "transition-left-to-right" = true; + }; + modules = [ + "pulseaudio" + "pulseaudio#microphone" + ]; + }; + + "group/status" = { + orientation = "inherit"; + drawer = { + "transition-duration" = 500; + "children-class" = "custom/power"; + "transition-left-to-right" = false; + }; + modules = [ + "custom/power" + "custom/lock" + "keyboard-state" + ]; + }; + + # ---------- Custom modules (from ModulesCustom) ---------- + "custom/weather" = { + return-type = "json"; + exec = "sh -lc 'WEATHER_ICON_STYLE=emoji WEATHER_TOOLTIP_MARKUP=1 ~/.local/bin/weather'"; + interval = 600; + tooltip = true; + }; + "custom/qs_wallpapers_apply" = { + # Image wallpaper apply (qs-wallpapers-apply) + format = " "; + "on-click" = "qs-wallpapers-apply"; + tooltip = true; + "tooltip-format" = "Set wallpaper"; + }; + "custom/qs_vid_wallpapers_apply" = { + # Video wallpaper apply (qs-vid-wallpapers-apply) + format = " "; + "on-click" = "qs-vid-wallpapers-apply"; + tooltip = true; + "tooltip-format" = "Set video wallpaper"; + }; + "custom/hypridle" = { + format = "󱫗 "; + "return-type" = "json"; + escape = true; + "exec-on-event" = true; + interval = 60; + exec = "$HOME/.config/hypr/scripts/Hypridle.sh status"; + "on-click" = "$HOME/.config/hypr/scripts/Hypridle.sh toggle"; + "on-click-right" = "hyprlock"; + }; + "custom/lock" = { + format = "󰌾"; + "on-click" = "$HOME/.config/hypr/scripts/LockScreen.sh"; + tooltip = true; + "tooltip-format" = "󰷛 Screen Lock"; + }; + "custom/startmenu" = { + tooltip = true; + "tooltip-format" = "App menu"; + format = ""; + # on-click = "rofi -show drun"; + on-click = "launch-nwg-menu"; + "on-click-right" = "nwg-drawer -mr 225 -ml 225 -mt 200 -mb 200 -is 48 --spacing 15"; + }; + + # Integrated CAVA visualizer using the inline script above + "custom/cava_mviz" = { + exec = "${waybarCava}/bin/WaybarCava"; + format = "[ {} ]"; + }; + + "custom/playerctl" = { + format = "{}"; + "return-type" = "json"; + "max-length" = 25; + exec = "playerctl -a metadata --format '{\"text\": \"{{artist}} {{markup_escape(title)}}\", \"tooltip\": \"{{playerName}} : {{markup_escape(title)}}\", \"alt\": \"{{status}}\", \"class\": \"{{status}}\"}' -F"; + "on-click-middle" = "playerctl play-pause"; + "on-click" = "playerctl previous"; + "on-click-right" = "playerctl next"; + "scroll-step" = 5.0; + "on-scroll-up" = "$HOME/.config/hypr/scripts/Volume.sh --inc"; + "on-scroll-down" = "$HOME/.config/hypr/scripts/Volume.sh --dec"; + "smooth-scrolling-threshold" = 1; + }; + + "custom/power" = { + format = " ⏻ "; + "on-click" = "qs-wlogout"; + "on-click-right" = "~/.config/waybar/scripts/power-menu.sh"; + tooltip = true; + "tooltip-format" = "Power menu: Left-click for QS logout, Right-click for rofi power menu"; + }; + "custom/reboot" = { + format = "󰜉"; + "on-click" = "systemctl reboot"; + tooltip = true; + "tooltip-format" = "Left Click: Reboot"; + }; + "custom/quit" = { + format = "󰗼"; + "on-click" = "hyprctl dispatch exit"; + tooltip = true; + "tooltip-format" = "Left Click: Exit Hyprland"; + }; + + "custom/swaync" = { + tooltip = true; + "tooltip-format" = "Left Click: Launch Notification Center\nRight Click: Do not Disturb"; + format = "{} {icon} "; + "format-icons" = { + notification = ""; + none = ""; + "dnd-notification" = ""; + "dnd-none" = ""; + "inhibited-notification" = ""; + "inhibited-none" = ""; + "dnd-inhibited-notification" = ""; + "dnd-inhibited-none" = ""; + }; + "return-type" = "json"; + "exec-if" = "which swaync-client"; + exec = "swaync-client -swb"; + "on-click" = "systemctl --user start swaync.service; swaync-client -t"; + "on-click-right" = "systemctl --user start swaync.service; swaync-client -d"; + escape = true; + }; + + # Separators + "custom/separator#dot" = { + format = ""; + interval = "once"; + tooltip = false; + }; + "custom/separator#dot-line" = { + format = ""; + interval = "once"; + tooltip = false; + }; + "custom/separator#line" = { + format = "|"; + interval = "once"; + tooltip = false; + }; + "custom/separator#blank" = { + format = ""; + interval = "once"; + tooltip = false; + }; + "custom/separator#blank_2" = { + format = " "; + interval = "once"; + tooltip = false; + }; + "custom/separator#blank_3" = { + format = " "; + interval = "once"; + tooltip = false; + }; + } + ]; + + # Consolidated style (Catppuccin Mocha) inlined + style = let + c = catppuccinColors; + in '' + @define-color rosewater ${c.rosewater}; + @define-color flamingo ${c.flamingo}; + @define-color pink ${c.pink}; + @define-color mauve ${c.mauve}; + @define-color red ${c.red}; + @define-color maroon ${c.maroon}; + @define-color peach ${c.peach}; + @define-color yellow ${c.yellow}; + @define-color green ${c.green}; + @define-color teal ${c.teal}; + @define-color sky ${c.sky}; + @define-color sapphire ${c.sapphire}; + @define-color blue ${c.blue}; + @define-color lavender ${c.lavender}; + @define-color text ${c.text}; + @define-color subtext1 ${c.subtext1}; + @define-color subtext0 ${c.subtext0}; + @define-color overlay2 ${c.overlay2}; + @define-color overlay1 ${c.overlay1}; + @define-color overlay0 ${c.overlay0}; + @define-color surface2 ${c.surface2}; + @define-color surface1 ${c.surface1}; + @define-color surface0 ${c.surface0}; + @define-color base ${c.base}; + @define-color mantle ${c.mantle}; + @define-color crust ${c.crust}; + + * { + font-family: "JetBrainsMono Nerd Font"; + font-weight: bold; + min-height: 0; + font-size: 101%; + font-feature-settings: '"zero", "ss01", "ss02", "ss03", "ss04", "ss05", "cv31"'; + } + + window#waybar { + background-color: @base; + border-radius: 5px; + } + + tooltip { + background: @base; + opacity: 1; + border-radius: 10px; + border-width: 2px; + border-style: solid; + border-color: @sapphire; + } + tooltip label { color: @blue; } + + /* Extra spacing between NixOS start menu and CAVA */ + #custom-startmenu { margin-left: 4px; margin-right: 8px; } + #custom-cava_mviz { margin-left: 4px; } + + #taskbar button, #workspaces button { + color: @surface2; + background-color: transparent; + padding-top: 4px; + padding-bottom: 4px; + padding-right: 6px; + padding-left: 4px; + } + #taskbar button.active { color: @maroon; } + #workspaces button.active { color: @green; } + #taskbar button.focused, #workspaces button.focused { + color: @rosewater; + background: transparent; + border-radius: 15px; + } + #workspaces button.urgent { + color: #11111b; + background: transparent; + border-radius: 15px; + } + #taskbar button:hover, #workspaces button:hover { + background: transparent; + color: @flamingo; + border-radius: 15px; + } + /* Workspaces colors: inactive= @sapphire (pale blue), active= @green, empty= @red */ + #workspaces button { color: @sapphire; } + #workspaces button.empty { color: @red; } + + #backlight, + #backlight-slider, + #battery, + #bluetooth, + #clock, + #cpu, + #disk, + #idle_inhibitor, + #keyboard-state, + #memory, + #mode, + #mpris, + #network, + #power-profiles-daemon, + #pulseaudio, + #pulseaudio-slider, + #taskbar button, + #taskbar, + #temperature, + #tray, + #window, + #wireplumber, + #workspaces, + #custom-backlight, + #custom-browser, + #custom-cava_mviz, + #custom-cycle_wall, + #custom-dot_update, + #custom-file_manager, + #custom-keybinds, + #custom-keyboard, + #custom-light_dark, + #custom-lock, + #custom-hint, + #custom-hypridle, + #custom-menu, + #custom-playerctl, + #custom-power_vertical, + #custom-power, + #custom-quit, + #custom-reboot, + #custom-settings, + #custom-spotify, + #custom-swaync, + #custom-tty, + #custom-updater, + #custom-weather, + #custom-weather.clearNight, + #custom-weather.cloudyFoggyDay, + #custom-weather.cloudyFoggyNight, + #custom-weather.default, + #custom-weather.rainyDay, + #custom-weather.rainyNight, + #custom-weather.severe, + #custom-weather.showyIcyDay, + #custom-weather.snowyIcyNight, + #custom-weather.sunnyDay { + opacity: 1; + padding-top: 4px; + padding-bottom: 4px; + padding-right: 6px; + padding-left: 6px; + } + + #idle_inhibitor.activated { color: @green; } + #idle_inhibitor.deactivated { color: @red; } + #mpris { color: @rosewater; } + #battery { color: @green; padding-left: 15px; border-radius: 15px 0 0 15px; } + @keyframes blink { to { background-color: #ffffff; color: #333333; } } + #battery.critical:not(.charging) { + color: #f53c3c; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; + } + #custom-lock, #custom-power { color: @red; border-radius: 15px; font-weight: bolder; padding-left: 1px; } + #network { background-color: transparent; color: @mauve; } + #backlight { color: @flamingo; } + #custom-weather { color: @green; border-radius: 15px; background-color: transparent; } + #custom-menu { color: #89b4fa; } + #pulseaudio { background-color: transparent; color: @blue; } + #clock, #clock-calender { color: @green; } + /* Use the same pale blue as clock for these icons */ + #custom-qs_wallpapers_apply, + #custom-qs_vid_wallpapers_apply { color: @sapphire; } + + /* Focused window title */ + #window { color: @lavender; } + /* When offscreen (offscreen-css = true) */ + #window.offscreen { color: @overlay1; } + /* Subtle hover effect */ + #window:hover { color: @rosewater; transition: color 120ms ease-in-out; } + + /* Playerctl track text */ + #custom-playerctl { color: @lavender; } + #custom-playerctl:hover { color: @rosewater; transition: color 120ms ease-in-out; } + + /* Start menu and notifications in green; turn red on new alerts */ + #custom-startmenu { color: @green; } + #custom-swaync { color: @green; } + #custom-swaync.notification, + #custom-swaync.dnd-notification, + #custom-swaync.inhibited-notification { color: @red; } + /* Group drawer button color */ + #group-mobo_drawer { color: @green; } + + #backlight-slider slider, #pulseaudio-slider slider { + min-height: 7px; min-width: 15px; opacity: 0; background-color: @text; border-radius: 3px; box-shadow: 1px 5px 6px 1px #272727; + } + #backlight-slider trough, #pulseaudio-slider trough { + min-height: 100px; min-width: 7px; border-radius: 5px; background-color: @surface0; + } + #backlight-slider highlight, #pulseaudio-slider highlight { min-width: 5px; border-radius: 5px; background-color: @blue; } + ''; + }; +} diff --git a/home-manager/programs/hypr/waybar/waybar-jak-oglo-simple.nix b/home-manager/programs/hypr/waybar/waybar-jak-oglo-simple.nix new file mode 100644 index 0000000..d84c8e3 --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-jak-oglo-simple.nix @@ -0,0 +1,846 @@ +{ + pkgs, + lib, + ... +}: +with lib; let + # Install Waybar helper scripts under ~/.config/waybar/scripts (needed for power-menu.sh) + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); + + # Import upstream wallust color variables and base style from the source theme + wallustColors = '' + /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ + /* wallust template - colors-waybar */ + + @define-color foreground #DADDF8; + @define-color background #1A1921; + @define-color background-alt rgba(26,25,33,0.25); + @define-color cursor #7B7EA0; + + @define-color color0 #413F48; + @define-color color1 #0F102E; + @define-color color2 #151636; + @define-color color3 #3F3683; + @define-color color4 #404470; + @define-color color5 #646AA1; + @define-color color6 #7177B1; + @define-color color7 #C2C7ED; + @define-color color8 #888BA6; + @define-color color9 #13153E; + @define-color color10 #1C1E49; + @define-color color11 #5448AF; + @define-color color12 #555A95; + @define-color color13 #868ED6; + @define-color color14 #969FEC; + @define-color color15 #C2C7ED; + ''; + + baseStyle = '' + /* ---- 💫 https://github.com/JaKooLit 💫 ---- */ + /* Oglo Chicklets */ + + * { + font-family: "JetBrainsMono Nerd Font", FontAwesome, Roboto, Helvetica, Arial, sans-serif; + font-size: 97%; + font-weight: bold; + } + + window#waybar { + background-color: #232a2e; + border-bottom: 8px solid #1d2327; + color: #d3c6aa; + transition-property: background-color; + transition-duration: .5s; + } + + window#waybar.hidden { + opacity: 0.2; + } + + /* + window#waybar.empty { + background-color: transparent; + } + window#waybar.solo { + background-color: #FFFFFF; + } + */ + + button { + all: unset; + background-color: #778f52; + color: #2d353b; + border: none; + border-bottom: 8px solid #5d743e; + border-radius: 5px; + padding-left: 15px; + padding-right: 15px; + transition: transform 0.1s ease-in-out; + } + + button:hover { + background: inherit; + background-color: #92ab6c; + border-bottom: 8px solid #788f57; + } + + button.active { + background: inherit; + background-color: #a5be7e; + border-bottom: 8px solid #8aa168; + } + + #mode { + background-color: #64727D; + border-bottom: 3px solid #ffffff; + } + + #backlight, + #backlight-slider, + #battery, + #bluetooth, + #clock, + #cpu, + #disk, + #idle_inhibitor, + #keyboard-state, + #memory, + #mode, + #mpris, + #network, + #power-profiles-daemon, + #pulseaudio, + #pulseaudio-slider, + #taskbar, + #temperature, + #tray, + #window, + #wireplumber, + #workspaces, + #custom-backlight, + #custom-browser, + #custom-cava_mviz, + #custom-cycle_wall, + #custom-dot_update, + #custom-file_manager, + #custom-keybinds, + #custom-keyboard, + #custom-light_dark, + #custom-lock, + #custom-hint, + #custom-hypridle, + #custom-menu, + #custom-playerctl, + #custom-power_vertical, + #custom-power, + #custom-quit, + #custom-reboot, + #custom-settings, + #custom-spotify, + #custom-swaync, + #custom-tty, + #custom-updater, + #custom-hyprpicker, + #custom-weather, + #custom-weather.clearNight, + #custom-weather.cloudyFoggyDay, + #custom-weather.cloudyFoggyNight, + #custom-weather.default, + #custom-weather.rainyDay, + #custom-weather.rainyNight, + #custom-weather.severe, + #custom-weather.showyIcyDay, + #custom-weather.snowyIcyNight, + #custom-weather.sunnyDay{ + color: #ffffff; + padding-top: 2px; + padding-bottom: 2px; + border-radius: 5px; + padding-left: 5px; + padding-right: 5px; + } + + #window, + #workspaces { + margin: 0 4px; + } + + /* If workspaces is the leftmost module, omit left margin */ + .modules-left > widget:first-child > #workspaces { + margin-left: 0; + } + + /* If workspaces is the rightmost module, omit right margin */ + .modules-right > widget:last-child > #workspaces { + margin-right: 0; + } + + #window { + background-color: #343f44; + color: #d3c6aa; + border-bottom: 8px solid #2b3539; + } + + #custom-swaync { + background-color: #778f52; + color: #2d353b; + border-bottom: 8px solid #5d743e; + } + + #custom-menu { + background-color: #778f52; + color: #2d353b; + border-bottom: 8px solid #5d743e; + } + + #custom-power { + background-color: #ee606a; + color: #2d353b; + border-bottom: 8px solid #ca4853; + padding-left: 10px; + } + + #custom-power_vertical{ + background-color: #ee606a; + color: #2d353b; + border-bottom: 8px solid #ca4853; + } + + #clock { + background-color: #96a84c; + color: #2d353b; + border-bottom: 8px solid #7a8c37; + } + + #battery { + background-color: #3a998f; + color: #2d353b; + border-bottom: 8px solid #227d74; + } + + @keyframes blink { + to { + background-color: #ffffff; + color: #000000; + } + } + + #battery.critical:not(.charging) { + background-color: #ee606a; + color: #2d353b; + border-bottom: 8px solid #ca4853; + animation-name: blink; + animation-duration: 3.0s; + animation-timing-function: steps(12); + animation-iteration-count: infinite; + animation-direction: alternate; + } + + label:focus { + background-color: #000000; + } + + #cpu { + background-color: #778f52; + color: #2d353b; + border-bottom: 8px solid #5d743e; + } + + #memory { + background-color: #d980ad; + color: #2d353b; + border-bottom: 8px solid #b86790; + } + + #disk { + background-color: #964B00; + border-bottom: 8px solid #793300; + } + + #custom-lock, + #custom-light_dark, + #backlight { + background-color: #64b6ac; + color: #2d353b; + border-bottom: 8px solid #4f9990; + padding-left: 10px; + } + + #network { + background-color: #2980b9; + } + + #network.disconnected { + background-color: #f53c3c; + } + + #pulseaudio { + background-color: #d8ac47; + color: #2d353b; + border-bottom: 8px solid #b78f30; + } + + #pulseaudio.muted { + background-color: #90b1b1; + color: #2a5c45; + } + + #wireplumber { + background-color: #fff0f5; + color: #000000; + } + + #wireplumber.muted { + background-color: #f53c3c; + } + + #custom-media { + background-color: #66cc99; + color: #2a5c45; + min-width: 100px; + } + + #custom-media.custom-spotify { + background-color: #66cc99; + } + + #custom-media.custom-vlc { + background-color: #ffa000; + } + + #temperature { + background-color: #f0932b; + border-bottom: 8px solid #b78f30; + } + + #temperature.critical { + background-color: #eb4d4b; + } + + #tray { + background-color: #e67f51; + color: #2d353b;; + border-bottom: 8px solid #c3653b; + } + + #tray > .passive { + -gtk-icon-effect: dim; + } + + #tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; + } + + #idle_inhibitor { + background-color: #2d3436; + } + + #idle_inhibitor.activated { + background-color: #ecf0f1; + color: #2d3436; + } + + #mpd { + background-color: #66cc99; + color: #2a5c45; + } + + #mpd.disconnected { + background-color: #f53c3c; + } + + #mpd.stopped { + background-color: #90b1b1; + } + + #mpd.paused { + background-color: #51a37a; + } + + #language { + background: #00b093; + color: #740864; + min-width: 16px; + } + + #keyboard-state { + background: #97e1ad; + color: #000000; + min-width: 16px; + border-bottom: 8px solid #78b48a; + } + + #keyboard-state > label { + padding: 0 5px; + } + + #keyboard-state > label.locked { + background: rgba(0, 0, 0, 0.2); + } + + #scratchpad { + background: rgba(0, 0, 0, 0.2); + } + + #scratchpad.empty { + background-color: transparent; + } + + tooltip { + background-color: #232a2e; + border: none; + border-bottom: 8px solid #1d2327; + } + + tooltip decoration { + box-shadow: none; + } + + tooltip decoration:backdrop { + box-shadow: none; + } + + tooltip label { + color: #d3c6aa; + padding-left: 5px; + padding-right: 5px; + padding-top: 0px; + padding-bottom: 5px; + } + + + #pulseaudio-slider slider { + min-width: 0px; + min-height: 0px; + opacity: 0; + background-image: none; + border: none; + box-shadow: none; + } + + #pulseaudio-slider trough { + background-color: #7f849c; + min-width: 80px; + min-height: 5px; + border-radius: 5px; + } + + #pulseaudio-slider highlight { + min-height: 10px; + border-radius: 5px; + } + + #backlight-slider slider { + min-width: 0px; + min-height: 0px; + opacity: 0; + background-image: none; + border: none; + box-shadow: none; + } + + #backlight-slider trough { + background-color: #7f849c; + min-width: 80px; + min-height: 10px; + border-radius: 5px; + } + + #backlight-slider highlight { + min-width: 10px; + border-radius: 5px; + } + ''; + + # Explicit overrides for notification coffee mug colors (red when inactive/no notifications, green when active/has notifications) + overrides = '' + /* Coffee mug notification colors */ + #custom-swaync { color: #ee606a; } /* default red */ + #custom-swaync.none, + #custom-swaync.dnd-none, + #custom-swaync.inhibited-none { color: #ee606a; } + #custom-swaync.notification, + #custom-swaync.dnd-notification, + #custom-swaync.inhibited-notification { color: #00f769; } + ''; +in { + # Install scripts (power-menu.sh, etc.) + home.file = builtins.listToAttrs ( + map (name: { + name = ".config/waybar/scripts/" + name; + value = { + source = "${scriptsDir}/${name}"; + executable = true; + }; + }) + scripts + ); + + programs.waybar = { + enable = true; + package = pkgs.waybar; + + settings = [ + { + layer = "top"; + position = "top"; + # mode = "dock"; + exclusive = true; + passthrough = false; + "gtk-layer-shell" = true; + "margin-left" = 6; + "margin-right" = 6; + "margin-top" = 2; + + # Layout + "modules-left" = [ + "idle_inhibitor" + "group/mobo_drawer" + "hyprland/workspaces#rw" + "tray" + "mpris" + ]; + "modules-center" = [ + "clock#2" + "group/notify" + "custom/weather" + ]; + "modules-right" = [ + "hyprland/window" + "battery" + "group/audio" + "custom/power" + ]; + + # Module definitions (subset needed by this layout) + battery = { + align = 0; + rotate = 0; + "full-at" = 100; + "design-capacity" = false; + states = { + good = 95; + warning = 30; + critical = 15; + }; + format = "{icon} {capacity}%"; + "format-charging" = " {capacity}%"; + "format-plugged" = "󱘖 {capacity}%"; + "format-alt-click" = "click"; + "format-full" = "{icon} Full"; + "format-alt" = "{icon} {time}"; + "format-icons" = [ + "󰂎" + "󰁺" + "󰁻" + "󰁼" + "󰁽" + "󰁾" + "󰁿" + "󰂀" + "󰂁" + "󰂂" + "󰁹" + ]; + "format-time" = "{H}h {M}min"; + tooltip = true; + "tooltip-format" = "{timeTo} {power}w"; + "on-click-middle" = "$HOME/.config/hypr/scripts/ChangeBlur.sh"; + "on-click-right" = "$HOME/.config/hypr/scripts/Wlogout.sh"; + }; + + "clock#2" = { + format = " {:%I:%M %p}"; + "format-alt" = "{:%A | %H:%M | %e %B}"; + "tooltip-format" = "{:%Y %B}\n{calendar}"; + }; + + cpu = { + format = "{usage}% 󰍛"; + interval = 1; + "min-length" = 5; + "format-alt-click" = "click"; + "format-alt" = "{icon0}{icon1}{icon2}{icon3} {usage:>2}% 󰍛"; + "format-icons" = ["▁" "▂" "▃" "▄" "▅" "▆" "▇" "█"]; + "on-click-right" = "gnome-system-monitor"; + }; + + disk = { + interval = 30; + path = "/"; + format = "{percentage_used}% 󰋊"; + "tooltip-format" = "{used} used out of {total} on {path} ({percentage_used}%)"; + }; + + "hyprland/workspaces#rw" = { + "disable-scroll" = true; + "all-outputs" = true; + "warp-on-scroll" = false; + "sort-by-number" = true; + "show-special" = false; + "on-click" = "activate"; + "on-scroll-up" = "hyprctl dispatch workspace e+1"; + "on-scroll-down" = "hyprctl dispatch workspace e-1"; + "persistent-workspaces" = {"*" = 5;}; + format = "{icon} {windows}"; + "format-window-separator" = " "; + "window-rewrite-default" = " "; + "window-rewrite" = { + "(.*) — Mozilla Firefox" = " $1"; + "(.*) - fish" = "> [$1]"; + "(.*) - zsh" = "> [$1]"; + "class" = "󰰭 "; + "class" = " 󰊠"; + "class" = " "; + "class" = " "; + "class<[Ss]ignal|signal-desktop|org.signal.Signal>" = "󰍩 "; + "title<.*Signal.*>" = "󰍩 "; + "class" = "🖥️ "; + "class<[Kk]denlive|org.kde.kdenlive>" = "🎬 "; + "title<.*Kdenlive.*>" = "🎬 "; + + # qs-* apps + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title" = " "; + "title<^Wallpapers$>" = " "; + "title<^Video Wallpapers$>" = " "; + "title<^qs-wlogout$>" = " "; + + # BoxBuddy + "class<[Bb]ox[Bb]uddy|io.github.dvlv.boxbuddy|io.github.dvlv.BoxBuddy>" = " "; + "title<.*BoxBuddy.*>" = " "; + + # Bazaar software store + "class" = " "; + "title<^Bazaar$>" = " "; + + # satty screenshot tool + "class" = " "; + "title<^satty$>" = " "; + }; + }; + + "hyprland/window" = { + format = "{}"; + "max-length" = 25; + "separate-outputs" = true; + "offscreen-css" = true; + "offscreen-css-text" = "(inactive)"; + }; + + idle_inhibitor = { + tooltip = true; + "tooltip-format-activated" = "Idle_inhibitor active"; + "tooltip-format-deactivated" = "Idle_inhibitor not active"; + format = "{icon}"; + "format-icons" = { + activated = " "; + deactivated = " "; + }; + }; + + memory = { + interval = 10; + format = "{used:0.1f}G 󰾆"; + "format-alt" = "{percentage}% 󰾆"; + "format-alt-click" = "click"; + tooltip = true; + "tooltip-format" = "{used:0.1f}GB/{total:0.1f}G"; + "on-click-right" = "$HOME/.config/hypr/scripts/WaybarScripts.sh --btop"; + }; + + "custom/weather" = { + "return-type" = "json"; + exec = "sh -lc 'WEATHER_ICON_STYLE=emoji WEATHER_TOOLTIP_MARKUP=1 ~/.local/bin/weather'"; + interval = 600; + tooltip = true; + }; + + mpris = { + interval = 10; + format = "{player_icon} "; + "format-paused" = "{status_icon} {dynamic}"; + "on-click-middle" = "playerctl play-pause"; + "on-click" = "playerctl previous"; + "on-click-right" = "playerctl next"; + "scroll-step" = 5.0; + "on-scroll-up" = "$HOME/.config/hypr/scripts/Volume.sh --inc"; + "on-scroll-down" = "$HOME/.config/hypr/scripts/Volume.sh --dec"; + "smooth-scrolling-threshold" = 1; + tooltip = true; + "tooltip-format" = "{status_icon} {dynamic}\nLeft Click: previous\nMid Click: Pause\nRight Click: Next"; + "player-icons" = { + chromium = ""; + default = ""; + firefox = ""; + kdeconnect = ""; + mopidy = ""; + mpv = "󰐹"; + spotify = ""; + vlc = "󰕼"; + }; + "status-icons" = { + paused = "󰐎"; + playing = ""; + stopped = ""; + }; + "max-length" = 30; + }; + + pulseaudio = { + format = "{icon} {volume}%"; + "format-bluetooth" = "{icon} 󰂰 {volume}%"; + "format-muted" = "󰖁"; + "format-icons" = { + headphone = ""; + "hands-free" = ""; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = ["" "" "󰕾" ""]; + "ignored-sinks" = ["Easy Effects Sink"]; + }; + "scroll-step" = 5.0; + "on-click" = "$HOME/.config/hypr/scripts/Volume.sh --toggle"; + "on-click-right" = "pavucontrol -t 3"; + "on-scroll-up" = "$HOME/.config/hypr/scripts/Volume.sh --inc"; + "on-scroll-down" = "$HOME/.config/hypr/scripts/Volume.sh --dec"; + "tooltip-format" = "{icon} {desc} | {volume}%"; + "smooth-scrolling-threshold" = 1; + }; + + "pulseaudio#microphone" = { + format = "{format_source}"; + "format-source" = " {volume}%"; + "format-source-muted" = ""; + "on-click" = "$HOME/.config/hypr/scripts/Volume.sh --toggle-mic"; + "on-click-right" = "pavucontrol -t 4"; + "on-scroll-up" = "$HOME/.config/hypr/scripts/Volume.sh --mic-inc"; + "on-scroll-down" = "$HOME/.config/hypr/scripts/Volume.sh --mic-dec"; + "tooltip-format" = "{source_desc} | {source_volume}%"; + "scroll-step" = 5; + }; + + tray = { + "icon-size" = 20; + spacing = 4; + }; + + # Groups + "group/mobo_drawer" = { + orientation = "inherit"; + drawer = { + "transition-duration" = 500; + "children-class" = "cpu"; + "transition-left-to-right" = true; + }; + modules = ["temperature" "cpu" "power-profiles-daemon" "memory" "disk"]; + }; + + "group/audio" = { + orientation = "inherit"; + drawer = { + "transition-duration" = 500; + "children-class" = "pulseaudio"; + "transition-left-to-right" = true; + }; + modules = ["pulseaudio" "pulseaudio#microphone"]; + }; + + "group/notify" = { + orientation = "inherit"; + drawer = { + "transition-duration" = 500; + "children-class" = "custom/swaync"; + "transition-left-to-right" = false; + }; + modules = ["custom/swaync" "custom/dot_update"]; + }; + + # Custom modules + "custom/power" = { + format = " ⏻ "; + "on-click" = "qs-wlogout"; + "on-click-right" = "~/.config/waybar/scripts/power-menu.sh"; + tooltip = true; + "tooltip-format" = "Power menu: Left-click for QS logout, Right-click for rofi power menu"; + }; + + "custom/quit" = { + format = "󰗼"; + "on-click" = "hyprctl dispatch exit"; + tooltip = true; + "tooltip-format" = "Left Click: Exit Hyprland"; + }; + + "custom/swaync" = { + tooltip = true; + "tooltip-format" = "Left Click: Launch Notification Center\nRight Click: Do not Disturb"; + format = "{} {icon} "; + # Coffee mug icon for all states + "format-icons" = { + notification = ""; + none = ""; + "dnd-notification" = ""; + "dnd-none" = ""; + "inhibited-notification" = ""; + "inhibited-none" = ""; + "dnd-inhibited-notification" = ""; + "dnd-inhibited-none" = ""; + }; + "return-type" = "json"; + "exec-if" = "which swaync-client"; + exec = "swaync-client -swb"; + "on-click" = "sleep 0.1 && swaync-client -t -sw"; + "on-click-right" = "swaync-client -d -sw"; + escape = true; + }; + + # Support modules referenced by groups + temperature = { + interval = 10; + tooltip = true; + "hwmon-path" = ["/sys/class/hwmon/hwmon1/temp1_input" "/sys/class/thermal/thermal_zone0/temp"]; + "critical-threshold" = 82; + "format-critical" = "{temperatureC}°C {icon}"; + format = "{temperatureC}°C {icon}"; + "format-icons" = ["󰈸"]; + "on-click-right" = "$HOME/.config/hypr/scripts/WaybarScripts.sh --nvtop"; + }; + + "power-profiles-daemon" = { + format = "{icon} "; + "tooltip-format" = "Power profile: {profile}\nDriver: {driver}"; + tooltip = true; + "format-icons" = { + default = ""; + performance = ""; + balanced = ""; + "power-saver" = ""; + }; + }; + + "custom/dot_update" = { + format = " 󰁈 "; + "on-click" = "$HOME/.config/hypr/scripts/KooLsDotsUpdate.sh"; + tooltip = true; + "tooltip-format" = "Check KooL Dots update\nIf available"; + }; + } + ]; + + # Compose style: wallust colors first, then base style, then our overrides + style = wallustColors + baseStyle + overrides; + }; +} diff --git a/home-manager/programs/hypr/waybar/waybar-jerry.nix b/home-manager/programs/hypr/waybar/waybar-jerry.nix new file mode 100644 index 0000000..3d9890b --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-jerry.nix @@ -0,0 +1,356 @@ +{ + pkgs, + config, + lib, + ... +}: let + terminal = "kitty"; + base00 = "0F1419"; + base01 = "131721"; + base03 = "3E4B59"; + base05 = "E6E1CF"; + base06 = "E6E1CF"; + base07 = "F3F4F5"; + base08 = "F07178"; + base09 = "FF8F40"; + base0A = "FFB454"; + base0B = "B8CC52"; + base0C = "95E6CB"; + base0D = "59C2FF"; + base0E = "D2A6FF"; + base0F = "E6B673"; +in + with lib; { + # Configure & Theme Waybar + programs.waybar = { + enable = true; + package = pkgs.waybar; + settings = [ + { + layer = "top"; + position = "top"; + + modules-center = ["network" "pulseaudio" "cpu" "hyprland/workspaces" "memory" "disk" "clock"]; # Eterna: [ "hyprland/window" ] + modules-left = ["custom/startmenu" "hyprland/window"]; # Eternal: [ "hyprland/workspaces" "cpu" "memory" "network" ] + modules-right = ["tray" "idle_inhibitor" "custom/notification" "battery" "custom/exit"]; # Eternal: [ "idle_inhibitor" "pulseaudio" "clock" "custom/notification" "tray" ] + + "hyprland/workspaces" = { + format = "{name}"; + format-icons = { + default = " "; + active = " "; + urgent = " "; + }; + on-scroll-up = "hyprctl dispatch workspace e+1"; + on-scroll-down = "hyprctl dispatch workspace e-1"; + }; + "clock" = { + format = '' {:%H:%M}''; + /* + ''{: %I:%M %p}''; + */ + tooltip = true; + tooltip-format = "{:%A, %d.%B %Y }{calendar}"; + }; + "hyprland/window" = { + max-length = 60; + separate-outputs = false; + }; + "memory" = { + interval = 5; + format = " {}%"; + tooltip = true; + on-click = "${terminal} -e btop"; + }; + "cpu" = { + interval = 5; + format = " {usage:2}%"; + tooltip = true; + on-click = "${terminal} -e btop"; + }; + "disk" = { + format = " {free}"; + tooltip = true; + # Not working with zaneyos window open then closes + #on-click = "${terminal} -e sh -c df -h ; read"; + }; + "network" = { + format-icons = ["󰤯" "󰤟" "󰤢" "󰤥" "󰤨"]; + format-ethernet = " {bandwidthDownBits}"; + format-wifi = " {bandwidthDownBits}"; + format-disconnected = "󰤮"; + tooltip = false; + on-click = "${terminal} -e btop"; + }; + "tray" = { + spacing = 12; + }; + "pulseaudio" = { + format = "{icon} {volume}% {format_source}"; + format-bluetooth = "{volume}% {icon} {format_source}"; + format-bluetooth-muted = " {icon} {format_source}"; + format-muted = " {format_source}"; + format-source = " {volume}%"; + format-source-muted = ""; + format-icons = { + headphone = ""; + hands-free = ""; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = ["" "" ""]; + }; + on-click = "pavucontrol"; + }; + "custom/exit" = { + tooltip = false; + format = "⏻"; + on-click = "sleep 0.1 && wlogout"; + }; + "custom/startmenu" = { + tooltip = false; + format = " "; + # exec = "rofi -show drun"; + on-click = "rofi -show drun"; + }; + "idle_inhibitor" = { + format = "{icon}"; + format-icons = { + activated = " "; + deactivated = " "; + }; + tooltip = "true"; + }; + "custom/notification" = { + tooltip = false; + format = "{icon} {}"; + format-icons = { + notification = ""; + none = ""; + dnd-notification = ""; + dnd-none = ""; + inhibited-notification = ""; + inhibited-none = ""; + dnd-inhibited-notification = ""; + dnd-inhibited-none = ""; + }; + return-type = "json"; + exec-if = "which swaync-client"; + exec = "swaync-client -swb"; + on-click = "swaync-client -t"; + escape = true; + }; + "battery" = { + states = { + warning = 30; + critical = 15; + }; + format = "{icon} {capacity}%"; + format-charging = "󰂄 {capacity}%"; + format-plugged = "󱘖 {capacity}%"; + format-icons = ["󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹"]; + on-click = ""; + tooltip = false; + }; + } + ]; + style = concatStrings [ + '' + * { + font-size: 16px; + font-family: JetBrainsMono Nerd Font, Font Awesome, sans-serif; + font-weight: bold; + } + window#waybar { + /* + + background-color: rgba(26,27,38,0); + border-bottom: 1px solid rgba(26,27,38,0); + border-radius: 0px; + color: #${base0F}; + */ + + background-color: rgba(26,27,38,0); + border-bottom: 1px solid rgba(26,27,38,0); + border-radius: 0px; + color: #${base0F}; + } + #workspaces { + /* + Eternal + background: linear-gradient(180deg, #${base00}, #${base01}); + margin: 5px 5px 5px 0px; + padding: 0px 10px; + border-radius: 0px 15px 50px 0px; + border: 0px; + font-style: normal; + color: #${base00}; + */ + background: linear-gradient(45deg, #${base01}, #${base01}); + margin: 5px; + padding: 0px 1px; + border-radius: 15px; + border: 0px; + font-style: normal; + color: #${base00}; + } + #workspaces button { + padding: 0px 5px; + margin: 4px 3px; + border-radius: 15px; + border: 0px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 0.5; + transition: all 0.3s ease-in-out; + } + #workspaces button.active { + padding: 0px 5px; + margin: 4px 3px; + border-radius: 15px; + border: 0px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 1.0; + min-width: 40px; + transition: all 0.3s ease-in-out; + } + #workspaces button:hover { + border-radius: 15px; + color: #${base00}; + background: linear-gradient(45deg, #${base0D}, #${base0E}); + opacity: 0.8; + } + tooltip { + background: #${base00}; + border: 1px solid #${base0E}; + border-radius: 10px; + } + tooltip label { + color: #${base07}; + } + #window { + /* + Eternal + color: #${base05}; + background: #${base00}; + border-radius: 15px; + margin: 5px; + padding: 2px 20px; + */ + margin: 5px; + padding: 2px 20px; + color: #${base05}; + background: #${base01}; + border-radius: 50px 15px 50px 15px; + } + #memory { + color: #${base0F}; + /* + Eternal + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + */ + background: #${base01}; + margin: 5px; + padding: 2px 20px; + border-radius: 15px 50px 15px 50px; + } + #clock { + color: #${base0B}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #idle_inhibitor { + color: #${base0A}; + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + } + #cpu { + color: #${base07}; + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + } + #disk { + color: #${base0F}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #battery { + color: #${base08}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #network { + color: #${base09}; + background: #${base00}; + border-radius: 50px 15px 50px 15px; + margin: 5px; + padding: 2px 20px; + } + #tray { + color: #${base05}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #pulseaudio { + color: #${base0D}; + /* + Eternal + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + */ + background: #${base01}; + margin: 4px; + padding: 2px 20px; + border-radius: 50px 15px 50px 15px; + } + #custom-notification { + color: #${base0C}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #custom-startmenu { + color: #${base0E}; + background: #${base00}; + border-radius: 0px 15px 50px 0px; + margin: 5px 5px 5px 0px; + padding: 2px 20px; + } + #idle_inhibitor { + color: #${base09}; + background: #${base00}; + border-radius: 15px 50px 15px 50px; + margin: 5px; + padding: 2px 20px; + } + #custom-exit { + color: #${base0E}; + background: #${base00}; + border-radius: 15px 0px 0px 50px; + margin: 5px 0px 5px 5px; + padding: 2px 20px; + } + '' + ]; + }; + } diff --git a/home-manager/programs/hypr/waybar/waybar-jwt-catppuccin.nix b/home-manager/programs/hypr/waybar/waybar-jwt-catppuccin.nix new file mode 100644 index 0000000..3db7f3b --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-jwt-catppuccin.nix @@ -0,0 +1,776 @@ +{pkgs, ...}: let + # Install any helper scripts shipped in modules/home/waybar/scripts into ~/.config/waybar/scripts + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); + + # Inline, improved Cava script packaged via Nix so we don't rely on an external bash file + waybarCava = pkgs.writeShellScriptBin "WaybarCava" '' + set -euo pipefail + + # Ensure cava exists + if ! command -v cava >/dev/null 2>&1; then + echo "cava not found in PATH" >&2 + exit 1 + fi + + # Characters for vertical bars (0..7) + bar="▁▂▃▄▅▆▇█" + + # Build sed script that: + # - strips semicolons (cava RAW ASCII delimiter) + # - maps digits 0..7 to the corresponding glyph in $bar + dict="s/;//g" + bar_length=''${#bar} + for ((i = 0; i < bar_length; i++)); do + dict+=";s/$i/''${bar:$i:1}/g" + done + + # Single-instance guard (kill prior instance cleanly) + RUNTIME_DIR="''${XDG_RUNTIME_DIR:-/tmp}" + pidfile="$RUNTIME_DIR/waybar-cava.pid" + if [ -f "$pidfile" ]; then + oldpid=$(cat "$pidfile" || true) + if [ -n "''${oldpid:-}" ] && kill -0 "$oldpid" 2>/dev/null; then + kill "$oldpid" 2>/dev/null || true + # Give the old pipeline a moment to exit + sleep 0.1 || true + fi + fi + echo $$ > "$pidfile" + + # Use a unique temporary config and clean it up on exit + config_file=$(mktemp "''${RUNTIME_DIR}/waybar-cava.XXXXXX.conf") + cleanup() { + rm -f "$config_file" "$pidfile" + } + trap cleanup EXIT INT TERM + + cat >"$config_file" < box > #idle_inhibitor { + color: #f38ba8; /* red by default */ + } + + window#waybar > box > #idle_inhibitor.activated { + color: #a6e3a1; /* green when activated */ + } + + window#waybar > box > #idle_inhibitor.deactivated { + color: #f38ba8; /* red when deactivated */ + } + + tooltip { + background: @base; + border-radius: 7px; + } + + button { + box-shadow: inset 0 3px transparent; + border: none; + border-radius: 0; + } + + button:hover { + background: inherit; + } + + #pulseaudio:hover { + background-color: @mantle; + } + + #workspaces button { + padding: 0 5px; + background-color: transparent; + } + + #workspaces button:hover { + background: rgba(0, 0, 0, 0.3); + border-radius: 7px; + } + + /* Hyprland workspaces - based on working waybar-jak-catppuccin */ + #workspaces button { + color: @sapphire; /* default/inactive workspaces - pale blue */ + background-color: transparent; + padding-top: 4px; + padding-bottom: 4px; + padding-right: 6px; + padding-left: 4px; + } + + #workspaces button.active { + color: @green; /* active workspace - green */ + background: transparent; + border-radius: 15px; + } + + #workspaces button.focused { + color: @rosewater; /* focused workspace - rosewater */ + background: transparent; + border-radius: 15px; + } + + #workspaces button.urgent { + color: @crust; + background: transparent; + border-radius: 15px; + } + + #workspaces button:hover { + background: transparent; + color: @flamingo; + border-radius: 15px; + } + + #workspaces button.empty { + color: @red; /* empty workspace - red */ + } + + #workspaces button.urgent { + background-color: #eb4d4b; + } + + #mode { + background-color: #64727d; + box-shadow: inset 0 0px #ffffff; + background: rgba(5, 5, 5, 0.3); + color: #000000; + padding: 1px 10px 1px 10px; + border-radius: 0px 0px 0px 0px; + margin-top: 5px; + } + + #clock, + #battery, + #cpu, + #memory, + #disk, + #temperature, + #backlight, + #network, + #custom-spotify, + #pulseaudio, + #wireplumber, + #custom-media, + #custom-waypaper, + #tray, + #mode, + #custom-weather, + #idle_inhibitor, + #scratchpad, + #power-profiles-daemon, + #custom-cava, + #custom-cava_mviz, + #custom-gpt, + #custom-mspowers, + #custom-qs-wallpaper, + #custom-qs-vid-wallpaper, + #custom-swaync, + #mpd { + background: @base; + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + + #window, + #workspaces { + background: @base; + color: @rosewater; + padding: 1px 1px 1px 1px; + margin-top: 5px; + } + + .modules-left > widget:first-child > #workspaces { + margin-left: 5px; + } + + .modules-right > widget:last-child > #workspaces { + margin-right: 0; + } + + #clock { + color: @pink; + margin-top: 5px; + margin-right: 5px; + } + + #custom-mspowers { + color: @yellow; + margin-top: 5px; + } + + #clock:hover { + background-color: @mantle; + } + + #custom-weather { + color: @sky; + padding: 1px 6px 1px 8px; + } + + #custom-spotify { + color: @maroon; + padding: 1px 6px 1px 8px; + } + + #custom-waypaper:hover { + background-color: @mantle; + } + + #custom-cava, + #custom-cava_mviz { + background: @base; + color: @mauve; + padding: 1px 10px; + margin-left: 8px; + } + + #custom-cava:hover, + #custom-cava_mviz:hover { + background-color: @mantle; + } + + #custom-gpt { + font-size: 20px; + background-position: center; + background-repeat: no-repeat; + background-size: 15px 15px; + color: @mauve; + border-radius: 7px; + } + + #custom-gpt:hover { + background-color: @mantle; + } + + #custom-weather:hover { + background-color: @mantle; + } + + #cava-player .status { + font-size: 18px; + color: #ffffff; + } + + #custom-power { + color: @mauve; + background-color: @base; + margin-right: 5px; + margin-top: 5px; + padding: 1px 5px 1px 2px; + } + + #custom-power:hover { + background-color: @mantle; + } + + #custom-menu { + color: @blue; + background-color: @base; + margin-left: 5px; + margin-top: 5px; + padding: 1px 10px 1px 10px; + font-size: 18px; + min-width: 30px; + } + + #custom-menu:hover { + background-color: @mantle; + } + + #custom-qs-wallpaper, + #custom-qs-vid-wallpaper { + color: @sapphire; + background-color: @base; + font-size: 18px; + min-width: 20px; + } + + #custom-qs-wallpaper:hover, + #custom-qs-vid-wallpaper:hover { + background-color: @mantle; + } + + #custom-swaync { + color: @green; + background-color: @base; + } + + #custom-swaync.notification, + #custom-swaync.dnd-notification, + #custom-swaync.inhibited-notification, + #custom-swaync.dnd-inhibited-notification { + color: @red; + } + + #custom-swaync:hover { + background-color: @mantle; + } + + #battery { + color: @mauve; + } + + #battery.charging, + #battery.plugged { + color: @green; + } + + @keyframes blink { + to { + background: @base; + color: #000000; + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + } + + #battery.critical:not(.charging) { + color: #ffffff; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: steps(12); + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #battery:hover { + background-color: @mantle; + } + + #power-profiles-daemon { + padding-right: 15px; + color: #000000; + } + + #power-profiles-daemon.performance { + color: #ffffff; + } + + #power-profiles-daemon.balanced { + color: #ffffff; + } + + #power-profiles-daemon.power-saver { + color: #000000; + } + + label:focus { + background: @base; + } + + #memory { + color: @yellow; + } + + #memory:hover { + background-color: @mantle; + } + + #disk { + background: @base; + } + + #backlight { + color: @green; + } + + #backlight:hover { + background-color: @mantle; + } + + #network { + color: @peach; + } + + #network.disconnected { + color: @red; + } + + #network:hover { + background-color: @mantle; + } + + #pulseaudio { + color: @peach; + } + + #pulseaudio.muted { + color: @peach; + } + + #temperature { + background: @base; + color: @yellow; + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + + #temperature.critical { + background-color: #eb4d4b; + } + + /* Idle inhibitor - comprehensive targeting without !important */ + #idle_inhibitor, + .idle_inhibitor, + button#idle_inhibitor, + .module#idle_inhibitor, + window#waybar #idle_inhibitor, + window#waybar .idle_inhibitor, + window#waybar button#idle_inhibitor { + color: #f38ba8; /* red when deactivated */ + background-color: #1e1e2e; + } + + #idle_inhibitor.activated, + .idle_inhibitor.activated, + button#idle_inhibitor.activated, + .module#idle_inhibitor.activated, + window#waybar #idle_inhibitor.activated, + window#waybar .idle_inhibitor.activated, + window#waybar button#idle_inhibitor.activated { + color: #a6e3a1; /* green when activated */ + background-color: #1e1e2e; + } + + #idle_inhibitor.deactivated, + .idle_inhibitor.deactivated, + button#idle_inhibitor.deactivated, + .module#idle_inhibitor.deactivated, + window#waybar #idle_inhibitor.deactivated, + window#waybar .idle_inhibitor.deactivated, + window#waybar button#idle_inhibitor.deactivated { + color: #f38ba8; /* red when deactivated */ + background-color: #1e1e2e; + } + + #idle_inhibitor:hover { + background-color: @mantle; + } + + ''; + }; +} diff --git a/home-manager/programs/hypr/waybar/waybar-jwt-transparent.nix b/home-manager/programs/hypr/waybar/waybar-jwt-transparent.nix new file mode 100644 index 0000000..4a8fc1b --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-jwt-transparent.nix @@ -0,0 +1,711 @@ +{pkgs, ...}: let + # Install any helper scripts shipped in modules/home/waybar/scripts into ~/.config/waybar/scripts + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); + + # Inline, improved Cava script packaged via Nix so we don't rely on an external bash file + waybarCava = pkgs.writeShellScriptBin "WaybarCava" '' + set -euo pipefail + + # Ensure cava exists + if ! command -v cava >/dev/null 2>&1; then + echo "cava not found in PATH" >&2 + exit 1 + fi + + # Characters for vertical bars (0..7) + bar="▁▂▃▄▅▆▇█" + + # Build sed script that: + # - strips semicolons (cava RAW ASCII delimiter) + # - maps digits 0..7 to the corresponding glyph in $bar + dict="s/;//g" + bar_length=''${#bar} + for ((i = 0; i < bar_length; i++)); do + dict+=";s/$i/''${bar:$i:1}/g" + done + + # Single-instance guard (kill prior instance cleanly) + RUNTIME_DIR="''${XDG_RUNTIME_DIR:-/tmp}" + pidfile="$RUNTIME_DIR/waybar-cava.pid" + if [ -f "$pidfile" ]; then + oldpid=$(cat "$pidfile" || true) + if [ -n "''${oldpid:-}" ] && kill -0 "$oldpid" 2>/dev/null; then + kill "$oldpid" 2>/dev/null || true + # Give the old pipeline a moment to exit + sleep 0.1 || true + fi + fi + echo $$ > "$pidfile" + + # Use a unique temporary config and clean it up on exit + config_file=$(mktemp "''${RUNTIME_DIR}/waybar-cava.XXXXXX.conf") + cleanup() { + rm -f "$config_file" "$pidfile" + } + trap cleanup EXIT INT TERM + + cat >"$config_file" < box { + margin: 4px 8px 0 8px; + padding: 2px; + background-color: rgba(0, 0, 0, 0.1); + border-radius: 8px; + } + + tooltip { + background: rgb(30, 30, 46); + border-radius: 7px; + } + + button { + box-shadow: inset 0 3px transparent; + border: none; + border-radius: 0; + } + + button:hover { + background: inherit; + } + + #workspaces button { + padding: 0 5px; + background-color: transparent; + } + + #workspaces button:hover { + background: rgba(255, 255, 255, 0.1); + border-radius: 7px; + } + + /* Hyprland workspaces - based on working waybar-jak-catppuccin */ + #workspaces button { + color: #74c7ec; /* default/inactive workspaces - sapphire */ + background-color: transparent; + padding-top: 4px; + padding-bottom: 4px; + padding-right: 6px; + padding-left: 4px; + } + + #workspaces button.active { + color: #a6e3a1; /* active workspace - green */ + background: transparent; + border-radius: 15px; + } + + #workspaces button.focused { + color: #f5e0dc; /* focused workspace - rosewater */ + background: transparent; + border-radius: 15px; + } + + #workspaces button.urgent { + color: #11111b; /* crust */ + background: transparent; + border-radius: 15px; + } + + #workspaces button:hover { + background: transparent; + color: #f2cdcd; /* flamingo */ + border-radius: 15px; + } + + #workspaces button.empty { + color: #f38ba8; /* empty workspace - red */ + } + + #workspaces button.urgent { + background-color: #eb4d4b; + } + + #mode { + background-color: #64727d; + box-shadow: inset 0 0px #ffffff; + background: rgba(5, 5, 5, 0.3); + color: #000000; + padding: 1px 10px 1px 10px; + border-radius: 0px 0px 0px 0px; + margin-top: 5px; + } + + #clock, + #battery, + #cpu, + #memory, + #disk, + #temperature, + #backlight, + #network, + #custom-spotify, + #pulseaudio, + #wireplumber, + #custom-media, + #custom-waypaper, + #tray, + #mode, + #custom-weather, + #idle_inhibitor, + #scratchpad, + #custom-power #power-profiles-daemon, + #custom-cava, + #custom-cava_mviz, + #custom-gpt, + #custom-menu, + #custom-qs-wallpaper, + #custom-qs-vid-wallpaper, + #custom-swaync, + #mpd { + background: rgba(30, 30, 46, 0); + padding: 1px 10px 1px 10px; + } + + #window, + #workspaces { + color: #f5e0dc; + padding: 1px 1px 1px 1px; + } + + .modules-right > widget:last-child > #workspaces { + margin-right: 0; + } + + #clock { + color: #ffffff; + margin-right: 5px; + } + + #clock:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-weather { + color: #ffffff; + padding: 1px 6px 1px 8px; + } + + #custom-weather:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-spotify { + color: #ffffff; + padding: 1px 6px 1px 8px; + } + + #custom-spotify:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-cava, + #custom-cava_mviz { + background: rgb(30, 30, 46); + color: #ffffff; + padding: 1px 10px; + margin-left: 8px; + } + + #custom-cava:hover, + #custom-cava_mviz:hover { + background-color: #181825; + } + + #custom-gpt { + font-size: 20px; + background-position: center; + background-repeat: no-repeat; + background-size: 15px 15px; + color: #ffffff; + border-radius: 7px; + } + + #custom-gpt:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #cava-player .status { + font-size: 18px; + color: #ffffff; + } + + #custom-power { + color: #ffffff; + padding: 1px 5px 1px 3px; + margin-right: 1px; + } + + #custom-power:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-menu { + color: #89b4fa; + padding: 1px 10px 1px 10px; + margin-left: 5px; + } + + #custom-menu:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-qs-wallpaper, + #custom-qs-vid-wallpaper { + color: #74c7ec; + } + + #custom-qs-wallpaper:hover, + #custom-qs-vid-wallpaper:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-swaync { + color: #a6e3a1; + } + + #custom-swaync.notification, + #custom-swaync.dnd-notification, + #custom-swaync.inhibited-notification, + #custom-swaync.dnd-inhibited-notification { + color: #f38ba8; + } + + #custom-swaync:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #battery { + color: #ffffff; + } + + #battery.charging, + #battery.plugged { + color: #85eb81; + } + + #battery:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + @keyframes blink { + to { + background: rgb(30, 30, 46); + color: #000000; + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + } + + #battery.critical:not(.charging) { + color: #ffffff; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: steps(12); + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #power-profiles-daemon { + padding-right: 15px; + color: #000000; + } + + #power-profiles-daemon.performance { + color: #ffffff; + } + + #power-profiles-daemon.balanced { + color: #ffffff; + } + + #power-profiles-daemon.power-saver { + color: #000000; + } + + label:focus { + background: rgb(30, 30, 46); + } + + #memory { + color: #ffffff; + padding: 1px 9px 1px 5px; + } + + #memory:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #disk { + background: rgb(30, 30, 46); + } + + #backlight { + color: #ffffff; + } + + #network { + color: #ffffff; + } + + #network.disconnected { + color: #f2564b; + } + + #network:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #pulseaudio { + color: #ffffff; + } + + #pulseaudio.muted { + color: #ffffff; + } + + #wireplumber { + color: #000000; + } + + #pulseaudio:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #wireplumber.muted { + background: rgb(30, 30, 46); + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + + #custom-media { + color: #2a5c45; + min-width: 100px; + } + + #custom-media.custom-spotify { + background: rgb(30, 30, 46); + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + + #custom-media.custom-vlc { + background: rgb(30, 30, 46); + margin-top: 5px; + } + + #temperature { + color: #ffffff; + } + + #temperature.critical { + background-color: #eb4d4b; + } + + #temperature:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #tray { + background-color: #2980b9; + } + + #tray > .passive { + -gtk-icon-effect: dim; + } + + #tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; + } + + /* Idle inhibitor - coffee mug color changes */ + #idle_inhibitor { + color: #f38ba8; /* default red (deactivated) */ + } + + #idle_inhibitor.activated { + color: #a6e3a1; /* green when activated */ + } + + #idle_inhibitor.deactivated { + color: #f38ba8; /* red when deactivated */ + } + + #idle_inhibitor:hover { + background-color: rgba(255, 255, 255, 0.1); + } + ''; + }; +} diff --git a/home-manager/programs/hypr/waybar/waybar-jwt-ultradark.nix b/home-manager/programs/hypr/waybar/waybar-jwt-ultradark.nix new file mode 100644 index 0000000..59e9303 --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-jwt-ultradark.nix @@ -0,0 +1,711 @@ +{pkgs, ...}: let + # Install any helper scripts shipped in modules/home/waybar/scripts into ~/.config/waybar/scripts + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); + + # Inline, improved Cava script packaged via Nix so we don't rely on an external bash file + waybarCava = pkgs.writeShellScriptBin "WaybarCava" '' + set -euo pipefail + + # Ensure cava exists + if ! command -v cava >/dev/null 2>&1; then + echo "cava not found in PATH" >&2 + exit 1 + fi + + # Characters for vertical bars (0..7) + bar="▁▂▃▄▅▆▇█" + + # Build sed script that: + # - strips semicolons (cava RAW ASCII delimiter) + # - maps digits 0..7 to the corresponding glyph in $bar + dict="s/;//g" + bar_length=''${#bar} + for ((i = 0; i < bar_length; i++)); do + dict+=";s/$i/''${bar:$i:1}/g" + done + + # Single-instance guard (kill prior instance cleanly) + RUNTIME_DIR="''${XDG_RUNTIME_DIR:-/tmp}" + pidfile="$RUNTIME_DIR/waybar-cava.pid" + if [ -f "$pidfile" ]; then + oldpid=$(cat "$pidfile" || true) + if [ -n "''${oldpid:-}" ] && kill -0 "$oldpid" 2>/dev/null; then + kill "$oldpid" 2>/dev/null || true + # Give the old pipeline a moment to exit + sleep 0.1 || true + fi + fi + echo $$ > "$pidfile" + + # Use a unique temporary config and clean it up on exit + config_file=$(mktemp "''${RUNTIME_DIR}/waybar-cava.XXXXXX.conf") + cleanup() { + rm -f "$config_file" "$pidfile" + } + trap cleanup EXIT INT TERM + + cat >"$config_file" < box { + margin: 4px 8px 0 8px; + padding: 2px; + background-color: rgba(0, 0, 0, 1); + border-radius: 8px; + } + + tooltip { + background: rgb(30, 30, 46); + border-radius: 7px; + } + + button { + box-shadow: inset 0 3px transparent; + border: none; + border-radius: 0; + } + + button:hover { + background: inherit; + } + + #workspaces button { + padding: 0 5px; + background-color: transparent; + } + + #workspaces button:hover { + background: rgba(255, 255, 255, 0.1); + border-radius: 7px; + } + + /* Hyprland workspaces - based on working waybar-jak-catppuccin */ + #workspaces button { + color: #74c7ec; /* default/inactive workspaces - sapphire */ + background-color: transparent; + padding-top: 4px; + padding-bottom: 4px; + padding-right: 6px; + padding-left: 4px; + } + + #workspaces button.active { + color: #a6e3a1; /* active workspace - green */ + background: transparent; + border-radius: 15px; + } + + #workspaces button.focused { + color: #f5e0dc; /* focused workspace - rosewater */ + background: transparent; + border-radius: 15px; + } + + #workspaces button.urgent { + color: #11111b; /* crust */ + background: transparent; + border-radius: 15px; + } + + #workspaces button:hover { + background: transparent; + color: #f2cdcd; /* flamingo */ + border-radius: 15px; + } + + #workspaces button.empty { + color: #f38ba8; /* empty workspace - red */ + } + + #workspaces button.urgent { + background-color: #eb4d4b; + } + + #mode { + background-color: #64727d; + box-shadow: inset 0 0px #ffffff; + background: rgba(5, 5, 5, 0.3); + color: #000000; + padding: 1px 10px 1px 10px; + border-radius: 0px 0px 0px 0px; + margin-top: 5px; + } + + #clock, + #battery, + #cpu, + #memory, + #disk, + #temperature, + #backlight, + #network, + #custom-spotify, + #pulseaudio, + #wireplumber, + #custom-media, + #custom-waypaper, + #tray, + #mode, + #custom-weather, + #idle_inhibitor, + #scratchpad, + #custom-power #power-profiles-daemon, + #custom-cava, + #custom-cava_mviz, + #custom-gpt, + #custom-menu, + #custom-qs-wallpaper, + #custom-qs-vid-wallpaper, + #custom-swaync, + #mpd { + background: rgba(30, 30, 46, 0); + padding: 1px 10px 1px 10px; + } + + #window, + #workspaces { + color: #f5e0dc; + padding: 1px 1px 1px 1px; + } + + .modules-right > widget:last-child > #workspaces { + margin-right: 0; + } + + #clock { + color: #e5e5e5; + margin-right: 5px; + } + + #clock:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-weather { + color: #e5e5e5; + padding: 1px 6px 1px 8px; + } + + #custom-weather:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-spotify { + color: #e5e5e5; + padding: 1px 6px 1px 8px; + } + + #custom-spotify:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-cava, + #custom-cava_mviz { + background: rgb(30, 30, 46); + color: #e5e5e5; + padding: 1px 10px; + margin-left: 8px; + } + + #custom-cava:hover, + #custom-cava_mviz:hover { + background-color: #181825; + } + + #custom-gpt { + font-size: 20px; + background-position: center; + background-repeat: no-repeat; + background-size: 15px 15px; + color: #e5e5e5; + border-radius: 7px; + } + + #custom-gpt:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #cava-player .status { + font-size: 18px; + color: #e5e5e5; + } + + #custom-power { + color: #e5e5e5; + padding: 1px 5px 1px 3px; + margin-right: 1px; + } + + #custom-power:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-menu { + color: #89b4fa; + padding: 1px 10px 1px 10px; + margin-left: 5px; + } + + #custom-menu:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-qs-wallpaper, + #custom-qs-vid-wallpaper { + color: #74c7ec; + } + + #custom-qs-wallpaper:hover, + #custom-qs-vid-wallpaper:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #custom-swaync { + color: #a6e3a1; + } + + #custom-swaync.notification, + #custom-swaync.dnd-notification, + #custom-swaync.inhibited-notification, + #custom-swaync.dnd-inhibited-notification { + color: #f38ba8; + } + + #custom-swaync:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #battery { + color: #e5e5e5; + } + + #battery.charging, + #battery.plugged { + color: #85eb81; + } + + #battery:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + @keyframes blink { + to { + background: rgb(30, 30, 46); + color: #000000; + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + } + + #battery.critical:not(.charging) { + color: #e5e5e5; + animation-name: blink; + animation-duration: 0.5s; + animation-timing-function: steps(12); + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #power-profiles-daemon { + padding-right: 15px; + color: #000000; + } + + #power-profiles-daemon.performance { + color: #e5e5e5; + } + + #power-profiles-daemon.balanced { + color: #e5e5e5; + } + + #power-profiles-daemon.power-saver { + color: #000000; + } + + label:focus { + background: rgb(30, 30, 46); + } + + #memory { + color: #e5e5e5; + padding: 1px 9px 1px 5px; + } + + #memory:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #disk { + background: rgb(30, 30, 46); + } + + #backlight { + color: #e5e5e5; + } + + #network { + color: #e5e5e5; + } + + #network.disconnected { + color: #f2564b; + } + + #network:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #pulseaudio { + color: #e5e5e5; + } + + #pulseaudio.muted { + color: #e5e5e5; + } + + #wireplumber { + color: #000000; + } + + #pulseaudio:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #wireplumber.muted { + background: rgb(30, 30, 46); + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + + #custom-media { + color: #2a5c45; + min-width: 100px; + } + + #custom-media.custom-spotify { + background: rgb(30, 30, 46); + padding: 1px 10px 1px 10px; + margin-top: 5px; + } + + #custom-media.custom-vlc { + background: rgb(30, 30, 46); + margin-top: 5px; + } + + #temperature { + color: #e5e5e5; + } + + #temperature.critical { + background-color: #eb4d4b; + } + + #temperature:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + #tray { + background-color: #2980b9; + } + + #tray > .passive { + -gtk-icon-effect: dim; + } + + #tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; + } + + /* Idle inhibitor - coffee mug color changes */ + #idle_inhibitor { + color: #f38ba8; /* default red (deactivated) */ + } + + #idle_inhibitor.activated { + color: #a6e3a1; /* green when activated */ + } + + #idle_inhibitor.deactivated { + color: #f38ba8; /* red when deactivated */ + } + + #idle_inhibitor:hover { + background-color: rgba(255, 255, 255, 0.1); + } + ''; + }; +} diff --git a/home-manager/programs/hypr/waybar/waybar-mangowc-jak-catppuccin.nix b/home-manager/programs/hypr/waybar/waybar-mangowc-jak-catppuccin.nix new file mode 100644 index 0000000..6a18fcb --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-mangowc-jak-catppuccin.nix @@ -0,0 +1,568 @@ +{pkgs, ...}: let + scriptsDir = ../waybar/scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); + + waybarCava = pkgs.writeShellScriptBin "WaybarCava" '' + set -euo pipefail + if ! command -v cava >/dev/null 2>&1; then + echo "cava not found in PATH" >&2 + exit 1 + fi + bar="▁▂▃▄▅▆▇█" + dict="s/;//g" + bar_length=''${#bar} + for ((i = 0; i < bar_length; i++)); do + dict+=";s/$i/''${bar:$i:1}/g" + done + RUNTIME_DIR="''${XDG_RUNTIME_DIR:-/tmp}" + pidfile="$RUNTIME_DIR/waybar-cava.pid" + if [ -f "$pidfile" ]; then + oldpid=$(cat "$pidfile" || true) + if [ -n "''${oldpid:-}" ] && kill -0 "$oldpid" 2>/dev/null; then + kill "$oldpid" 2>/dev/null || true + sleep 0.1 || true + fi + fi + echo $$ > "$pidfile" + config_file=$(mktemp "''${RUNTIME_DIR}/waybar-cava.XXXXXX.conf") + cleanup() { rm -f "$config_file" "$pidfile"; } + trap cleanup EXIT INT TERM + cat >"$config_file" </dev/null 2>&1; then + echo "cava not found in PATH" >&2 + exit 1 + fi + + # Characters for vertical bars (0..7) + bar="▁▂▃▄▅▆▇█" + + # Build sed script that maps 0..7 to glyphs and strips ';' + dict="s/;//g" + bar_length=''${#bar} + for ((i = 0; i < bar_length; i++)); do + dict+=";s/$i/''${bar:$i:1}/g" + done + + RUNTIME_DIR="''${XDG_RUNTIME_DIR:-/tmp}" + pidfile="$RUNTIME_DIR/waybar-cava.pid" + if [ -f "$pidfile" ]; then + oldpid=$(cat "$pidfile" || true) + if [ -n "''${oldpid:-}" ] && kill -0 "$oldpid" 2>/dev/null; then + kill "$oldpid" 2>/dev/null || true + sleep 0.1 || true + fi + fi + echo $$ > "$pidfile" + + config_file=$(mktemp "''${RUNTIME_DIR}/waybar-cava.XXXXXX.conf") + cleanup() { + rm -f "$config_file" "$pidfile" + } + trap cleanup EXIT INT TERM + + cat >"$config_file" < * { margin: 0; } + /* Tighten QS buttons margins/padding to bring them closer */ + #custom-qs_wallpapers_apply, + #custom-qs_vid_wallpapers_apply { + margin: 2px 2px; + padding: 2px 4px; + } + #idle_inhibitor { + color: #b45bcf; + background: #3a3c4e; + margin: 4px 0px; + padding: 2px 14px; + border-radius: 10px; + } + #custom-power { + color: #ea51b2; /* Red color for power menu */ + background: #3a3c4e; + margin: 4px 4px; + padding: 10px 10px; + border-radius: 10px; + } + '' + ]; + }; + } diff --git a/home-manager/programs/hypr/waybar/waybar-pctrade-catppuccin.nix b/home-manager/programs/hypr/waybar/waybar-pctrade-catppuccin.nix new file mode 100644 index 0000000..6802976 --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-pctrade-catppuccin.nix @@ -0,0 +1,340 @@ +{ + pkgs, + lib, + host, + config, + ... +}: let + # Keep consistency with other modules; scriptsDir can supply Weather.py, etc. + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); +in + with lib; { + # Install any helper scripts from ./scripts into ~/.config/waybar/scripts + home.file = builtins.listToAttrs ( + map + (name: { + name = ".config/waybar/scripts/" + name; + value = { + source = "${scriptsDir}/${name}"; + executable = true; + }; + }) + scripts + ); + + programs.waybar = { + enable = true; + package = pkgs.waybar; + + # Waybar configuration + settings = [ + { + layer = "top"; + position = "top"; + height = 30; + spacing = 4; + + # Layout to mirror waybarpc/config (with requested removals) + # - Left: start menu, clock, separator, weather + # - Center: workspaces + # - Right: tray, pulseaudio, battery, swaync, idle_inhibitor, power + modules-left = [ + "custom/startmenu" + "clock" + "custom/weather" + ]; + modules-center = [ + "hyprland/workspaces" + ]; + modules-right = [ + "battery" + "tray" + "custom/notification" + "idle_inhibitor" + "pulseaudio" + "custom/power" + ]; + + # Workspaces (Hyprland) + "hyprland/workspaces" = { + format = "{name}"; + format-icons = { + default = " "; + active = " "; + urgent = " "; + }; + on-scroll-up = "hyprctl dispatch workspace e+1"; + on-scroll-down = "hyprctl dispatch workspace e-1"; + }; + + # 12-hour time format + clock = { + format = '' {:%I:%M %p}''; + tooltip = true; + tooltip-format = "{:%A, %d.%B %Y }\n{calendar}"; + }; + + # Idle inhibitor with coffee mug icon + idle_inhibitor = { + format = "{icon}"; + format-icons = { + activated = ""; + deactivated = ""; + }; + tooltip = true; + }; + + # Notifications (SwayNotificationCenter via swaync-client) + "custom/notification" = { + tooltip = false; + format = "{icon}"; + format-icons = { + notification = ""; + none = ""; + dnd-notification = ""; + dnd-none = ""; + inhibited-notification = ""; + inhibited-none = ""; + dnd-inhibited-notification = ""; + dnd-inhibited-none = ""; + }; + return-type = "json"; + exec-if = "which swaync-client"; + exec = "swaync-client -swb"; + on-click = "swaync-client -t"; + escape = true; + }; + + # Audio + pulseaudio = { + format = "{icon} {volume}% {format_source}"; + format-bluetooth = "{volume}% {icon} {format_source}"; + format-bluetooth-muted = " {icon} {format_source}"; + format-muted = " {format_source}"; + format-source = " {volume}%"; + format-source-muted = ""; + format-icons = { + headphone = ""; + hands-free = ""; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = ["" "" ""]; + }; + on-click = "sleep 0.1 && pavucontrol"; + }; + + # Battery + battery = { + states = { + warning = 30; + critical = 15; + }; + format = "{icon} {capacity}%"; + format-charging = "󰂄 {capacity}%"; + format-plugged = "󱘖 {capacity}%"; + format-icons = [ + "󰁺" + "󰁻" + "󰁼" + "󰁽" + "󰁾" + "󰁿" + "󰂀" + "󰂁" + "󰂂" + "󰁹" + ]; + tooltip = false; + }; + + # Tray + tray = { + spacing = 10; + }; + + # Start menu and power menu + "custom/startmenu" = { + format = ""; + tooltip = true; + "tooltip-format" = "App menu"; + # on-click = "rofi -show drun"; + on-click = "launch-nwg-menu"; + "on-click-right" = "nwg-drawer -mr 225 -ml 225 -mt 200 -mb 200 -is 48 --spacing 15"; + }; + + "custom/power" = { + tooltip = true; + "tooltip-format" = "Power menu: Left-click for QS logout"; + format = " ⏻ "; + on-click = "qs-wlogout"; + }; + + # Weather widget (uses existing ddubsos Weather.py script if present) + "custom/weather" = { + return-type = "json"; + exec = "sh -lc 'WEATHER_ICON_STYLE=emoji WEATHER_TOOLTIP_MARKUP=1 ~/.local/bin/weather'"; + interval = 600; + tooltip = true; + }; + } + ]; + + # Styling to mirror ~/Projects/waybarpc/style.css (with Catppuccin-like colors) + style = lib.concatStrings [ + '' + /* Color variables based on waybarpc/colors.css */ + @define-color bg #1a1b26; + @define-color bg-alt #16161e; + @define-color bg-alt2 #24283b; + @define-color bg-alt3 #414868; + @define-color border #565f89; + @define-color border2 #6183bb; + @define-color text #c0caf5; + @define-color text-dark #1a1b26; /* alias for typo tex-dark in original */ + @define-color tex-dark #1a1b26; + @define-color accent #7e9cd8; + @define-color accent2 #957fb8; + @define-color red #f7768e; + @define-color green #a6e3a1; + @define-color blue #89b4fa; + + * { + font-family: JetBrainsMono Nerd Font, monospace; + font-size: 13px; + } + + window#waybar { background-color: @bg; } + window#waybar.hidden { opacity: 0.5; margin: 15px; } + + /* Workspaces container */ + #workspaces { + background-color: @bg-alt; + padding: 2px; + margin: 2px; + border-radius: 10px; + } + /* 3D shaded workspace buttons */ + #workspaces button { + all: initial; + min-width: 0; + background: linear-gradient(to bottom, @bg-alt3, @bg); + padding: 6px 12px; + transition: all 0.1s linear; + border-top: 1px solid @border; + box-shadow: 0px 17px 5px 1px rgba(0, 0, 0, 0.2); + color: @accent2; + font-weight: 800; + text-shadow: -1px -1px 1px rgba(205, 214, 244, 0.1), 0px 2px 3px rgba(0, 0, 0, 0.3); + } + #workspaces button:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + } + #workspaces button:last-child { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + } + #workspaces button.empty { + color: #737373; + background: transparent; + text-shadow: none; + box-shadow: none; + } + #workspaces button.active { + box-shadow: 0px 20px 8px 2px rgba(0, 0, 0, 0.35); + background: linear-gradient(to bottom, @bg-alt3, @bg-alt); + color: @text; + font-size: 25px; + border-top: 1px solid @border2; + border-bottom: 3px solid @border2; + text-shadow: 0px 0px 14px @text; + } + #workspaces button.focused { + background: linear-gradient(to bottom, @accent2, @bg-alt3); + color: @bg; + text-shadow: 0px 0px 12px @accent2; + font-size: 22px; + border-top: 1px solid @accent2; + } + #workspaces button.visible { + background: linear-gradient(to bottom, @bg-alt3, @bg-alt2); + color: @accent; + text-shadow: none; + font-size: 18px; + border-top: 1px solid @border; + } + #workspaces button.urgent { + background-color: @red; + color: @bg; + text-shadow: 0px 0px 8px @red; + font-weight: bold; + } + + /* Clock with gradient block */ + #clock { + background: linear-gradient(to bottom, @bg-alt3, @bg); + font-size: 14px; + margin: 2px; + padding: 6px 10px; + border-radius: 6px; + font-weight: bold; + border-top: 1px solid @border; + border-bottom: 2px solid @bg-alt; + color: @text; + } + + /* Notifications state color cues */ + #custom-notification.none { color: @green; } + #custom-notification.notification, #custom-notification.dnd-notification, #custom-notification.inhibited-notification, #custom-notification.dnd-inhibited-notification { color: @red; } + + /* Idle inhibitor colors */ + #idle_inhibitor.activated { color: @green; } + #idle_inhibitor.deactivated { color: @red; } + + /* Right-side blocks */ + #pulseaudio, #battery, #tray { + background: linear-gradient(to bottom, @bg-alt3, @bg); + padding: 4px 8px; + margin: 2px; + border-radius: 6px; + border-top: 1px solid @border; + border-bottom: 2px solid @bg-alt; + font-weight: bold; + color: @text; + } + #pulseaudio { margin-left: 0; margin-right: 0; } + + /* Shaded block style (match workspace button look) */ + #custom-startmenu, #custom-weather, #idle_inhibitor, #custom-power, #custom-notification { + background: linear-gradient(to bottom, @bg-alt3, @bg); + padding: 6px 10px; + margin: 2px; + border-radius: 6px; + border-top: 1px solid @border; + border-bottom: 2px solid @bg-alt; + box-shadow: 0px 12px 4px 1px rgba(0, 0, 0, 0.2); + text-shadow: -1px -1px 1px rgba(205, 214, 244, 0.1), 0px 2px 3px rgba(0, 0, 0, 0.3); + color: @text; + font-size: 14px; + } + /* Make icons slightly larger for clarity */ + #custom-startmenu, #idle_inhibitor, #custom-power, #custom-notification { font-size: 16px; } + #battery.warning, #battery.critical, #battery.urgent { color: @red; } + + /* Separator styling */ + #custom-sep { color: @accent; } + + /* Center the startmenu glyph like the power button */ + #custom-startmenu { + min-width: 28px; + min-height: 24px; + padding-left: 8px; + padding-right: 12px; + } + '' + ]; + }; + } diff --git a/home-manager/programs/hypr/waybar/waybar-tony.nix b/home-manager/programs/hypr/waybar/waybar-tony.nix new file mode 100644 index 0000000..f01fdca --- /dev/null +++ b/home-manager/programs/hypr/waybar/waybar-tony.nix @@ -0,0 +1,449 @@ +{ + pkgs, + lib, + host, + config, + ... +}: let + inherit (import ../../../hosts/${host}/variables.nix) clock24h; + scriptsDir = ./scripts; + scripts = builtins.attrNames (builtins.readDir scriptsDir); +in + with lib; { + # Configure & Theme Waybar (Tony) + home.file = builtins.listToAttrs ( + map (name: { + name = ".config/waybar/scripts/" + name; + value = { + source = "${scriptsDir}/${name}"; + executable = true; + }; + }) + scripts + ); + + programs.waybar = { + enable = true; + package = pkgs.waybar; + settings = [ + { + layer = "top"; + position = "top"; + height = 30; + spacing = 4; + + modules-left = [ + "custom/startmenu" + "custom/sep_gap_left" + "group/qs_wallpapers" + "custom/sep" + "hyprland/workspaces" + "custom/sep" + #"custom/sep" + "hyprland/window" + "custom/sep" + ]; + modules-center = [ + #"custom/sep" + "idle_inhibitor" + "custom/weather" + "custom/notification" + #"custom/sep" + ]; + modules-right = [ + "custom/sep" + "tray" + "custom/sep" + "pulseaudio" + #"network" + "cpu" + #"memory" + "clock" + "custom/sep" + "custom/power" + ]; + + "hyprland/workspaces" = { + disable-scroll = true; + all-outputs = true; + warp-on-scroll = false; + format = "{name}"; + persistent-workspaces = { + "*" = 9; + }; + }; + "hyprland/window" = { + max-length = 40; + seperate-outputs = false; + }; + tray = { + spacing = 10; + }; + clock = { + format-alt = "{:%Y-%m-%d}"; + }; + cpu = { + format = "CPU: {usage}%"; + tooltip = false; + }; + memory = { + format = "Mem: {used}GiB"; + }; + disk = { + interval = 60; + path = "/"; + format = "Disk: {free}"; + }; + battery = { + states = { + good = 95; + warning = 30; + critical = 15; + }; + format = "Bat: {capacity}% {icon} {time}"; + format-plugged = "{capacity}% "; + format-alt = "Bat {capacity}%"; + format-time = "{H}:{M}"; + format-icons = [ + "" + "" + "" + "" + "" + ]; + }; + network = { + format-icons = [ + "󰤯" + "󰤟" + "󰤢" + "󰤥" + "󰤨" + ]; + format-ethernet = "{ifname} {ipaddr}"; + format-wifi = "{essid} {signalStrength}% {ipaddr}"; + format-disconnected = "󰤮"; + tooltip = true; + tooltip-format = "{ifname}\nIPv4: {ipaddr}/{cidr}\nGateway: {gwaddr}\nSSID: {essid}\nSignal: {signalStrength}%"; + on-click = "nmtui"; + }; + "custom/sep" = { + format = "|"; + interval = 0; + }; + "idle_inhibitor" = { + format = "{icon}"; + format-icons = { + activated = ""; + deactivated = ""; + }; + tooltip = true; + }; + "custom/startmenu" = { + format = ""; + tooltip = true; + "tooltip-format" = "App menu"; + on-click = "rofi -show drun"; + "on-click-right" = "nwg-drawer -mr 225 -ml 225 -mt 200 -mb 200 -is 48 --spacing 15"; + }; + + # Wallpaper quick-switchers close to startmenu + "custom/qs_wallpapers_apply" = { + format = ""; + tooltip = true; + "tooltip-format" = "Set image wallpaper"; + on-click = "qs-wallpapers-apply"; + }; + "custom/qs_vid_wallpapers_apply" = { + format = ""; + tooltip = true; + "tooltip-format" = "Set video wallpaper"; + on-click = "qs-vid-wallpapers-apply"; + }; + "group/qs_wallpapers" = { + orientation = "inherit"; + modules = [ + "custom/qs_wallpapers_apply" + "custom/sep_gap" + "custom/qs_vid_wallpapers_apply" + ]; + }; + + # Narrow spacer for QS icons + "custom/sep_gap" = { + format = " "; + tooltip = false; + }; + # Spacer between startmenu and first QS icon + "custom/sep_gap_left" = { + format = " "; + tooltip = false; + }; + "custom/notification" = { + tooltip = false; + format = "{icon} {}"; + format-icons = { + notification = ""; + none = ""; + dnd-notification = ""; + dnd-none = ""; + inhibited-notification = ""; + inhibited-none = ""; + dnd-inhibited-notification = ""; + dnd-inhibited-none = ""; + }; + return-type = "json"; + exec-if = "which swaync-client"; + exec = "swaync-client -swb"; + on-click = "swaync-client -t"; + escape = true; + }; + "custom/weather" = { + return-type = "json"; + exec = "sh -lc 'WEATHER_ICON_STYLE=emoji WEATHER_TOOLTIP_MARKUP=1 ~/.local/bin/weather'"; + interval = 600; + tooltip = true; + }; + pulseaudio = { + format = "{icon} {volume}% {format_source}"; + format-bluetooth = "{volume}% {icon} {format_source}"; + format-bluetooth-muted = " {icon} {format_source}"; + format-muted = " {format_source}"; + format-source = " {volume}%"; + format-source-muted = ""; + format-icons = { + headphone = ""; + hands-free = ""; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = [ + "" + "" + "" + ]; + }; + on-click = "pavucontrol"; + }; + "custom/power" = { + format = " ⏻ "; + tooltip = true; + "tooltip-format" = "Power menu: Left-click for QS logout, Right-click for rofi power menu"; + "on-click" = "qs-wlogout"; + "on-click-right" = "~/.config/waybar/scripts/power-menu.sh"; + }; + } + ]; + style = concatStrings [ + '' + @define-color bg #1a1b26; + @define-color fg #a9b1d6; + @define-color blk #32344a; + @define-color red #f7768e; + @define-color grn #9ece6a; + @define-color ylw #e0af68; + @define-color blu #7aa2f7; + @define-color mag #ad8ee6; + @define-color cyn #0db9d7; + @define-color brblk #444b6a; + @define-color white #ffffff; + + * { + font-family: "JetBrainsMono Nerd Font", monospace; + font-size: 16px; + font-weight: bold; + } + + window#waybar { + background-color: @bg; + color: @fg; + } + + #workspaces button { + padding: 0 6px; + color: @cyn; + background: transparent; + border-bottom: 3px solid @bg; + } + #workspaces button.active { + color: @cyn; + border-bottom: 3px solid @mag; + } + #workspaces button.empty { + color: @white; + } + #workspaces button.empty.active { + color: @cyn; + border-bottom: 3px solid @mag; + } + + #workspaces button.urgent { + background-color: @red; + } + + button:hover { + background: inherit; + box-shadow: inset 0 -3px #ffffff; + } + + #clock, + #custom-sep, + #battery, + #cpu, + #memory, + #disk, + #network, + #tray, + #pulseaudio, + #idle_inhibitor, + #custom-notification, + #custom-power { + padding: 0 8px; + color: @white; + } + /* Rounded, raised bottom effect for center/right modules */ + #clock, + #battery, + #cpu, + #memory, + #disk, + #network, + #tray, + #pulseaudio, + #idle_inhibitor, + #custom-notification, + #custom-power { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + box-shadow: inset 0 -2px rgba(255,255,255,0.07); + } + /* Tighten spacing between the two QS buttons */ + #group-qs_wallpapers { margin: 0; padding: 0; } + + /* Make QS icons match startmenu color and background (bright green) */ + /* Start menu stays bright green; QS icons switch to clock blue (@cyn) */ + #custom-startmenu { + color: @grn; + background: transparent; + font-size: 21px; /* ~30% larger than base 16px */ + } + #custom-qs_wallpapers_apply, + #custom-qs_vid_wallpapers_apply { + color: @cyn; /* match clock color */ + background: transparent; + font-size: 21px; /* ~30% larger than base 16px */ + } + /* Prevent QS icons from being clipped; add a tiny internal pad */ + #custom-qs_wallpapers_apply, + #custom-qs_vid_wallpapers_apply { + padding: 0 2px; + min-width: 20px; + } + #custom-startmenu { margin-right: 6px; } + + #custom-sep { + color: @brblk; + } + /* Width for gap spacer between QS icons */ + #custom-sep_gap { padding: 0 1px; } + #custom-sep_gap_left { padding: 0 2px; } + + #clock { + color: @cyn; + border-bottom: 4px solid @cyn; + } + + #battery { + color: @mag; + border-bottom: 4px solid @mag; + } + + #disk { + color: @ylw; + border-bottom: 4px solid @ylw; + } + + #memory { + color: @mag; + border-bottom: 4px solid @mag; + } + + #cpu { + color: @grn; + border-bottom: 4px solid @grn; + } + + #network { + color: @blu; + border-bottom: 4px solid @blu; + } + + #network.disconnected { + background-color: @red; + } + + #pulseaudio { + color: @blu; + border-bottom: 4px solid @blu; + } + + /* Center the idle_inhibitor icon within its pill */ + #idle_inhibitor { + /* Center visually via symmetric padding + fixed min width */ + padding-left: 10px; + padding-right: 10px; + min-width: 26px; /* keep a consistent hit area */ + border-bottom: 4px solid @brblk; /* default raised strip */ + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + } + #idle_inhibitor label { + margin: 0; /* remove any label offset */ + padding: 0; + } + + #idle_inhibitor.deactivated { + color: @red; + border-bottom: 4px solid @red; + } + + #idle_inhibitor.activated { + color: @grn; + border-bottom: 4px solid @grn; + margin-right: 5px; + } + + #custom-notification { + color: @grn; /* default green */ + border-bottom: 4px solid @grn; + } + /* Turn red on new alerts */ + #custom-notification.notification, + #custom-notification.dnd-notification, + #custom-notification.inhibited-notification { + color: @red; + border-bottom: 4px solid @red; + } + /* Add subtle raised effect to center modules too */ + #custom-notification, + #idle_inhibitor { + box-shadow: inset 0 -3px @brblk; + } + + #tray { + background-color: @bg; /* match bar background */ + border-bottom: 4px solid @brblk; /* restore raised strip */ + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + } + + #custom-power { + color: @red; + border-bottom: 4px solid @red; + } + + '' + ]; + }; + } diff --git a/home-manager/programs/programs.nix b/home-manager/programs/programs.nix index 6dc4d22..ef08a0c 100644 --- a/home-manager/programs/programs.nix +++ b/home-manager/programs/programs.nix @@ -10,7 +10,8 @@ ./hypr/hyprland.nix ./hypr/hyprlock.nix ./hypr/theme.nix - ./hypr/waybar.nix + # ./hypr/waybar.nix + ./hypr/waybar/waybar-ddubs-2.nix ./hypr/wlogout.nix ] # macOS programs diff --git a/hosts/buildbox/configuration.nix b/hosts/buildbox/configuration.nix index 4d7ef33..a92141b 100644 --- a/hosts/buildbox/configuration.nix +++ b/hosts/buildbox/configuration.nix @@ -1,7 +1,3 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running 'nixos-help'). - { config, pkgs, diff --git a/hosts/ender-ml/configuration.nix b/hosts/ender-ml/configuration.nix index d4a1d14..845cb00 100644 --- a/hosts/ender-ml/configuration.nix +++ b/hosts/ender-ml/configuration.nix @@ -1,7 +1,3 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running 'nixos-help'). - { config, pkgs, diff --git a/hosts/eva-01/configuration.nix b/hosts/eva-01/configuration.nix index 939e3e4..e3349b7 100644 --- a/hosts/eva-01/configuration.nix +++ b/hosts/eva-01/configuration.nix @@ -1,7 +1,3 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - { config, pkgs, @@ -31,7 +27,12 @@ # Nix optimizations nix.optimise.automatic = true; - nix.settings.auto-optimise-store = true; + nix.settings = { + download-buffer-size = 200000000; + auto-optimise-store = true; + substituters = ["https://hyprland.cachix.org"]; + trusted-public-keys = ["hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="]; + }; nix.gc = { automatic = true; dates = "weekly"; @@ -94,7 +95,8 @@ }; # Set your time zone. - time.timeZone = "America/Los_Angeles"; + # time.timeZone = "America/Los_Angeles"; + services.automatic-timezoned.enable = true; # Select internationalisation properties. i18n.defaultLocale = "en_US.UTF-8"; @@ -139,7 +141,8 @@ #Enable Ollama daemon services.ollama = { enable = true; - package = pkgs-unstable.ollama; + acceleration = "cuda"; + package = pkgs-unstable.ollama-cuda; }; #Enable Hyprland @@ -190,17 +193,10 @@ users.users.rogueking = { isNormalUser = true; description = "rogueking"; - extraGroups = [ - "networkmanager" - "wheel" - "docker" - ]; + extraGroups = [ "networkmanager" "wheel" "docker" ]; shell = pkgs.zsh; #packages = [ inputs.home-manager.packages.${pkgs.system}.default ]; packages = with pkgs; [ - #apps - #cli - # thunderbird ]; }; @@ -264,8 +260,6 @@ enable = true; allowedTCPPorts = [ 22 - # 80 - # 443 ]; logRefusedConnections = true; }; @@ -274,7 +268,7 @@ # $ nix search wget environment.systemPackages = with pkgs; [ # System-level only - fprintd # fingerprint daemon integration + fprintd ]; fonts.packages = with pkgs; [ @@ -300,39 +294,6 @@ backupFileExtension = "backup"; }; - #home-manager = { - # extraSpecialArgs = { inherit inputs; }; - # users = { - # "rogueking" = import ../home-manager/home.nix; - # }; - # backupFileExtension = "backup"; - #}; - - # Some programs need SUID wrappers, can be configured further or are - # started in user sessions. - # programs.mtr.enable = true; - # programs.gnupg.agent = { - # enable = true; - # enableSSHSupport = true; - # }; - - # List services that you want to enable: - - # Enable the OpenSSH daemon. - # services.openssh.enable = true; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "25.05"; # Did you read the comment? + system.stateVersion = "25.11"; # Did you read the comment? } diff --git a/hosts/eva-02/configuration.nix b/hosts/eva-02/configuration.nix index 0838d77..3c13ccc 100644 --- a/hosts/eva-02/configuration.nix +++ b/hosts/eva-02/configuration.nix @@ -1,7 +1,3 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - { config, pkgs, @@ -32,17 +28,10 @@ }; networking.hostName = "eva-02"; # Define your hostname. - # Configure network proxy if necessary - # networking.proxy.default = "http://user:password@proxy:port/"; - # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; # Set your time zone. time.timeZone = "America/Los_Angeles"; - # Enable the X11 windowing system. - # You can disable this if you're only using the Wayland session. - # services.xserver.enable = true; # Enable for Darwin compatibility - # Enable sound with pipewire. # hardware.pulseaudio.enable = false; #hardware.pulseaudio = { @@ -174,24 +163,5 @@ backupFileExtension = "backup"; }; - # Some programs need SUID wrappers, can be configured further or are - # started in user sessions. - # programs.mtr.enable = true; - # programs.gnupg.agent = { - # enable = true; - # enableSSHSupport = true; - # }; - - # List services that you want to enable: - - # Enable the OpenSSH daemon. - # services.openssh.enable = true; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - system.stateVersion = 6; } diff --git a/hosts/eva-03/configuration.nix b/hosts/eva-03/configuration.nix index 90671b8..2e91d26 100644 --- a/hosts/eva-03/configuration.nix +++ b/hosts/eva-03/configuration.nix @@ -1,7 +1,3 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - { config, configPath, @@ -41,7 +37,12 @@ # Nix optimizations nix.optimise.automatic = true; - nix.settings.auto-optimise-store = true; + nix.settings = { + download-buffer-size = 200000000; + auto-optimise-store = true; + substituters = ["https://hyprland.cachix.org"]; + trusted-public-keys = ["hyprland.cachix.org-1:a7pgxzMz7+chwVL3/pzj6jIBMioiJM7ypFP8PwtkuGc="]; + }; nix.gc = { automatic = true; dates = "weekly"; @@ -52,10 +53,6 @@ networking.hostName = "eva-03"; # Define your hostname. # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - # Configure network proxy if necessary - # networking.proxy.default = "http://user:password@proxy:port/"; - # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; - # Enable networking networking.networkmanager.enable = true; @@ -93,10 +90,6 @@ # Modesetting is required. modesetting.enable = true; - # Nvidia power management. Experimental, and can cause sleep/suspend to fail. - # Enable this if you have graphical corruption issues or application crashes after waking - # up from sleep. This fixes it by saving the entire VRAM memory to /tmp/ instead - # of just the bare essentials. # powerManagement.enable = false; open = false; @@ -169,9 +162,22 @@ openFirewall = true; acceleration = "cuda"; package = pkgs-unstable.ollama-cuda; - environmentVariables = { - CUDA_VISIBLE_DEVICES = "0,1"; - }; + # environmentVariables = { + # CUDA_VISIBLE_DEVICES = "0,1"; + # }; + }; + + services.comfyui = { + enable = true; + enableManager = true; + listenAddress = "0.0.0.0"; + dataDir = "/home/rogueking/Documents/models/comfyui-data"; + user = "rogueking"; + group = "users"; + package = pkgs-unstable.comfy-ui-cuda; + #createUser = true; # Use existing user + # If dataDir is on a separate mount (NFS, ZFS dataset, etc.): + # requiresMounts = [ "home-myuser-comfyui\\x2ddata.mount" ]; }; services.fprintd.enable = true; @@ -303,7 +309,8 @@ pkgs-unstable hostname hostTypes - ; + ; + host = hostname; }; users = { "rogueking" = import ./../../home-manager/home.nix; @@ -311,6 +318,6 @@ backupFileExtension = "backup"; }; - system.stateVersion = "25.05"; + system.stateVersion = "25.11"; }