From cf727978f00e011539e8a2b1bb0cf5e595d8a9e1 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 22 Sep 2025 10:35:32 -0400 Subject: Weather.py: switch to Open-Meteo; add caching, reverse geocoding, robust geolocation, and config options Conflicts: config/hypr/UserScripts/Weather.py interactive rebase in progress; onto ce07268 Last command done (1 command done): pick 2a5a7c5 # Weather.py: switch to Open-Meteo; add caching, reverse geocoding, robust geolocation, and config options No commands remaining. You are currently rebasing branch 'development' on 'ce07268'. Changes to be committed: modified: config/hypr/UserScripts/Weather.py --- config/hypr/UserScripts/Weather.py | 1080 ++++++++++++++++++++++++++++++++++-- 1 file changed, 1033 insertions(+), 47 deletions(-) diff --git a/config/hypr/UserScripts/Weather.py b/config/hypr/UserScripts/Weather.py index a8b70417..a71fe8ca 100755 --- a/config/hypr/UserScripts/Weather.py +++ b/config/hypr/UserScripts/Weather.py @@ -1,15 +1,70 @@ #!/usr/bin/env python3 # /* ---- đŸ’Ģ https://github.com/JaKooLit đŸ’Ģ ---- */ # -# original code https://gist.github.com/Surendrajat/ff3876fd2166dd86fb71180f4e9342d7 -# weather using python +# Rewritten to use Open-Meteo APIs (worldwide, no API key) for robust weather data. +# Outputs Waybar-compatible JSON and a simple text cache. -import requests import json import os -from pyquery import PyQuery # install using `pip install pyquery` +import sys +import time +import html +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/hypr/UserScripts/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 metric) +UNITS = os.getenv("WEATHER_UNITS", "metric").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 -# weather icons -weather_icons = { +SESSION = requests.Session() +SESSION.headers.update({"User-Agent": UA}) + +# =============== Icon and status mapping =============== +# Reuse prior icon set for continuity +WEATHER_ICONS = { "sunnyDay": "ķ°–™", "clearNight": "ķ°–”", "cloudyFoggyDay": "", @@ -22,45 +77,524 @@ weather_icons = { "default": "", } +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 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" + + 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 + 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: + 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: + idx = times.index(cur_time) if cur_time in times else 0 + + 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}") + + if not isinstance(code, int) or code < 0: + unavailable = True + log_debug("Weather code invalid; setting status to 'Condition 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" -# Get current location based on IP address -def get_location(): - response = requests.get("https://ipinfo.io") - data = response.json() - loc = data["loc"].split(",") - return float(loc[0]), float(loc[1]) + 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}" -# Get latitude and longitude -latitude, longitude = get_location() + # 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]) -# Open-Meteo API endpoint -url = f"https://weather.com/en-PH/weather/today/l/{latitude},{longitude}" + out_data = { + "text": f"{icon} {temp_str}", + "alt": status, + "tooltip": tooltip_text, + "class": code_for_class, + } -# manual location_id -# NOTE: if you want to add manually, make sure you disable def get_location above -# to get your own location_id, go to https://weather.com & search your location. -# once you choose your location, you can see the location_id in the URL(64 chars long hex string) -# like this: https://weather.com/en-PH/weather/today/l/bca47d1099e762a012b9a139c36f30a0b1e647f69c0c4ac28b537e7ae9c1c200 -# location_id = "bca47d1099e762a012b9a139c36f30a0b1e647f69c0c4ac28b537e7ae9c1c200" # TODO + 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" + ) -# NOTE to change to deg F, change the URL to your preffered location after weather.com -# Default is English-Philippines with Busan, South Korea as location_id -# get html page -# url = "https://weather.com/en-PH/weather/today/l/" + location_id + return out_data, simple_weather -html_data = PyQuery(url=url) -# current temperature -temp = html_data("span[data-testid='TemperatureValue']").eq(0).text() +def main() -> None: + lat, lon = get_coords() -# current status phrase + # 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() status = html_data("div[data-testid='wxPhrase']").text() status = f"{status[:16]}.." if len(status) > 17 else status # status code -# Fix provided by mio-dokuhaki -status_code = html_data("#regionHeader").attr("class").split(" ")[1].split("-")[0] +status_code = html_data("#regionHeader").attr("class").split(" ")[2].split("-")[2] # status icon icon = ( @@ -89,7 +623,7 @@ temp_max = ( temp_min_max = f"ī‹‹ {temp_min}\t\t {temp_max}" # wind speed -wind_speed = str(html_data("span[data-testid='Wind'] > span").text()) +wind_speed = html_data("span[data-testid='Wind']").text().split("\n")[1] wind_text = f" {wind_speed}" # humidity @@ -129,19 +663,471 @@ out_data = { "alt": status, "tooltip": tooltip_text, "class": status_code, +======= +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", +>>>>>>> 2a5a7c5 (Weather.py: switch to Open-Meteo; add caching, reverse geocoding, robust geolocation, and config options) } -print(json.dumps(out_data)) - -simple_weather = ( - f"{icon} {status}\n" - + f" {temp} ({temp_feel_text})\n" - + f"{wind_text} \n" - + f"{humidity_text} \n" - + f"{visibility_text} AQI{air_quality_index}\n" -) -try: - with open(os.path.expanduser("~/.cache/.weather_cache"), "w") as file: - file.write(simple_weather) -except Exception as e: - print(f"Error writing to cache: {e}") + +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 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", + "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" + + 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) + icon = wmo_to_icon(code, is_day) + status = wmo_to_status(code) + + # 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]) + + out_data = { + "text": f"{icon} {temp_str}", + "alt": status, + "tooltip": tooltip_text, + "class": f"wmo-{code} {'day' if is_day else 'night'}", + } + + 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() -- cgit v1.2.3 From c6da731f145da72f1043f0c5bcde4842b43c7f9f Mon Sep 17 00:00:00 2001 From: Vighnesh Shukla Date: Tue, 23 Sep 2025 20:45:49 +0530 Subject: Made few tweaks to make the windows to stop the constant changes in the focus of the Jetbrains Softwares --- config/hypr/UserConfigs/WindowRules.conf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index e5fd11b9..baa6d29a 100644 --- a/config/hypr/UserConfigs/WindowRules.conf +++ b/config/hypr/UserConfigs/WindowRules.conf @@ -202,6 +202,14 @@ windowrule = keepaspectratio, title:^(Picture-in-Picture)$ windowrule = noblur, tag:games* windowrule = fullscreen, tag:games* + +#This not gonna take the focus to the window that appears when hovering over some of the parts of the IntelliJ Products +windowrule = noinitialfocus, class:^(jetbrains-*) +windowrule = noinitialfocus, title:^(wind.*)$ + +#This will gonna make the VS Code bluer like other apps +windowrule2 = opacity 0.8,class:^(code)$ + #windowrule = bordercolor rgb(EE4B55) rgb(880808), fullscreen:1 #windowrule = bordercolor rgb(282737) rgb(1E1D2D), floating:1 #windowrule = opacity 0.8 0.8, pinned:1 -- cgit v1.2.3 From f46fa54f44a03a6ceb02fc251dedfd5474f1094c Mon Sep 17 00:00:00 2001 From: CharlyMH Date: Fri, 3 Oct 2025 20:12:41 +0200 Subject: Update Dropterminal.sh The function now chooses the focused monitor instead of the first one in the array. --- config/hypr/scripts/Dropterminal.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/hypr/scripts/Dropterminal.sh b/config/hypr/scripts/Dropterminal.sh index fa5b899b..f1bfe0a5 100755 --- a/config/hypr/scripts/Dropterminal.sh +++ b/config/hypr/scripts/Dropterminal.sh @@ -119,7 +119,7 @@ animate_slide_up() { # Function to get monitor info for centering get_monitor_info() { - hyprctl monitors -j | jq -r '.[0] | "\(.x) \(.y) \(.width) \(.height)"' + hyprctl monitors -j | jq -r '.[] | select(.focused == true) | "\(.x) \(.y) \(.width) \(.height)"' } # Function to calculate dropdown position @@ -290,4 +290,4 @@ else hyprctl dispatch focuswindow "address:$TERMINAL_ADDR" fi fi -fi \ No newline at end of file +fi -- cgit v1.2.3 -- cgit v1.2.3 From 5c41160260900398b8f9440edf0c735946187e90 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 14:36:19 -0400 Subject: feat(hyprsunset): add Hyprsunset toggle script, SUPER+N keybind, and Waybar nightlight button across all layouts; include in status and vertical power groups --- .gitignore | 3 + config/hypr/UserConfigs/UserKeybinds.conf | 4 ++ config/hypr/scripts/Hyprsunset.sh | 77 ++++++++++++++++++++++++ config/waybar/ModulesCustom | 11 ++++ config/waybar/ModulesGroups | 2 + config/waybar/configs/[BOT & Left] SouthWest | 1 + config/waybar/configs/[BOT & Right] SouthEast | 1 + config/waybar/configs/[LEFT] WestWing | 1 + config/waybar/configs/[LEFT] WestWing v2 | 1 + config/waybar/configs/[RIGHT] EastWing | 1 + config/waybar/configs/[RIGHT] EastWing v2 | 1 + config/waybar/configs/[TOP & BOT] SummitSplit | 2 + config/waybar/configs/[TOP & BOT] SummitSplit v2 | 3 +- config/waybar/configs/[TOP] 0-Ja-0 | 1 + config/waybar/configs/[TOP] Arrow | 3 +- config/waybar/configs/[TOP] Camellia | 3 +- config/waybar/configs/[TOP] Chrysanthemum | 3 +- config/waybar/configs/[TOP] Everforest | 1 + config/waybar/configs/[TOP] Gardenia | 3 +- config/waybar/configs/[TOP] Minimal - Long | 1 + config/waybar/configs/[TOP] Minimal - Short | 1 + config/waybar/configs/[TOP] Peony | 3 +- config/waybar/configs/[TOP] Sleek | 1 + 23 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 .gitignore create mode 100755 config/hypr/scripts/Hyprsunset.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d20a9670 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +WARP.md +.warp.md +result diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index 2f0e808f..d8a1d52e 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -29,6 +29,7 @@ bind = $mainMod, H, exec, $scriptsDir/KeyHints.sh # help / cheat sheet bind = $mainMod ALT, R, exec, $scriptsDir/Refresh.sh # Refresh waybar, swaync, rofi bind = $mainMod ALT, E, exec, $scriptsDir/RofiEmoji.sh # emoji menu bind = $mainMod, S, exec, $scriptsDir/RofiSearch.sh # Google search using rofi +bind = $mainMod SHIFT, S, exec, rofi -show windows # list/switch apps using rofi bind = $mainMod ALT, O, exec, $scriptsDir/ChangeBlur.sh # Toggle blur settings bind = $mainMod SHIFT, G, exec, $scriptsDir/GameMode.sh # Toggle animations ON/OFF bind = $mainMod ALT, L, exec, $scriptsDir/ChangeLayout.sh # Toggle Master or Dwindle Layout @@ -55,6 +56,9 @@ bind = $mainMod CTRL ALT, B, exec, pkill -SIGUSR1 waybar # Toggle hide/show wayb bind = $mainMod CTRL, B, exec, $scriptsDir/WaybarStyles.sh # Waybar Styles Menu bind = $mainMod ALT, B, exec, $scriptsDir/WaybarLayout.sh # Waybar Layout Menu +# Night light toggle (Hyprsunset) +bind = $mainMod, N, exec, $scriptsDir/Hyprsunset.sh toggle + # FEATURES / EXTRAS (UserScripts) bind = $mainMod SHIFT, M, exec, $UserScripts/RofiBeats.sh # online music using rofi bind = $mainMod, W, exec, $UserScripts/WallpaperSelect.sh # Select wallpaper to apply diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh new file mode 100755 index 00000000..68e5fab8 --- /dev/null +++ b/config/hypr/scripts/Hyprsunset.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Hyprsunset toggle + Waybar status helper +# Phase 1: manual toggle only (no scheduling) +# Icons: +# - Off: bright sun +# - On: sunset icon if available, otherwise a blue sun +# +# Customize via env vars: +# HYPERSUNSET_TEMP default 4500 (K) +# HYPERSUNSET_ICON_MODE sunset|blue (default: sunset) + +STATE_FILE="$HOME/.cache/.hyprsunset_state" +TARGET_TEMP="${HYPERSUNSET_TEMP:-4500}" +ICON_MODE="${HYPERSUNSET_ICON_MODE:-sunset}" + +ensure_state() { + [[ -f "$STATE_FILE" ]] || echo "off" > "$STATE_FILE" +} + +# Render icons using pango markup to allow colorization +icon_off() { + # bright sun when not activated + printf "" +} + +icon_on() { + case "$ICON_MODE" in + sunset) + printf "" + ;; + blue) + printf "" + ;; + *) + printf "" + ;; + esac +} + +cmd_toggle() { + ensure_state + state="$(cat "$STATE_FILE" || echo off)" + if [[ "$state" == "on" ]]; then + if command -v hyprsunset >/dev/null 2>&1; then + hyprsunset -r || true + fi + echo off > "$STATE_FILE" + else + if command -v hyprsunset >/dev/null 2>&1; then + hyprsunset -t "$TARGET_TEMP" || true + fi + echo on > "$STATE_FILE" + fi +} + +cmd_status() { + ensure_state + state="$(cat "$STATE_FILE" || echo off)" + if [[ "$state" == "on" ]]; then + txt="$(icon_on)" + cls="on" + tip="Night light on @ ${TARGET_TEMP}K" + else + txt="$(icon_off)" + cls="off" + tip="Night light off" + fi + printf '{"text":"%s","class":"%s","tooltip":"%s"}\n' "$txt" "$cls" "$tip" +} + +case "${1:-}" in + toggle) cmd_toggle ;; + status) cmd_status ;; + *) echo "usage: $0 [toggle|status]" >&2; exit 2 ;; + esac diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index dddc5ccc..1f4afa20 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -104,6 +104,17 @@ "tooltip-format": "Left Click: Switch Dark-Light Themes\nMiddle Click: Wallpaper Menu\nRight Click: Waybar Styles Menu", }, +// Night light (Hyprsunset) +"custom/nightlight": { + "return-type": "json", + "exec": "$HOME/.config/hypr/scripts/Hyprsunset.sh status", + "interval": 5, + "format": "{}", + "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", + "tooltip": true, + "tooltip-format": "Night light toggle" +}, + "custom/lock": { "format": "ķ°Œž", "on-click": "$HOME/.config/hypr/scripts/LockScreen.sh", diff --git a/config/waybar/ModulesGroups b/config/waybar/ModulesGroups index 30e47f16..8d4453a2 100644 --- a/config/waybar/ModulesGroups +++ b/config/waybar/ModulesGroups @@ -89,6 +89,7 @@ }, "modules": [ "custom/power", + "custom/nightlight", "custom/lock", "keyboard-state", "custom/keyboard", @@ -131,6 +132,7 @@ }, "modules": [ "custom/power", + "custom/nightlight", "custom/lock", "custom/logout", "custom/reboot" diff --git a/config/waybar/configs/[BOT & Left] SouthWest b/config/waybar/configs/[BOT & Left] SouthWest index a039f040..594b46d3 100644 --- a/config/waybar/configs/[BOT & Left] SouthWest +++ b/config/waybar/configs/[BOT & Left] SouthWest @@ -46,6 +46,7 @@ "power-profiles-daemon", "pulseaudio#microphone", "keyboard-state", + "custom/nightlight", "custom/power", ], }, diff --git a/config/waybar/configs/[BOT & Right] SouthEast b/config/waybar/configs/[BOT & Right] SouthEast index 9a58e952..03cdb06c 100644 --- a/config/waybar/configs/[BOT & Right] SouthEast +++ b/config/waybar/configs/[BOT & Right] SouthEast @@ -46,6 +46,7 @@ "power-profiles-daemon", "pulseaudio#microphone", "keyboard-state", + "custom/nightlight", "custom/power", ], }, diff --git a/config/waybar/configs/[LEFT] WestWing b/config/waybar/configs/[LEFT] WestWing index e3f25d1a..28e5dbec 100644 --- a/config/waybar/configs/[LEFT] WestWing +++ b/config/waybar/configs/[LEFT] WestWing @@ -39,6 +39,7 @@ "backlight#vertical", "pulseaudio#microphone_vertical", "pulseaudio#vertical", + "custom/nightlight", "custom/power_vertical", "custom/menu", ], diff --git a/config/waybar/configs/[LEFT] WestWing v2 b/config/waybar/configs/[LEFT] WestWing v2 index f1ed69c3..906f83d6 100644 --- a/config/waybar/configs/[LEFT] WestWing v2 +++ b/config/waybar/configs/[LEFT] WestWing v2 @@ -40,6 +40,7 @@ "backlight#vertical", "pulseaudio/slider", "pulseaudio#microphone_vertical", + "custom/nightlight", "group/power#vert", ], diff --git a/config/waybar/configs/[RIGHT] EastWing b/config/waybar/configs/[RIGHT] EastWing index b64fe5f8..a5ce6756 100644 --- a/config/waybar/configs/[RIGHT] EastWing +++ b/config/waybar/configs/[RIGHT] EastWing @@ -39,6 +39,7 @@ "backlight#vertical", "pulseaudio#microphone_vertical", "pulseaudio#vertical", + "custom/nightlight", "custom/power_vertical", "custom/menu", ], diff --git a/config/waybar/configs/[RIGHT] EastWing v2 b/config/waybar/configs/[RIGHT] EastWing v2 index f9991bd6..28dd1e43 100644 --- a/config/waybar/configs/[RIGHT] EastWing v2 +++ b/config/waybar/configs/[RIGHT] EastWing v2 @@ -40,6 +40,7 @@ "backlight#vertical", "pulseaudio/slider", "pulseaudio#microphone_vertical", + "custom/nightlight", "group/power#vert", ], diff --git a/config/waybar/configs/[TOP & BOT] SummitSplit b/config/waybar/configs/[TOP & BOT] SummitSplit index 03c8e81b..516e9834 100644 --- a/config/waybar/configs/[TOP & BOT] SummitSplit +++ b/config/waybar/configs/[TOP & BOT] SummitSplit @@ -44,6 +44,7 @@ "network", "custom/updater", "custom/cycle_wall", + "custom/nightlight", "custom/lock", ], }, @@ -88,6 +89,7 @@ "pulseaudio", //"wireplumber", "pulseaudio#microphone", + "custom/nightlight", "custom/power", ], }], diff --git a/config/waybar/configs/[TOP & BOT] SummitSplit v2 b/config/waybar/configs/[TOP & BOT] SummitSplit v2 index 1425f657..4d576aef 100644 --- a/config/waybar/configs/[TOP & BOT] SummitSplit v2 +++ b/config/waybar/configs/[TOP & BOT] SummitSplit v2 @@ -28,13 +28,14 @@ "network", ], "modules-center": ["hyprland/window"], - "modules-right": [ +"modules-right": [ "mpris", "battery", "backlight", "pulseaudio", "group/mobo_drawer", "idle_inhibitor", + "custom/nightlight", "group/power" ], diff --git a/config/waybar/configs/[TOP] 0-Ja-0 b/config/waybar/configs/[TOP] 0-Ja-0 index c4cb0a65..6e7fc9aa 100644 --- a/config/waybar/configs/[TOP] 0-Ja-0 +++ b/config/waybar/configs/[TOP] 0-Ja-0 @@ -48,6 +48,7 @@ "custom/separator#dot-line", "mpris", "custom/separator#blank", + "custom/nightlight", "group/status", ], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Arrow b/config/waybar/configs/[TOP] Arrow index 8001d8fb..7fc55f60 100644 --- a/config/waybar/configs/[TOP] Arrow +++ b/config/waybar/configs/[TOP] Arrow @@ -36,7 +36,8 @@ "battery", "custom/arrow2", "tray", - "custom/arrow1", +"custom/arrow1", + "custom/nightlight", "clock#2" ], } diff --git a/config/waybar/configs/[TOP] Camellia b/config/waybar/configs/[TOP] Camellia index c93e9079..efaf6e20 100644 --- a/config/waybar/configs/[TOP] Camellia +++ b/config/waybar/configs/[TOP] Camellia @@ -44,7 +44,8 @@ "power-profiles-daemon", "battery", "clock#3", - "network"], + "network", + "custom/nightlight"], // Additional modules // "pulseaudio/slider": { diff --git a/config/waybar/configs/[TOP] Chrysanthemum b/config/waybar/configs/[TOP] Chrysanthemum index ebaa0ca4..d12f73e3 100644 --- a/config/waybar/configs/[TOP] Chrysanthemum +++ b/config/waybar/configs/[TOP] Chrysanthemum @@ -36,6 +36,7 @@ "modules-right": [ "pulseaudio#1", "backlight#2", - "battery"], + "battery", + "custom/nightlight"], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Everforest b/config/waybar/configs/[TOP] Everforest index a66763ed..7b007f78 100644 --- a/config/waybar/configs/[TOP] Everforest +++ b/config/waybar/configs/[TOP] Everforest @@ -45,6 +45,7 @@ "battery#forest", "custom/separator#blank_2", "group/audio", + "custom/nightlight", ], // Additional / Edited Waybar Modules // diff --git a/config/waybar/configs/[TOP] Gardenia b/config/waybar/configs/[TOP] Gardenia index 77e86bae..073ff46e 100644 --- a/config/waybar/configs/[TOP] Gardenia +++ b/config/waybar/configs/[TOP] Gardenia @@ -37,7 +37,8 @@ "modules-right": [ "pulseaudio#1", "backlight#2", - "battery" + "battery", + "custom/nightlight" ], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Minimal - Long b/config/waybar/configs/[TOP] Minimal - Long index b57cf3a3..a5be4bd7 100644 --- a/config/waybar/configs/[TOP] Minimal - Long +++ b/config/waybar/configs/[TOP] Minimal - Long @@ -42,6 +42,7 @@ "custom/separator#blank_2", "group/audio", "custom/separator#blank_2", + "custom/nightlight", "custom/power", ], diff --git a/config/waybar/configs/[TOP] Minimal - Short b/config/waybar/configs/[TOP] Minimal - Short index 57abd3d3..7b9a1929 100644 --- a/config/waybar/configs/[TOP] Minimal - Short +++ b/config/waybar/configs/[TOP] Minimal - Short @@ -34,5 +34,6 @@ "backlight", "pulseaudio", "battery", + "custom/nightlight", "custom/power"], } diff --git a/config/waybar/configs/[TOP] Peony b/config/waybar/configs/[TOP] Peony index 2fd1dfe3..a1ef02e8 100644 --- a/config/waybar/configs/[TOP] Peony +++ b/config/waybar/configs/[TOP] Peony @@ -44,6 +44,7 @@ "temperature", "custom/separator#blank", "group/mobo_drawer", - "network"], + "network", + "custom/nightlight"], } \ No newline at end of file diff --git a/config/waybar/configs/[TOP] Sleek b/config/waybar/configs/[TOP] Sleek index fe0f41ba..f591f472 100644 --- a/config/waybar/configs/[TOP] Sleek +++ b/config/waybar/configs/[TOP] Sleek @@ -39,6 +39,7 @@ "pulseaudio", "custom/separator#blank", "group/mobo_drawer", + "custom/nightlight", "custom/power", ], } -- cgit v1.2.3 From 4b2fb9ebeb2abaeb52f24ed4889801b36cbb2ba9 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 14:53:46 -0400 Subject: fix(hyprsunset): support hyprsunset v0.3.x (-i instead of -r), avoid CTM conflicts, run in background, process-based status --- config/hypr/scripts/Hyprsunset.sh | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 68e5fab8..0de19d44 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -42,14 +42,26 @@ icon_on() { cmd_toggle() { ensure_state state="$(cat "$STATE_FILE" || echo off)" + + # Always stop any running hyprsunset first to avoid CTM manager conflicts + if pgrep -x hyprsunset >/dev/null 2>&1; then + pkill -x hyprsunset || true + # give it a moment to release the CTM manager + sleep 0.2 + fi + if [[ "$state" == "on" ]]; then + # Turning OFF: set identity and exit if command -v hyprsunset >/dev/null 2>&1; then - hyprsunset -r || true + nohup hyprsunset -i >/dev/null 2>&1 & + # if hyprsunset persists, stop it shortly after applying identity + sleep 0.3 && pkill -x hyprsunset || true fi echo off > "$STATE_FILE" else + # Turning ON: start hyprsunset at target temp in background if command -v hyprsunset >/dev/null 2>&1; then - hyprsunset -t "$TARGET_TEMP" || true + nohup hyprsunset -t "$TARGET_TEMP" >/dev/null 2>&1 & fi echo on > "$STATE_FILE" fi @@ -57,8 +69,14 @@ cmd_toggle() { cmd_status() { ensure_state - state="$(cat "$STATE_FILE" || echo off)" - if [[ "$state" == "on" ]]; then + # Prefer live process detection; fall back to state file + if pgrep -x hyprsunset >/dev/null 2>&1; then + onoff="on" + else + onoff="$(cat "$STATE_FILE" || echo off)" + fi + + if [[ "$onoff" == "on" ]]; then txt="$(icon_on)" cls="on" tip="Night light on @ ${TARGET_TEMP}K" -- cgit v1.2.3 From d200f9134d5e6bbc558b8acaea64229ba7b420cb Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 15:08:38 -0400 Subject: feat(hyprsunset): add libnotify messages on toggle; use plain glyph text; ensure nightlight button is visible in [TOP] Default Laptop; set escape=false for custom/nightlight --- config/hypr/scripts/Hyprsunset.sh | 15 +++++++++------ config/waybar/ModulesCustom | 3 ++- config/waybar/configs/[TOP] Default Laptop | 1 + 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 0de19d44..0372d720 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -21,20 +21,21 @@ ensure_state() { # Render icons using pango markup to allow colorization icon_off() { - # bright sun when not activated - printf "" + # bright sun when not activated (plain glyph; styling via Waybar CSS by class) + printf "" } icon_on() { case "$ICON_MODE" in sunset) - printf "" + # fallback to same glyph; color can be handled by CSS if desired + printf "" ;; blue) - printf "" + printf "" ;; *) - printf "" + printf "" ;; esac } @@ -50,7 +51,7 @@ cmd_toggle() { sleep 0.2 fi - if [[ "$state" == "on" ]]; then +if [[ "$state" == "on" ]]; then # Turning OFF: set identity and exit if command -v hyprsunset >/dev/null 2>&1; then nohup hyprsunset -i >/dev/null 2>&1 & @@ -58,12 +59,14 @@ cmd_toggle() { sleep 0.3 && pkill -x hyprsunset || true fi echo off > "$STATE_FILE" + notify-send -u low "Hyprsunset: Disabled" || true else # Turning ON: start hyprsunset at target temp in background if command -v hyprsunset >/dev/null 2>&1; then nohup hyprsunset -t "$TARGET_TEMP" >/dev/null 2>&1 & fi echo on > "$STATE_FILE" + notify-send -u low "Hyprsunset: Enabled" "${TARGET_TEMP}K" || true fi } diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index 1f4afa20..512ff751 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -112,7 +112,8 @@ "format": "{}", "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", "tooltip": true, - "tooltip-format": "Night light toggle" + "tooltip-format": "Night light toggle", + "escape": false }, "custom/lock": { diff --git a/config/waybar/configs/[TOP] Default Laptop b/config/waybar/configs/[TOP] Default Laptop index b9722b89..0b264c6b 100644 --- a/config/waybar/configs/[TOP] Default Laptop +++ b/config/waybar/configs/[TOP] Default Laptop @@ -54,6 +54,7 @@ "custom/separator#line", "group/audio", "custom/separator#dot-line", + "custom/nightlight", "group/status", ], } \ No newline at end of file -- cgit v1.2.3 From eeb7d323e179e899bb21ae494b0052f0196362f9 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 15:23:05 -0400 Subject: fix(waybar/nightlight): use universal icons (☀/🌇) and explicit {text} format for JSON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/hypr/scripts/Hyprsunset.sh | 13 +++++++------ config/waybar/ModulesCustom | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 0372d720..1e84bc66 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -21,21 +21,22 @@ ensure_state() { # Render icons using pango markup to allow colorization icon_off() { - # bright sun when not activated (plain glyph; styling via Waybar CSS by class) - printf "" + # universally available sun symbol + printf "☀" } icon_on() { case "$ICON_MODE" in sunset) - # fallback to same glyph; color can be handled by CSS if desired - printf "" + # sunset emoji (falls back to tofu if no emoji font) + printf "🌇" ;; blue) - printf "" + # no color in text; rely on CSS .on to style if desired + printf "☀" ;; *) - printf "" + printf "☀" ;; esac } diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index 512ff751..f4e95433 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -109,7 +109,7 @@ "return-type": "json", "exec": "$HOME/.config/hypr/scripts/Hyprsunset.sh status", "interval": 5, - "format": "{}", + "format": "{text}", "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", "tooltip": true, "tooltip-format": "Night light toggle", -- cgit v1.2.3 From f24d5aed8e4362f42a022a52c16accbcb5b69c66 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 17:02:52 -0400 Subject: feat(nightlight/ui): enlarge icon via Pango markup (escape=false), reserve space (min-length) --- config/hypr/scripts/Hyprsunset.sh | 4 ++-- config/waybar/ModulesCustom | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/config/hypr/scripts/Hyprsunset.sh b/config/hypr/scripts/Hyprsunset.sh index 1e84bc66..c7c4b395 100755 --- a/config/hypr/scripts/Hyprsunset.sh +++ b/config/hypr/scripts/Hyprsunset.sh @@ -81,11 +81,11 @@ cmd_status() { fi if [[ "$onoff" == "on" ]]; then - txt="$(icon_on)" + txt="$(icon_on)" cls="on" tip="Night light on @ ${TARGET_TEMP}K" else - txt="$(icon_off)" + txt="$(icon_off)" cls="off" tip="Night light off" fi diff --git a/config/waybar/ModulesCustom b/config/waybar/ModulesCustom index f4e95433..146ec275 100644 --- a/config/waybar/ModulesCustom +++ b/config/waybar/ModulesCustom @@ -108,8 +108,9 @@ "custom/nightlight": { "return-type": "json", "exec": "$HOME/.config/hypr/scripts/Hyprsunset.sh status", - "interval": 5, + "interval": 3, "format": "{text}", + "min-length": 2, "on-click": "$HOME/.config/hypr/scripts/Hyprsunset.sh toggle", "tooltip": true, "tooltip-format": "Night light toggle", -- cgit v1.2.3 From 2ddd87fd710a75ca51c291c5f4b7fdd40b93d533 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 5 Oct 2025 20:34:06 -0400 Subject: Updated weather.py should be same as development branch On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: modified: config/hypr/UserScripts/Weather.py --- config/hypr/UserScripts/Weather.py | 627 ++++++++++++++++++++++++++++++------- 1 file changed, 512 insertions(+), 115 deletions(-) diff --git a/config/hypr/UserScripts/Weather.py b/config/hypr/UserScripts/Weather.py index a8b70417..ca1d5281 100755 --- a/config/hypr/UserScripts/Weather.py +++ b/config/hypr/UserScripts/Weather.py @@ -1,15 +1,69 @@ #!/usr/bin/env python3 # /* ---- đŸ’Ģ https://github.com/JaKooLit đŸ’Ģ ---- */ # -# original code https://gist.github.com/Surendrajat/ff3876fd2166dd86fb71180f4e9342d7 -# weather using python +# Rewritten to use Open-Meteo APIs (worldwide, no API key) for robust weather data. +# Outputs Waybar-compatible JSON and a simple text cache. -import requests import json import os -from pyquery import PyQuery # install using `pip install pyquery` +import sys +import time +import html +from typing import Any, Dict, List, Optional, Tuple + +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 /home/dwilliams/Projects/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 metric) +UNITS = os.getenv("WEATHER_UNITS", "metric").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 -# weather icons -weather_icons = { +SESSION = requests.Session() +SESSION.headers.update({"User-Agent": UA}) + +# =============== Icon and status mapping =============== +# Reuse prior icon set for continuity +WEATHER_ICONS = { "sunnyDay": "ķ°–™", "clearNight": "ķ°–”", "cloudyFoggyDay": "", @@ -22,126 +76,469 @@ weather_icons = { "default": "", } +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", +} -# Get current location based on IP address -def get_location(): - response = requests.get("https://ipinfo.io") - data = response.json() - loc = data["loc"].split(",") - return float(loc[0]), float(loc[1]) +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"] -# Get latitude and longitude -latitude, longitude = get_location() -# Open-Meteo API endpoint -url = f"https://weather.com/en-PH/weather/today/l/{latitude},{longitude}" +def wmo_to_status(code: int) -> str: + return WMO_STATUS.get(code, "Unknown") -# manual location_id -# NOTE: if you want to add manually, make sure you disable def get_location above -# to get your own location_id, go to https://weather.com & search your location. -# once you choose your location, you can see the location_id in the URL(64 chars long hex string) -# like this: https://weather.com/en-PH/weather/today/l/bca47d1099e762a012b9a139c36f30a0b1e647f69c0c4ac28b537e7ae9c1c200 -# location_id = "bca47d1099e762a012b9a139c36f30a0b1e647f69c0c4ac28b537e7ae9c1c200" # TODO -# NOTE to change to deg F, change the URL to your preffered location after weather.com -# Default is English-Philippines with Busan, South Korea as location_id -# get html page -# url = "https://weather.com/en-PH/weather/today/l/" + location_id +# =============== Utilities =============== -html_data = PyQuery(url=url) +def esc(s: Optional[str]) -> str: + return html.escape(s, quote=False) if s else "" -# current temperature -temp = html_data("span[data-testid='TemperatureValue']").eq(0).text() +def log_debug(msg: str) -> None: + if DEBUG: + print(msg, file=sys.stderr) -# current status phrase -status = html_data("div[data-testid='wxPhrase']").text() -status = f"{status[:16]}.." if len(status) > 17 else status +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) -# status code -# Fix provided by mio-dokuhaki -status_code = html_data("#regionHeader").attr("class").split(" ")[1].split("-")[0] -# status icon -icon = ( - weather_icons[status_code] - if status_code in weather_icons - else weather_icons["default"] -) +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 -# temperature feels like -temp_feel = html_data( - "div[data-testid='FeelsLikeSection'] > span > span[data-testid='TemperatureValue']" -).text() -temp_feel_text = f"Feels like {temp_feel}c" - -# min-max temperature -temp_min = ( - html_data("div[data-testid='wxData'] > span[data-testid='TemperatureValue']") - .eq(1) - .text() -) -temp_max = ( - html_data("div[data-testid='wxData'] > span[data-testid='TemperatureValue']") - .eq(0) - .text() -) -temp_min_max = f"ī‹‹ {temp_min}\t\t {temp_max}" - -# wind speed -wind_speed = str(html_data("span[data-testid='Wind'] > span").text()) -wind_text = f" {wind_speed}" - -# humidity -humidity = html_data("span[data-testid='PercentageValue']").text() -humidity_text = f"îŗ {humidity}" - -# visibility -visibility = html_data("span[data-testid='VisibilityValue']").text() -visibility_text = f"īŽ {visibility}" - -# air quality index -air_quality_index = html_data("text[data-testid='DonutChartValue']").text() - -# hourly rain prediction -prediction = html_data("section[aria-label='Hourly Forecast']")( - "div[data-testid='SegmentPrecipPercentage'] > span" -).text() -prediction = prediction.replace("Chance of Rain", "") -prediction = f"\n\n (hourly) {prediction}" if len(prediction) > 0 else prediction - -# tooltip text -tooltip_text = str.format( - "\t\t{}\t\t\n{}\n{}\n{}\n\n{}\n{}\n{}{}", - f'{temp}', - f" {icon}", - f"{status}", - f"{temp_feel_text}", - f"{temp_min_max}", - f"{wind_text}\t{humidity_text}", - f"{visibility_text}\tAQI {air_quality_index}", - f" {prediction}", -) -# print waybar module data -out_data = { - "text": f"{icon} {temp}", - "alt": status, - "tooltip": tooltip_text, - "class": status_code, -} -print(json.dumps(out_data)) - -simple_weather = ( - f"{icon} {status}\n" - + f" {temp} ({temp_feel_text})\n" - + f"{wind_text} \n" - + f"{humidity_text} \n" - + f"{visibility_text} AQI{air_quality_index}\n" -) +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", + "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" + + 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) + icon = wmo_to_icon(code, is_day) + status = wmo_to_status(code) + + # 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]) + + out_data = { + "text": f"{icon} {temp_str}", + "alt": status, + "tooltip": tooltip_text, + "class": f"wmo-{code} {'day' if is_day else 'night'}", + } + + 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)) + -try: - with open(os.path.expanduser("~/.cache/.weather_cache"), "w") as file: - file.write(simple_weather) -except Exception as e: - print(f"Error writing to cache: {e}") +if __name__ == "__main__": + main() -- cgit v1.2.3 From 93defad110bc12ffb18508ab1fb6f1b26875ea05 Mon Sep 17 00:00:00 2001 From: Donald Williams <129223418+dwilliam62@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:42:26 -0400 Subject: Update WindowRules.conf Fixed windowrule2 syntax error --- config/hypr/UserConfigs/WindowRules.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/hypr/UserConfigs/WindowRules.conf b/config/hypr/UserConfigs/WindowRules.conf index baa6d29a..16dc48a5 100644 --- a/config/hypr/UserConfigs/WindowRules.conf +++ b/config/hypr/UserConfigs/WindowRules.conf @@ -208,7 +208,7 @@ windowrule = noinitialfocus, class:^(jetbrains-*) windowrule = noinitialfocus, title:^(wind.*)$ #This will gonna make the VS Code bluer like other apps -windowrule2 = opacity 0.8,class:^(code)$ +windowrule = opacity 0.8,class:^(code)$ #windowrule = bordercolor rgb(EE4B55) rgb(880808), fullscreen:1 #windowrule = bordercolor rgb(282737) rgb(1E1D2D), floating:1 -- cgit v1.2.3 From 6bb0ce31bb01516a37c4b5b09843a9f0b5dd859d Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 6 Oct 2025 12:47:13 -0400 Subject: Added initial CHANGELOG On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: new file: CHANGELOG.md --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6b40856f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,28 @@ +# CHANGELOG - JAK's Hyprland Dotfiles + + +## Updates and fixes + +## October 6th, 2025 + +- Fixes: + - `windowrule` syntax error corrected + - Wallpaper selector didn't apply wallpaper to SDDM + - Theme colors didn't change when new wallpaper selected + - `Weather.py` fixes: + - Nearly rewritten from scratch + - Now launches properly in waybar + - Better detection of location + - Also variables in script to override + - Changed to OpenMeto for weather info + - With alternative fallbacks as well + - DropDown Terminal fixes: + - Added `TerminalDropDown.sh` to startup + - Now dropdown works first time you invoke it + - Hard coded to kitty vs. `$term` + - More consistent operation vs. `$term` + - Updated `gestures` to accomodate Hyprland 0.5x changes + + + +If you have any questions, feel free to contact via [GitHub Discussions](https://github.com/JaKooLit/Hyprland-Dots/discussions) or [Through Discord Server](https://discord.gg/kool-tech-world) -- cgit v1.2.3 From c77c87cfa2708f0aa60009d7ce279b61827d480d Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 6 Oct 2025 12:53:24 -0400 Subject: Updated CHANGELOG formatinmg On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: modified: CHANGELOG.md --- CHANGELOG.md | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b40856f..990a2f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,28 +1,23 @@ -# CHANGELOG - JAK's Hyprland Dotfiles +# Changelog — JAK's Hyprland Dotfiles +## 2025-10-06 -## Updates and fixes +### 🐛 Fixes +- Correct `windowrule` syntax error. +- Ensure wallpaper selector applies wallpaper to SDDM. +- Update theme colors when a new wallpaper is selected. -## October 6th, 2025 - -- Fixes: - - `windowrule` syntax error corrected - - Wallpaper selector didn't apply wallpaper to SDDM - - Theme colors didn't change when new wallpaper selected - - `Weather.py` fixes: - - Nearly rewritten from scratch - - Now launches properly in waybar - - Better detection of location - - Also variables in script to override - - Changed to OpenMeto for weather info - - With alternative fallbacks as well - - DropDown Terminal fixes: - - Added `TerminalDropDown.sh` to startup - - Now dropdown works first time you invoke it - - Hard coded to kitty vs. `$term` - - More consistent operation vs. `$term` - - Updated `gestures` to accomodate Hyprland 0.5x changes +### đŸŒĻī¸ Weather.py +- â™ģī¸ Substantial refactor. +- ✅ Launches properly in Waybar. +- 📍 Improved location detection; overridable variables exposed. +- 🌐 Switched to Open-Meteo for weather data with fallback providers. +### đŸ–Ĩī¸ Drop-down terminal +- 🔧 Start on login via `TerminalDropDown.sh` so first invocation works. +- 🐱 Use Kitty explicitly instead of `$TERM` for consistent behavior. +### đŸ–ąī¸ Gestures +- 🔧 Updated to accommodate Hyprland 0.5x changes. If you have any questions, feel free to contact via [GitHub Discussions](https://github.com/JaKooLit/Hyprland-Dots/discussions) or [Through Discord Server](https://discord.gg/kool-tech-world) -- cgit v1.2.3 From 51c1e99478f0a44eefb80c678ec8d6318b8078a0 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 6 Oct 2025 13:12:43 -0400 Subject: updating CHANGELOG.md --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 990a2f03..0287f9fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,9 @@ ## 2025-10-06 ### 🐛 Fixes -- Correct `windowrule` syntax error. -- Ensure wallpaper selector applies wallpaper to SDDM. -- Update theme colors when a new wallpaper is selected. +- `windowrule` syntax error. +- wallpaper selector applies wallpaper to SDDM. +- update theme colors when a new wallpaper is selected. ### đŸŒĻī¸ Weather.py - â™ģī¸ Substantial refactor. -- cgit v1.2.3 From f2870284fc88faec277ee8369bb28872524040ef Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 6 Oct 2025 13:34:57 -0400 Subject: Added contributors to CHANGELOG.md --- CHANGELOG.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0287f9fc..85d1559b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,9 @@ ## 2025-10-06 ### 🐛 Fixes -- `windowrule` syntax error. -- wallpaper selector applies wallpaper to SDDM. -- update theme colors when a new wallpaper is selected. +- Correct `windowrule` syntax error. +- Ensure wallpaper selector applies wallpaper to SDDM. +- Update theme colors when a new wallpaper is selected. ### đŸŒĻī¸ Weather.py - â™ģī¸ Substantial refactor. @@ -20,4 +20,11 @@ ### đŸ–ąī¸ Gestures - 🔧 Updated to accommodate Hyprland 0.5x changes. +### đŸ‘Ĩ Contributors + +- [CharlyMH](https://github.com/CharlyMH) +- [ndeekshith](https://github.com/ndeekshith) +- [SherLock707](https://github.com/SherLock707) +- [SVIGHNESH](https://github.com/SVIGHNESH) + If you have any questions, feel free to contact via [GitHub Discussions](https://github.com/JaKooLit/Hyprland-Dots/discussions) or [Through Discord Server](https://discord.gg/kool-tech-world) -- cgit v1.2.3 From ec40222920d2a2ececca10382dedfff6b84ea85f Mon Sep 17 00:00:00 2001 From: Don Williams Date: Thu, 9 Oct 2025 17:04:15 -0400 Subject: Updated scripts to allow debian installs debian has been updated to current version 0.51.1 On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: modified: copy.sh modified: release.sh --- copy.sh | 799 ++++++++++++++++++++++++++++++------------------------------- release.sh | 6 +- 2 files changed, 402 insertions(+), 403 deletions(-) diff --git a/copy.sh b/copy.sh index f6f736dc..77639bf9 100755 --- a/copy.sh +++ b/copy.sh @@ -5,7 +5,7 @@ clear wallpaper=$HOME/.config/hypr/wallpaper_effects/.wallpaper_current waybar_style="$HOME/.config/waybar/style/[Extra] Neon Circuit.css" waybar_config="$HOME/.config/waybar/configs/[TOP] Default" -waybar_config_laptop="$HOME/.config/waybar/configs/[TOP] Default Laptop" +waybar_config_laptop="$HOME/.config/waybar/configs/[TOP] Default Laptop" # Set some colors for output messages OK="$(tput setaf 2)[OK]$(tput sgr0)" @@ -23,28 +23,27 @@ BLUE="$(tput setaf 4)" SKY_BLUE="$(tput setaf 6)" RESET="$(tput sgr0)" - # Check if running as root. If root, script will exit if [[ $EUID -eq 0 ]]; then - echo "${ERROR} This script should ${WARNING}NOT${RESET} be executed as root!! Exiting......." - printf "\n%.0s" {1..2} - exit 1 + echo "${ERROR} This script should ${WARNING}NOT${RESET} be executed as root!! Exiting......." + printf "\n%.0s" {1..2} + exit 1 fi # Function to print colorful text print_color() { - printf "%b%s%b\n" "$1" "$2" "$RESET" + printf "%b%s%b\n" "$1" "$2" "$RESET" } -# Check /etc/os-release to see if this is an Ubuntu or Debian based distro -if grep -iq '^\(ID_LIKE\|ID\)=.*\(debian\|ubuntu\)' /etc/os-release >/dev/null 2>&1; then - printf "\n%.0s" {1..1} - print_color $WARNING " +# Check /etc/os-release to see if this is an Ubuntu based distro +if grep -iq '^\(ID_LIKE\|ID\)=.*ubuntu' /etc/os-release >/dev/null 2>&1; then + printf "\n%.0s" {1..1} + print_color $WARNING " █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ KOOL DOTS version INCOMPATIBLE █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ - Debian / Ubuntu detected. Refer to Hyprland-Dots README + Ubuntu detected. Refer to Hyprland-Dots README For instruction on how to update your KooL Hyprland Dots exiting .... @@ -53,14 +52,13 @@ if grep -iq '^\(ID_LIKE\|ID\)=.*\(debian\|ubuntu\)' /etc/os-release >/dev/null 2 exit 1 fi - -printf "\n%.0s" {1..1} +printf "\n%.0s" {1..1} echo -e "\e[35m â•Ļ╔═┌─┐┌─┐â•Ļ ╔â•Ļ╗┌─┐┌â”Ŧ┐┌─┐ ╠╩╗│ ││ │║ ║║│ │ │ └─┐ 2025 ╩ ╩└─┘└─┘╩═╝ ═╩╝└─┘ ┴ └─┘ \e[0m" -printf "\n%.0s" {1..1} +printf "\n%.0s" {1..1} ####### Announcement echo "${WARNING}A T T E N T I O N !${RESET}" @@ -69,7 +67,7 @@ printf "\n%.0s" {1..1} # Create Directory for Copy Logs if [ ! -d Copy-Logs ]; then - mkdir Copy-Logs + mkdir Copy-Logs fi # Set the name of the log file to include the current date and time @@ -86,8 +84,8 @@ if lspci -k | grep -A 2 -E "(VGA|3D)" | grep -iq nvidia; then sed -i '/env = NVD_BACKEND,direct/s/^#//' config/hypr/UserConfigs/ENVariables.conf sed -i '/env = GSK_RENDERER,ngl/s/^#//' config/hypr/UserConfigs/ENVariables.conf - # no hardware cursors if nvidia detected - sed -i 's/^\([[:space:]]*no_hardware_cursors[[:space:]]*=[[:space:]]*\)2/\1 1/' config/hypr/UserConfigs/UserSettings.conf + # no hardware cursors if nvidia detected + sed -i 's/^\([[:space:]]*no_hardware_cursors[[:space:]]*=[[:space:]]*\)2/\1 1/' config/hypr/UserConfigs/UserSettings.conf #sed -i 's/^\([[:space:]]*explicit_sync[[:space:]]*=[[:space:]]*\)2/\1 0/' config/hypr/UserConfigs/UserSettings.conf fi @@ -110,13 +108,13 @@ fi # activating hyprcursor on env by checking if the directory ~/.icons/Bibata-Modern-Ice/hyprcursors exists if [ -d "$HOME/.icons/Bibata-Modern-Ice/hyprcursors" ]; then - HYPRCURSOR_ENV_FILE="config/hypr/UserConfigs/ENVariables.conf" - echo "${INFO} Bibata-Hyprcursor directory detected. Activating Hyprcursor...." 2>&1 | tee -a "$LOG" || true - sed -i 's/^#env = HYPRCURSOR_THEME,Bibata-Modern-Ice/env = HYPRCURSOR_THEME,Bibata-Modern-Ice/' "$HYPRCURSOR_ENV_FILE" - sed -i 's/^#env = HYPRCURSOR_SIZE,24/env = HYPRCURSOR_SIZE,24/' "$HYPRCURSOR_ENV_FILE" + HYPRCURSOR_ENV_FILE="config/hypr/UserConfigs/ENVariables.conf" + echo "${INFO} Bibata-Hyprcursor directory detected. Activating Hyprcursor...." 2>&1 | tee -a "$LOG" || true + sed -i 's/^#env = HYPRCURSOR_THEME,Bibata-Modern-Ice/env = HYPRCURSOR_THEME,Bibata-Modern-Ice/' "$HYPRCURSOR_ENV_FILE" + sed -i 's/^#env = HYPRCURSOR_SIZE,24/env = HYPRCURSOR_SIZE,24/' "$HYPRCURSOR_ENV_FILE" fi -printf "\n%.0s" {1..1} +printf "\n%.0s" {1..1} # Function to detect keyboard layout using localectl or setxkbmap detect_layout() { @@ -160,15 +158,15 @@ ${MAGENTA} NOTE:${RESET} â€ĸ For example: ${YELLOW}us, kr, gb, ru${RESET} " printf "\n%.0s" {1..1} - + echo -n "${CAT} - Please enter the correct keyboard layout: " read new_layout if [ -n "$new_layout" ]; then - layout="$new_layout" - break + layout="$new_layout" + break else - echo "${CAT} Please enter a keyboard layout." + echo "${CAT} Please enter a keyboard layout." fi done fi @@ -182,15 +180,16 @@ while true; do read keyboard_layout case $keyboard_layout in - [yY]) - awk -v layout="$layout" '/kb_layout/ {$0 = " kb_layout = " layout} 1' config/hypr/UserConfigs/UserSettings.conf > temp.conf - mv temp.conf config/hypr/UserConfigs/UserSettings.conf - - echo "${NOTE} kb_layout ${MAGENTA}$layout${RESET} configured in settings." 2>&1 | tee -a "$LOG" - break ;; - [nN]) - printf "\n%.0s" {1..2} - print_color $WARNING " + [yY]) + awk -v layout="$layout" '/kb_layout/ {$0 = " kb_layout = " layout} 1' config/hypr/UserConfigs/UserSettings.conf >temp.conf + mv temp.conf config/hypr/UserConfigs/UserSettings.conf + + echo "${NOTE} kb_layout ${MAGENTA}$layout${RESET} configured in settings." 2>&1 | tee -a "$LOG" + break + ;; + [nN]) + printf "\n%.0s" {1..2} + print_color $WARNING " █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ STOP AND READ █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ @@ -210,54 +209,56 @@ ${MAGENTA} NOTE:${RESET} â€ĸ You can also set more than 2 keyboard layouts â€ĸ For example: ${YELLOW}us, kr, gb, ru${RESET} " - printf "\n%.0s" {1..1} - - echo -n "${CAT} - Please enter the correct keyboard layout: " - read new_layout - - awk -v new_layout="$new_layout" '/kb_layout/ {$0 = " kb_layout = " new_layout} 1' config/hypr/UserConfigs/UserSettings.conf > temp.conf - mv temp.conf config/hypr/UserConfigs/UserSettings.conf - echo "${OK} kb_layout $new_layout configured in settings." 2>&1 | tee -a "$LOG" - break ;; - *) - echo "${ERROR} Please enter either 'y' or 'n'." ;; + printf "\n%.0s" {1..1} + + echo -n "${CAT} - Please enter the correct keyboard layout: " + read new_layout + + awk -v new_layout="$new_layout" '/kb_layout/ {$0 = " kb_layout = " new_layout} 1' config/hypr/UserConfigs/UserSettings.conf >temp.conf + mv temp.conf config/hypr/UserConfigs/UserSettings.conf + echo "${OK} kb_layout $new_layout configured in settings." 2>&1 | tee -a "$LOG" + break + ;; + *) + echo "${ERROR} Please enter either 'y' or 'n'." + ;; esac done # Check if asusctl is installed and add rog-control-center on Startup if command -v asusctl >/dev/null 2>&1; then - sed -i '/^\s*#exec-once = rog-control-center/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf + sed -i '/^\s*#exec-once = rog-control-center/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf fi # Check if blueman-applet is installed and add blueman-applet on Startup if command -v blueman-applet >/dev/null 2>&1; then - sed -i '/^\s*#exec-once = blueman-applet/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf + sed -i '/^\s*#exec-once = blueman-applet/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf fi # Check if ags is installed edit ags behaviour on configs if command -v ags >/dev/null 2>&1; then - sed -i '/^\s*#exec-once = ags/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf - sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh - sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/Refresh.sh + sed -i '/^\s*#exec-once = ags/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf + sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh + sed -i '/#ags -q && ags &/s/^#//' config/hypr/scripts/Refresh.sh - # Uncomment the ags overview keybind - sed -i '/^#bind = \$mainMod, A, exec, pkill rofi || true && ags -t '\''overview'\''/s/^#//' config/hypr/UserConfigs/UserKeybinds.conf + # Uncomment the ags overview keybind + sed -i '/^#bind = \$mainMod, A, exec, pkill rofi || true && ags -t '\''overview'\''/s/^#//' config/hypr/UserConfigs/UserKeybinds.conf - # Comment the quickshell line if not already commented - sed -i '/^\s*bind\s*=\s*\$mainMod,\s*A,\s*global,\s*quickshell:overviewToggle/{s/^\s*/#/}' config/hypr/UserConfigs/UserKeybinds.conf + # Comment the quickshell line if not already commented + sed -i '/^\s*bind\s*=\s*\$mainMod,\s*A,\s*global,\s*quickshell:overviewToggle/{s/^\s*/#/}' config/hypr/UserConfigs/UserKeybinds.conf fi # Check if quickshell is installed; edit quickshell behaviour on configs if command -v qs >/dev/null 2>&1; then - sed -i '/^\s*#exec-once = qs/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf - sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh - sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/Refresh.sh + sed -i '/^\s*#exec-once = qs/s/^#//' config/hypr/UserConfigs/Startup_Apps.conf + sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/RefreshNoWaybar.sh + sed -i '/#pkill qs && qs &/s/^#//' config/hypr/scripts/Refresh.sh - # Uncomment the quickshell keybind line - sed -i "/^#bind = \$mainMod, A, global, quickshell:overviewToggle/s/^#//" config/hypr/UserConfigs/UserKeybinds.conf + # Uncomment the quickshell keybind line + sed -i "/^#bind = \$mainMod, A, global, quickshell:overviewToggle/s/^#//" config/hypr/UserConfigs/UserKeybinds.conf - # Ensure the ags overview keybind is commented - sed -i "/^\s*bind\s*=\s*\\\$mainMod,\s*A,\s*exec,\s*pkill rofi\s*||\s*true\s*&&\s*ags\s*-t\s*'overview'/{s/^\s*/#/}" config/hypr/UserConfigs/UserKeybinds.conf + # Ensure the ags overview keybind is commented + sed -i "/^\s*bind\s*=\s*\\\$mainMod,\s*A,\s*exec,\s*pkill rofi\s*||\s*true\s*&&\s*ags\s*-t\s*'overview'/{s/^\s*/#/}" config/hypr/UserConfigs/UserKeybinds.conf fi printf "\n%.0s" {1..1} @@ -265,34 +266,34 @@ printf "\n%.0s" {1..1} # Checking if neovim or vim is installed and offer user if they want to make as default editor # Function to modify the ENVariables.conf file update_editor() { - local editor=$1 - sed -i "s/#env = EDITOR,.*/env = EDITOR,$editor #default editor/" config/hypr/UserConfigs/01-UserDefaults.conf - echo "${OK} Default editor set to ${MAGENTA}$editor${RESET}." 2>&1 | tee -a "$LOG" + local editor=$1 + sed -i "s/#env = EDITOR,.*/env = EDITOR,$editor #default editor/" config/hypr/UserConfigs/01-UserDefaults.conf + echo "${OK} Default editor set to ${MAGENTA}$editor${RESET}." 2>&1 | tee -a "$LOG" } EDITOR_SET=0 # Check for neovim if installed -if command -v nvim &> /dev/null; then - printf "${INFO} ${MAGENTA}neovim${RESET} is detected as installed\n" - echo -n "${CAT} Do you want to make ${MAGENTA}neovim${RESET} the default editor? (y/N): " - read EDITOR_CHOICE - if [[ "$EDITOR_CHOICE" == "y" || "$EDITOR_CHOICE" == "Y" ]]; then - update_editor "nvim" - EDITOR_SET=1 - fi +if command -v nvim &>/dev/null; then + printf "${INFO} ${MAGENTA}neovim${RESET} is detected as installed\n" + echo -n "${CAT} Do you want to make ${MAGENTA}neovim${RESET} the default editor? (y/N): " + read EDITOR_CHOICE + if [[ "$EDITOR_CHOICE" == "y" || "$EDITOR_CHOICE" == "Y" ]]; then + update_editor "nvim" + EDITOR_SET=1 + fi fi printf "\n" # Check for vim if installed, but only if neovim wasn't chosen -if [[ "$EDITOR_SET" -eq 0 ]] && command -v vim &> /dev/null; then - printf "${INFO} ${MAGENTA}vim${RESET} is detected as installed\n" - echo -n "${CAT} Do you want to make ${MAGENTA}vim${RESET} the default editor? (y/N): " - read EDITOR_CHOICE - if [[ "$EDITOR_CHOICE" == "y" || "$EDITOR_CHOICE" == "Y" ]]; then - update_editor "vim" - EDITOR_SET=1 - fi +if [[ "$EDITOR_SET" -eq 0 ]] && command -v vim &>/dev/null; then + printf "${INFO} ${MAGENTA}vim${RESET} is detected as installed\n" + echo -n "${CAT} Do you want to make ${MAGENTA}vim${RESET} the default editor? (y/N): " + read EDITOR_CHOICE + if [[ "$EDITOR_CHOICE" == "y" || "$EDITOR_CHOICE" == "Y" ]]; then + update_editor "vim" + EDITOR_SET=1 + fi fi printf "\n" @@ -305,22 +306,22 @@ while true; do echo "${MAGENTA}Select monitor resolution to properly configure appearance and fonts:" echo "$YELLOW -- Enter 1. for monitor resolution less than 1440p (< 1440p)" echo "$YELLOW -- Enter 2. for monitor resolution equal to or higher than 1440p (â‰Ĩ 1440p)" - + echo -n "$CAT Enter the number of your choice (1 or 2): " read res_choice case $res_choice in - 1) - resolution="< 1440p" - break - ;; - 2) - resolution="â‰Ĩ 1440p" - break - ;; - *) - echo "${ERROR} Invalid choice. Please enter 1 for < 1440p or 2 for â‰Ĩ 1440p." - ;; + 1) + resolution="< 1440p" + break + ;; + 2) + resolution="â‰Ĩ 1440p" + break + ;; + *) + echo "${ERROR} Invalid choice. Please enter 1 for < 1440p or 2 for â‰Ĩ 1440p." + ;; esac done @@ -343,8 +344,8 @@ if [ "$resolution" == "< 1440p" ]; then # rofi fonts reduction rofi_config_file="config/rofi/0-shared-fonts.rasi" if [ -f "$rofi_config_file" ]; then - sed -i '/element-text {/,/}/s/[[:space:]]*font: "JetBrainsMono Nerd Font SemiBold 13"/font: "JetBrainsMono Nerd Font SemiBold 11"/' "$rofi_config_file" 2>&1 | tee -a "$LOG" - sed -i '/configuration {/,/}/s/[[:space:]]*font: "JetBrainsMono Nerd Font SemiBold 15"/font: "JetBrainsMono Nerd Font SemiBold 13"/' "$rofi_config_file" 2>&1 | tee -a "$LOG" + sed -i '/element-text {/,/}/s/[[:space:]]*font: "JetBrainsMono Nerd Font SemiBold 13"/font: "JetBrainsMono Nerd Font SemiBold 11"/' "$rofi_config_file" 2>&1 | tee -a "$LOG" + sed -i '/configuration {/,/}/s/[[:space:]]*font: "JetBrainsMono Nerd Font SemiBold 15"/font: "JetBrainsMono Nerd Font SemiBold 13"/' "$rofi_config_file" 2>&1 | tee -a "$LOG" fi fi @@ -352,46 +353,46 @@ printf "\n%.0s" {1..1} # Ask whether to change to 12hr format while true; do - echo -e "${NOTE} ${SKY_BLUE} By default, KooL's Dots are configured in 24H clock format." - echo -n "$CAT Do you want to change to 12H (AM/PM) clock format? (y/n): " - read answer - - # Convert the answer to lowercase for comparison - answer=$(echo "$answer" | tr '[:upper:]' '[:lower:]') - - # Check if the answer is valid - if [[ "$answer" == "y" ]]; then - # Modify waybar clock modules if 12hr is selected - # Clock 1 - sed -i 's#^\(\s*\)//\("format": " {:%I:%M %p}",\) #\1\2 #g' config/waybar/Modules 2>&1 | tee -a "$LOG" - sed -i 's#^\(\s*\)\("format": " {:%H:%M:%S}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - - # Clock 2 - sed -i 's#^\(\s*\)\("format": " {:%H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - - # Clock 3 - sed -i 's#^\(\s*\)//\("format": "{:%I:%M %p - %d/%b}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - sed -i 's#^\(\s*\)\("format": "{:%H:%M - %d/%b}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - - # Clock 4 - sed -i 's#^\(\s*\)//\("format": "{:%B | %a %d, %Y | %I:%M %p}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - sed -i 's#^\(\s*\)\("format": "{:%B | %a %d, %Y | %H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - - # Clock 5 - sed -i 's#^\(\s*\)//\("format": "{:%A, %I:%M %P}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - sed -i 's#^\(\s*\)\("format": "{:%a %d | %H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" - - # for hyprlock - sed -i 's/^\s*text = cmd\[update:1000\] echo "\$(date +"%H")"/# &/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" - sed -i 's/^\(\s*\)# *text = cmd\[update:1000\] echo "\$(date +"%I")" #AM\/PM/\1 text = cmd\[update:1000\] echo "\$(date +"%I")" #AM\/PM/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" - - sed -i 's/^\s*text = cmd\[update:1000\] echo "\$(date +"%S")"/# &/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" - sed -i 's/^\(\s*\)# *text = cmd\[update:1000\] echo "\$(date +"%S %p")" #AM\/PM/\1 text = cmd\[update:1000\] echo "\$(date +"%S %p")" #AM\/PM/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" - - echo "${OK} 12H format set on waybar clocks succesfully." 2>&1 | tee -a "$LOG" - - # Function to apply 12H format to SDDM themes - apply_sddm_12h_format() { + echo -e "${NOTE} ${SKY_BLUE} By default, KooL's Dots are configured in 24H clock format." + echo -n "$CAT Do you want to change to 12H (AM/PM) clock format? (y/n): " + read answer + + # Convert the answer to lowercase for comparison + answer=$(echo "$answer" | tr '[:upper:]' '[:lower:]') + + # Check if the answer is valid + if [[ "$answer" == "y" ]]; then + # Modify waybar clock modules if 12hr is selected + # Clock 1 + sed -i 's#^\(\s*\)//\("format": " {:%I:%M %p}",\) #\1\2 #g' config/waybar/Modules 2>&1 | tee -a "$LOG" + sed -i 's#^\(\s*\)\("format": " {:%H:%M:%S}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + + # Clock 2 + sed -i 's#^\(\s*\)\("format": " {:%H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + + # Clock 3 + sed -i 's#^\(\s*\)//\("format": "{:%I:%M %p - %d/%b}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + sed -i 's#^\(\s*\)\("format": "{:%H:%M - %d/%b}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + + # Clock 4 + sed -i 's#^\(\s*\)//\("format": "{:%B | %a %d, %Y | %I:%M %p}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + sed -i 's#^\(\s*\)\("format": "{:%B | %a %d, %Y | %H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + + # Clock 5 + sed -i 's#^\(\s*\)//\("format": "{:%A, %I:%M %P}",\) #\1\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + sed -i 's#^\(\s*\)\("format": "{:%a %d | %H:%M}",\) #\1//\2#g' config/waybar/Modules 2>&1 | tee -a "$LOG" + + # for hyprlock + sed -i 's/^\s*text = cmd\[update:1000\] echo "\$(date +"%H")"/# &/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" + sed -i 's/^\(\s*\)# *text = cmd\[update:1000\] echo "\$(date +"%I")" #AM\/PM/\1 text = cmd\[update:1000\] echo "\$(date +"%I")" #AM\/PM/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" + + sed -i 's/^\s*text = cmd\[update:1000\] echo "\$(date +"%S")"/# &/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" + sed -i 's/^\(\s*\)# *text = cmd\[update:1000\] echo "\$(date +"%S %p")" #AM\/PM/\1 text = cmd\[update:1000\] echo "\$(date +"%S %p")" #AM\/PM/' config/hypr/hyprlock.conf 2>&1 | tee -a "$LOG" + + echo "${OK} 12H format set on waybar clocks succesfully." 2>&1 | tee -a "$LOG" + + # Function to apply 12H format to SDDM themes + apply_sddm_12h_format() { local sddm_directory=$1 # Check if the directory exists @@ -401,36 +402,36 @@ while true; do sudo sed -i 's|^## HourFormat="hh:mm AP"|HourFormat="hh:mm AP"|' "$sddm_directory/theme.conf" 2>&1 | tee -a "$LOG" || true sudo sed -i 's|^HourFormat="HH:mm"|## HourFormat="HH:mm"|' "$sddm_directory/theme.conf" 2>&1 | tee -a "$LOG" || true fi - } + } - # Applying to different SDDM themes - apply_sddm_12h_format "/usr/share/sddm/themes/simple-sddm" - apply_sddm_12h_format "/usr/share/sddm/themes/simple_sddm_2" + # Applying to different SDDM themes + apply_sddm_12h_format "/usr/share/sddm/themes/simple-sddm" + apply_sddm_12h_format "/usr/share/sddm/themes/simple_sddm_2" - # For SDDM (sequoia_2) - sddm_directory_3="/usr/share/sddm/themes/sequoia_2" - if [ -d "$sddm_directory_3" ]; then - echo "${YELLOW}sddm sequoia_2${RESET} theme exists. Editing to 12H format" 2>&1 | tee -a "$LOG" + # For SDDM (sequoia_2) + sddm_directory_3="/usr/share/sddm/themes/sequoia_2" + if [ -d "$sddm_directory_3" ]; then + echo "${YELLOW}sddm sequoia_2${RESET} theme exists. Editing to 12H format" 2>&1 | tee -a "$LOG" - # Comment out the existing clockFormat="HH:mm" line - sudo sed -i 's|^clockFormat="HH:mm"|## clockFormat="HH:mm"|' "$sddm_directory_3/theme.conf" 2>&1 | tee -a "$LOG" || true + # Comment out the existing clockFormat="HH:mm" line + sudo sed -i 's|^clockFormat="HH:mm"|## clockFormat="HH:mm"|' "$sddm_directory_3/theme.conf" 2>&1 | tee -a "$LOG" || true - # Insert the new clockFormat="hh:mm AP" line if it's not already present - if ! grep -q 'clockFormat="hh:mm AP"' "$sddm_directory_3/theme.conf"; then - sudo sed -i '/^clockFormat=/a clockFormat="hh:mm AP"' "$sddm_directory_3/theme.conf" 2>&1 | tee -a "$LOG" || true - fi - - echo "${OK} 12H format set to SDDM successfully." 2>&1 | tee -a "$LOG" + # Insert the new clockFormat="hh:mm AP" line if it's not already present + if ! grep -q 'clockFormat="hh:mm AP"' "$sddm_directory_3/theme.conf"; then + sudo sed -i '/^clockFormat=/a clockFormat="hh:mm AP"' "$sddm_directory_3/theme.conf" 2>&1 | tee -a "$LOG" || true fi - break - - elif [[ "$answer" == "n" ]]; then - echo "${NOTE} You chose not to change to 12H format." 2>&1 | tee -a "$LOG" - break # Exit the loop if the user chooses "n" - else - echo "${ERROR} Invalid choice. Please enter y for yes or n for no." + echo "${OK} 12H format set to SDDM successfully." 2>&1 | tee -a "$LOG" fi + + break + + elif [[ "$answer" == "n" ]]; then + echo "${NOTE} You chose not to change to 12H format." 2>&1 | tee -a "$LOG" + break # Exit the loop if the user chooses "n" + else + echo "${ERROR} Invalid choice. Please enter y for yes or n for no." + fi done printf "\n%.0s" {1..1} @@ -444,16 +445,16 @@ read border_choice # Check user's choice if [[ "$border_choice" =~ ^[Yy]$ ]]; then - # Disable Rainbow Borders - mv config/hypr/UserScripts/RainbowBorders.sh config/hypr/UserScripts/RainbowBorders.bak.sh - - # Comment out the exec-once and animation lines - sed -i '/exec-once = \$UserScripts\/RainbowBorders.sh/s/^/#/' config/hypr/UserConfigs/Startup_Apps.conf - sed -i '/^[[:space:]]*animation = borderangle, 1, 180, liner, loop/s/^/#/' config/hypr/UserConfigs/UserAnimations.conf - - echo "${OK} Rainbow borders are now disabled." 2>&1 | tee -a "$LOG" + # Disable Rainbow Borders + mv config/hypr/UserScripts/RainbowBorders.sh config/hypr/UserScripts/RainbowBorders.bak.sh + + # Comment out the exec-once and animation lines + sed -i '/exec-once = \$UserScripts\/RainbowBorders.sh/s/^/#/' config/hypr/UserConfigs/Startup_Apps.conf + sed -i '/^[[:space:]]*animation = borderangle, 1, 180, liner, loop/s/^/#/' config/hypr/UserConfigs/UserAnimations.conf + + echo "${OK} Rainbow borders are now disabled." 2>&1 | tee -a "$LOG" else - echo "${NOTE} No changes made. Rainbow borders remain enabled." 2>&1 | tee -a "$LOG" + echo "${NOTE} No changes made. Rainbow borders remain enabled." 2>&1 | tee -a "$LOG" fi printf "\n%.0s" {1..1} @@ -478,7 +479,7 @@ DIRS="fastfetch kitty rofi swaync" for DIR2 in $DIRS; do DIRPATH="$HOME/.config/$DIR2" - + if [ -d "$DIRPATH" ]; then while true; do printf "\n${INFO} Found ${YELLOW}$DIR2${RESET} config found in ~/.config/\n" @@ -486,43 +487,43 @@ for DIR2 in $DIRS; do read DIR1_CHOICE case "$DIR1_CHOICE" in - [Yy]* ) - BACKUP_DIR=$(get_backup_dirname) - # Backup the existing directory - mv "$DIRPATH" "$DIRPATH-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" - echo -e "${NOTE} - Backed up $DIR2 to $DIRPATH-backup-$BACKUP_DIR." 2>&1 | tee -a "$LOG" - - # Copy the new config - cp -r "config/$DIR2" "$HOME/.config/$DIR2" 2>&1 | tee -a "$LOG" - echo -e "${OK} - Replaced $DIR2 with new configuration." 2>&1 | tee -a "$LOG" - - # Restoring rofi themes directory unique themes - if [ "$DIR2" = "rofi" ]; then - if [ -d "$DIRPATH-backup-$BACKUP_DIR/themes" ]; then - for file in "$DIRPATH-backup-$BACKUP_DIR/themes"/*; do - [ -e "$file" ] || continue # Skip if no files are found - echo "Copying $file to $HOME/.config/rofi/themes/" >> "$LOG" - cp -n "$file" "$HOME/.config/rofi/themes/" >> "$LOG" 2>&1 - done || true - fi - - # restoring global 0-shared-fonts.rasi - if [ -f "$DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi" ]; then - echo "Restoring $DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi to $HOME/.config/rofi/" >> "$LOG" - cp "$DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi" "$HOME/.config/rofi/0-shared-fonts.rasi" >> "$LOG" 2>&1 - fi + [Yy]*) + BACKUP_DIR=$(get_backup_dirname) + # Backup the existing directory + mv "$DIRPATH" "$DIRPATH-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" + echo -e "${NOTE} - Backed up $DIR2 to $DIRPATH-backup-$BACKUP_DIR." 2>&1 | tee -a "$LOG" + + # Copy the new config + cp -r "config/$DIR2" "$HOME/.config/$DIR2" 2>&1 | tee -a "$LOG" + echo -e "${OK} - Replaced $DIR2 with new configuration." 2>&1 | tee -a "$LOG" + + # Restoring rofi themes directory unique themes + if [ "$DIR2" = "rofi" ]; then + if [ -d "$DIRPATH-backup-$BACKUP_DIR/themes" ]; then + for file in "$DIRPATH-backup-$BACKUP_DIR/themes"/*; do + [ -e "$file" ] || continue # Skip if no files are found + echo "Copying $file to $HOME/.config/rofi/themes/" >>"$LOG" + cp -n "$file" "$HOME/.config/rofi/themes/" >>"$LOG" 2>&1 + done || true + fi + # restoring global 0-shared-fonts.rasi + if [ -f "$DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi" ]; then + echo "Restoring $DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi to $HOME/.config/rofi/" >>"$LOG" + cp "$DIRPATH-backup-$BACKUP_DIR/0-shared-fonts.rasi" "$HOME/.config/rofi/0-shared-fonts.rasi" >>"$LOG" 2>&1 fi - break - ;; - [Nn]* ) - echo -e "${NOTE} - Skipping ${YELLOW}$DIR2${RESET}" 2>&1 | tee -a "$LOG" - break - ;; - * ) - echo -e "${WARN} - Invalid choice. Please enter Y or N." - ;; + fi + + break + ;; + [Nn]*) + echo -e "${NOTE} - Skipping ${YELLOW}$DIR2${RESET}" 2>&1 | tee -a "$LOG" + break + ;; + *) + echo -e "${WARN} - Invalid choice. Please enter Y or N." + ;; esac done else @@ -538,103 +539,103 @@ printf "\n%.0s" {1..1} DIRW="waybar" DIRPATHw="$HOME/.config/$DIRW" if [ -d "$DIRPATHw" ]; then - while true; do - echo -n "${CAT} Do you want to replace ${YELLOW}$DIRW${RESET} config? (y/n): " - read DIR1_CHOICE - - case "$DIR1_CHOICE" in - [Yy]* ) - BACKUP_DIR=$(get_backup_dirname) - cp -r "$DIRPATHw" "$DIRPATHw-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" - echo -e "${NOTE} - Backed up $DIRW to $DIRPATHw-backup-$BACKUP_DIR." 2>&1 | tee -a "$LOG" - - # Remove the old $DIRPATHw and copy the new one - rm -rf "$DIRPATHw" && cp -r "config/$DIRW" "$DIRPATHw" 2>&1 | tee -a "$LOG" - - # Step 1: Handle waybar symlinks - for file in "config" "style.css"; do - symlink="$DIRPATHw-backup-$BACKUP_DIR/$file" - target_file="$DIRPATHw/$file" - - if [ -L "$symlink" ]; then - symlink_target=$(readlink "$symlink") - if [ -f "$symlink_target" ]; then - rm -f "$target_file" && cp -f "$symlink_target" "$target_file" - echo -e "${NOTE} - Copied $file as a regular file." - else - echo -e "${WARN} - Symlink target for $file does not exist." - fi - fi - done - - # Step 2: Copy non-existing directories and files under waybar/configs - for dir in "$DIRPATHw-backup-$BACKUP_DIR/configs"/*; do - [ -e "$dir" ] || continue # Skip if no files are found - if [ -d "$dir" ]; then - target_dir="$HOME/.config/waybar/configs/$(basename "$dir")" - if [ ! -d "$target_dir" ]; then - echo "Copying directory $dir to $HOME/.config/waybar/configs/" >> "$LOG" - cp -r "$dir" "$HOME/.config/waybar/configs/" - else - echo "Directory $target_dir already exists. Skipping." >> "$LOG" - fi - fi - done - - for file in "$DIRPATHw-backup-$BACKUP_DIR/configs"/*; do - [ -e "$file" ] || continue - target_file="$HOME/.config/waybar/configs/$(basename "$file")" - if [ ! -e "$target_file" ]; then - echo "Copying $file to $HOME/.config/waybar/configs/" >> "$LOG" - cp "$file" "$HOME/.config/waybar/configs/" - else - echo "File $target_file already exists. Skipping." >> "$LOG" - fi - done || true - - # Step 3: Copy unique files in waybar/style - for file in "$DIRPATHw-backup-$BACKUP_DIR/style"/*; do - [ -e "$file" ] || continue - - if [ -d "$file" ]; then - target_dir="$HOME/.config/waybar/style/$(basename "$file")" - if [ ! -d "$target_dir" ]; then - echo "Copying directory $file to $HOME/.config/waybar/style/" >> "$LOG" - cp -r "$file" "$HOME/.config/waybar/style/" - else - echo "Directory $target_dir already exists. Skipping." >> "$LOG" - fi - else - target_file="$HOME/.config/waybar/style/$(basename "$file")" - if [ ! -e "$target_file" ]; then - echo "Copying file $file to $HOME/.config/waybar/style/" >> "$LOG" - cp "$file" "$HOME/.config/waybar/style/" - else - echo "File $target_file already exists. Skipping." >> "$LOG" - fi - fi - done || true - - # Step 4: restore Modules_Extras - BACKUP_FILEw="$DIRPATHw-backup-$BACKUP_DIR/UserModules" - if [ -f "$BACKUP_FILEw" ]; then - cp -f "$BACKUP_FILEw" "$DIRPATHw/UserModules" - fi - - break - ;; - [Nn]* ) - echo -e "${NOTE} - Skipping ${YELLOW}$DIRW${RESET} config replacement." 2>&1 | tee -a "$LOG" - break - ;; - * ) - echo -e "${WARN} - Invalid choice. Please enter Y or N." - ;; - esac - done + while true; do + echo -n "${CAT} Do you want to replace ${YELLOW}$DIRW${RESET} config? (y/n): " + read DIR1_CHOICE + + case "$DIR1_CHOICE" in + [Yy]*) + BACKUP_DIR=$(get_backup_dirname) + cp -r "$DIRPATHw" "$DIRPATHw-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" + echo -e "${NOTE} - Backed up $DIRW to $DIRPATHw-backup-$BACKUP_DIR." 2>&1 | tee -a "$LOG" + + # Remove the old $DIRPATHw and copy the new one + rm -rf "$DIRPATHw" && cp -r "config/$DIRW" "$DIRPATHw" 2>&1 | tee -a "$LOG" + + # Step 1: Handle waybar symlinks + for file in "config" "style.css"; do + symlink="$DIRPATHw-backup-$BACKUP_DIR/$file" + target_file="$DIRPATHw/$file" + + if [ -L "$symlink" ]; then + symlink_target=$(readlink "$symlink") + if [ -f "$symlink_target" ]; then + rm -f "$target_file" && cp -f "$symlink_target" "$target_file" + echo -e "${NOTE} - Copied $file as a regular file." + else + echo -e "${WARN} - Symlink target for $file does not exist." + fi + fi + done + + # Step 2: Copy non-existing directories and files under waybar/configs + for dir in "$DIRPATHw-backup-$BACKUP_DIR/configs"/*; do + [ -e "$dir" ] || continue # Skip if no files are found + if [ -d "$dir" ]; then + target_dir="$HOME/.config/waybar/configs/$(basename "$dir")" + if [ ! -d "$target_dir" ]; then + echo "Copying directory $dir to $HOME/.config/waybar/configs/" >>"$LOG" + cp -r "$dir" "$HOME/.config/waybar/configs/" + else + echo "Directory $target_dir already exists. Skipping." >>"$LOG" + fi + fi + done + + for file in "$DIRPATHw-backup-$BACKUP_DIR/configs"/*; do + [ -e "$file" ] || continue + target_file="$HOME/.config/waybar/configs/$(basename "$file")" + if [ ! -e "$target_file" ]; then + echo "Copying $file to $HOME/.config/waybar/configs/" >>"$LOG" + cp "$file" "$HOME/.config/waybar/configs/" + else + echo "File $target_file already exists. Skipping." >>"$LOG" + fi + done || true + + # Step 3: Copy unique files in waybar/style + for file in "$DIRPATHw-backup-$BACKUP_DIR/style"/*; do + [ -e "$file" ] || continue + + if [ -d "$file" ]; then + target_dir="$HOME/.config/waybar/style/$(basename "$file")" + if [ ! -d "$target_dir" ]; then + echo "Copying directory $file to $HOME/.config/waybar/style/" >>"$LOG" + cp -r "$file" "$HOME/.config/waybar/style/" + else + echo "Directory $target_dir already exists. Skipping." >>"$LOG" + fi + else + target_file="$HOME/.config/waybar/style/$(basename "$file")" + if [ ! -e "$target_file" ]; then + echo "Copying file $file to $HOME/.config/waybar/style/" >>"$LOG" + cp "$file" "$HOME/.config/waybar/style/" + else + echo "File $target_file already exists. Skipping." >>"$LOG" + fi + fi + done || true + + # Step 4: restore Modules_Extras + BACKUP_FILEw="$DIRPATHw-backup-$BACKUP_DIR/UserModules" + if [ -f "$BACKUP_FILEw" ]; then + cp -f "$BACKUP_FILEw" "$DIRPATHw/UserModules" + fi + + break + ;; + [Nn]*) + echo -e "${NOTE} - Skipping ${YELLOW}$DIRW${RESET} config replacement." 2>&1 | tee -a "$LOG" + break + ;; + *) + echo -e "${WARN} - Invalid choice. Please enter Y or N." + ;; + esac + done else - cp -r "config/$DIRW" "$DIRPATHw" 2>&1 | tee -a "$LOG" - echo -e "${OK} - Copy completed for ${YELLOW}$DIRW${RESET}" 2>&1 | tee -a "$LOG" + cp -r "config/$DIRW" "$DIRPATHw" 2>&1 | tee -a "$LOG" + echo -e "${OK} - Copy completed for ${YELLOW}$DIRW${RESET}" 2>&1 | tee -a "$LOG" fi printf "\n%.0s" {1..1} @@ -651,12 +652,12 @@ DIR="btop cava hypr Kvantum qt5ct qt6ct swappy wallust wlogout" for DIR_NAME in $DIR; do DIRPATH="$HOME/.config/$DIR_NAME" - + # Backup the existing directory if it exists if [ -d "$DIRPATH" ]; then echo -e "\n${NOTE} - Config for ${YELLOW}$DIR_NAME${RESET} found, attempting to back up." BACKUP_DIR=$(get_backup_dirname) - + # Backup the existing directory mv "$DIRPATH" "$DIRPATH-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" if [ $? -eq 0 ]; then @@ -666,7 +667,7 @@ for DIR_NAME in $DIR; do exit 1 fi fi - + # Copy the new config if [ -d "config/$DIR_NAME" ]; then cp -r "config/$DIR_NAME/" "$HOME/.config/$DIR_NAME" 2>&1 | tee -a "$LOG" @@ -698,21 +699,21 @@ if command -v ags >/dev/null 2>&1; then else read -p "${CAT} Do you want to overwrite your existing ${YELLOW}ags${RESET} config? [y/N] " answer_ags case "$answer_ags" in - [Yy]* ) - BACKUP_DIR=$(get_backup_dirname) - mv "$DIRPATH_AGS" "$DIRPATH_AGS-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" - echo -e "${NOTE} - Backed up ags config to $DIRPATH_AGS-backup-$BACKUP_DIR" - - if cp -r "config/ags/" "$DIRPATH_AGS" 2>&1 | tee -a "$LOG"; then - echo "${OK} - ${YELLOW}ags configs${RESET} overwritten successfully." - else - echo "${ERROR} - Failed to copy ${YELLOW}ags${RESET} config." - exit 1 - fi - ;; - * ) - echo "${NOTE} - Skipping overwrite of ags config." - ;; + [Yy]*) + BACKUP_DIR=$(get_backup_dirname) + mv "$DIRPATH_AGS" "$DIRPATH_AGS-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" + echo -e "${NOTE} - Backed up ags config to $DIRPATH_AGS-backup-$BACKUP_DIR" + + if cp -r "config/ags/" "$DIRPATH_AGS" 2>&1 | tee -a "$LOG"; then + echo "${OK} - ${YELLOW}ags configs${RESET} overwritten successfully." + else + echo "${ERROR} - Failed to copy ${YELLOW}ags${RESET} config." + exit 1 + fi + ;; + *) + echo "${NOTE} - Skipping overwrite of ags config." + ;; esac fi fi @@ -734,28 +735,27 @@ if command -v qs >/dev/null 2>&1; then else read -p "${CAT} Do you want to overwrite your existing ${YELLOW}quickshell${RESET} config? [y/N] " answer_qs case "$answer_qs" in - [Yy]* ) - BACKUP_DIR=$(get_backup_dirname) - mv "$DIRPATH_QS" "$DIRPATH_QS-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" - echo -e "${NOTE} - Backed up quickshell to $DIRPATH_QS-backup-$BACKUP_DIR" - - cp -r "config/quickshell/" "$DIRPATH_QS" 2>&1 | tee -a "$LOG" - if [ $? -eq 0 ]; then - echo "${OK} - ${YELLOW}quickshell${RESET} overwritten successfully." - else - echo "${ERROR} - Failed to copy ${YELLOW}quickshell${RESET} config." - exit 1 - fi - ;; - * ) - echo "${NOTE} - Skipping overwrite of quickshell config." - ;; + [Yy]*) + BACKUP_DIR=$(get_backup_dirname) + mv "$DIRPATH_QS" "$DIRPATH_QS-backup-$BACKUP_DIR" 2>&1 | tee -a "$LOG" + echo -e "${NOTE} - Backed up quickshell to $DIRPATH_QS-backup-$BACKUP_DIR" + + cp -r "config/quickshell/" "$DIRPATH_QS" 2>&1 | tee -a "$LOG" + if [ $? -eq 0 ]; then + echo "${OK} - ${YELLOW}quickshell${RESET} overwritten successfully." + else + echo "${ERROR} - Failed to copy ${YELLOW}quickshell${RESET} config." + exit 1 + fi + ;; + *) + echo "${NOTE} - Skipping overwrite of quickshell config." + ;; esac fi fi printf "\n%.0s" {1..1} - # Restore automatically Animations and Monitor-Profiles # including monitors.conf and workspaces.conf HYPR_DIR="$HOME/.config/hypr" @@ -764,14 +764,14 @@ BACKUP_HYPR_PATH="$HYPR_DIR-backup-$BACKUP_DIR" if [ -d "$BACKUP_HYPR_PATH" ]; then echo -e "\n${NOTE} Restoring ${SKY_BLUE}Animations & Monitor Profiles${RESET} directories into ${YELLOW}$HYPR_DIR${RESET}..." - + DIR_B=("Monitor_Profiles" "animations" "wallpaper_effects") - # Restore directories automatically + # Restore directories automatically for DIR_RESTORE in "${DIR_B[@]}"; do BACKUP_SUBDIR="$BACKUP_HYPR_PATH/$DIR_RESTORE" - + if [ -d "$BACKUP_SUBDIR" ]; then - cp -r "$BACKUP_SUBDIR" "$HYPR_DIR/" + cp -r "$BACKUP_SUBDIR" "$HYPR_DIR/" echo "${OK} - Restored directory: ${MAGENTA}$DIR_RESTORE${RESET}" 2>&1 | tee -a "$LOG" fi done @@ -782,7 +782,7 @@ if [ -d "$BACKUP_HYPR_PATH" ]; then BACKUP_FILE="$BACKUP_HYPR_PATH/$FILE_RESTORE" if [ -f "$BACKUP_FILE" ]; then - cp "$BACKUP_FILE" "$HYPR_DIR/$FILE_RESTORE" + cp "$BACKUP_FILE" "$HYPR_DIR/$FILE_RESTORE" echo "${OK} - Restored file: ${MAGENTA}$FILE_RESTORE${RESET}" 2>&1 | tee -a "$LOG" fi done @@ -815,8 +815,8 @@ if [ -z "$BACKUP_DIR" ]; then fi if [ -d "$BACKUP_DIR_PATH" ]; then - echo -e "${NOTE} Restoring previous ${MAGENTA}User-Configs${RESET}... " - print_color $WARNING " + echo -e "${NOTE} Restoring previous ${MAGENTA}User-Configs${RESET}... " + print_color $WARNING " █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ NOTES for RESTORING PREVIOUS CONFIGS █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ @@ -824,8 +824,7 @@ if [ -d "$BACKUP_DIR_PATH" ]; then If you decide to restore your old configs, make sure to handle the updates or changes manually !!! " - echo -e "${MAGENTA}Kindly Visit and check KooL's Hyprland-Dots GitHub page for the history of commits.${RESET}" - + echo -e "${MAGENTA}Kindly Visit and check KooL's Hyprland-Dots GitHub page for the history of commits.${RESET}" for FILE_NAME in "${FILES_TO_RESTORE[@]}"; do BACKUP_FILE="$BACKUP_DIR_PATH/$FILE_NAME" @@ -870,7 +869,7 @@ if [ -d "$BACKUP_DIR_PATH_S" ]; then printf "\n${INFO} Found ${YELLOW}$SCRIPT_NAME${RESET} in hypr backup...\n" echo -n "${CAT} Do you want to restore ${YELLOW}$SCRIPT_NAME${RESET} from backup? (y/N): " read script_restore - + if [[ "$script_restore" == [Yy]* ]]; then if cp "$BACKUP_SCRIPT" "$DIRSHPATH/UserScripts/$SCRIPT_NAME"; then echo "${OK} - $SCRIPT_NAME restored!" 2>&1 | tee -a "$LOG" @@ -902,7 +901,7 @@ if [ -d "$BACKUP_DIR_PATH_F" ]; then for FILE_RESTORE in "${FILES_2_RESTORE[@]}"; do BACKUP_FILE="$BACKUP_DIR_PATH_F/$FILE_RESTORE" - + if [ -f "$BACKUP_FILE" ]; then echo -e "\n${INFO} Found ${YELLOW}$FILE_RESTORE${RESET} in hypr backup..." echo -n "${CAT} Do you want to restore ${YELLOW}$FILE_RESTORE${RESET} from backup? (y/N): " @@ -933,7 +932,7 @@ if [ ! -d "$rofi_DIR" ]; then fi if [ -d "$HOME/.config/rofi/themes" ]; then if [ -z "$(ls -A $HOME/.config/rofi/themes)" ]; then - echo '/* Dummy Rofi theme */' > "$HOME/.config/rofi/themes/dummy.rasi" + echo '/* Dummy Rofi theme */' >"$HOME/.config/rofi/themes/dummy.rasi" fi ln -snf "$HOME/.config/rofi/themes/"* "$HOME/.local/share/rofi/themes/" # Delete the dummy file if it was created @@ -951,7 +950,7 @@ if cp -r wallpapers $HOME/Pictures/; then else echo "${ERROR} Failed to copy some ${YELLOW}wallpapers${RESET}" | tee -a "$LOG" fi - + # Set some files as executable chmod +x "$HOME/.config/hypr/scripts/"* 2>&1 | tee -a "$LOG" chmod +x "$HOME/.config/hypr/UserScripts/"* 2>&1 | tee -a "$LOG" @@ -960,26 +959,25 @@ chmod +x "$HOME/.config/hypr/initial-boot.sh" 2>&1 | tee -a "$LOG" # Waybar config to symlink & retain based on machine type if hostnamectl | grep -q 'Chassis: desktop'; then - config_file="$waybar_config" - config_remove=" Laptop" + config_file="$waybar_config" + config_remove=" Laptop" else - config_file="$waybar_config_laptop" - config_remove="" + config_file="$waybar_config_laptop" + config_remove="" fi # Check if ~/.config/waybar/config does not exist or is a symlink if [ ! -e "$HOME/.config/waybar/config" ] || [ -L "$HOME/.config/waybar/config" ]; then - ln -sf "$config_file" "$HOME/.config/waybar/config" 2>&1 | tee -a "$LOG" + ln -sf "$config_file" "$HOME/.config/waybar/config" 2>&1 | tee -a "$LOG" fi - # Remove inappropriate waybar configs rm -rf "$HOME/.config/waybar/configs/[TOP] Default$config_remove" \ - "$HOME/.config/waybar/configs/[BOT] Default$config_remove" \ - "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v1)" \ - "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v2)" \ - "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v3)" \ - "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v4)" 2>&1 | tee -a "$LOG" || true + "$HOME/.config/waybar/configs/[BOT] Default$config_remove" \ + "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v1)" \ + "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v2)" \ + "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v3)" \ + "$HOME/.config/waybar/configs/[TOP] Default$config_remove (old v4)" 2>&1 | tee -a "$LOG" || true printf "\n%.0s" {1..1} @@ -989,24 +987,24 @@ if [ -d "$sddm_simple_sddm_2" ]; then while true; do echo -n "${CAT} SDDM simple_sddm_2 theme detected! Apply current wallpaper as SDDM background? (y/n): " read SDDM_WALL - + # Remove any leading/trailing whitespace or newlines from input SDDM_WALL=$(echo "$SDDM_WALL" | tr -d '\n' | tr -d ' ') case $SDDM_WALL in - [Yy]) - # Copy the wallpaper, ignore errors if the file exists or fails - sudo cp -r "config/hypr/wallpaper_effects/.wallpaper_current" "/usr/share/sddm/themes/simple_sddm_2/Backgrounds/default" || true - echo "${NOTE} Current wallpaper applied as default SDDM background" 2>&1 | tee -a "$LOG" - break - ;; - [Nn]) - echo "${NOTE} You chose not to apply the current wallpaper to SDDM." 2>&1 | tee -a "$LOG" - break - ;; - *) - echo "Please enter 'y' or 'n' to proceed." - ;; + [Yy]) + # Copy the wallpaper, ignore errors if the file exists or fails + sudo cp -r "config/hypr/wallpaper_effects/.wallpaper_current" "/usr/share/sddm/themes/simple_sddm_2/Backgrounds/default" || true + echo "${NOTE} Current wallpaper applied as default SDDM background" 2>&1 | tee -a "$LOG" + break + ;; + [Nn]) + echo "${NOTE} You chose not to apply the current wallpaper to SDDM." 2>&1 | tee -a "$LOG" + break + ;; + *) + echo "Please enter 'y' or 'n' to proceed." + ;; esac done fi @@ -1018,37 +1016,37 @@ echo "${MAGENTA}By default only a few wallpapers are copied${RESET}..." while true; do echo -n "${CAT} Would you like to download additional wallpapers? ${WARN} This is 1GB in size (y/n): " read WALL - + case $WALL in - [Yy]) - echo "${NOTE} Downloading additional wallpapers..." - if git clone "https://github.com/JaKooLit/Wallpaper-Bank.git"; then - echo "${OK} Wallpapers downloaded successfully." 2>&1 | tee -a "$LOG" - - # Check if wallpapers directory exists and create it if not - if [ ! -d "$HOME/Pictures/wallpapers" ]; then - mkdir -p "$HOME/Pictures/wallpapers" - echo "${OK} Created wallpapers directory." 2>&1 | tee -a "$LOG" - fi + [Yy]) + echo "${NOTE} Downloading additional wallpapers..." + if git clone "https://github.com/JaKooLit/Wallpaper-Bank.git"; then + echo "${OK} Wallpapers downloaded successfully." 2>&1 | tee -a "$LOG" + + # Check if wallpapers directory exists and create it if not + if [ ! -d "$HOME/Pictures/wallpapers" ]; then + mkdir -p "$HOME/Pictures/wallpapers" + echo "${OK} Created wallpapers directory." 2>&1 | tee -a "$LOG" + fi - if cp -R Wallpaper-Bank/wallpapers/* "$HOME/Pictures/wallpapers/" >> "$LOG" 2>&1; then - echo "${OK} Wallpapers copied successfully." 2>&1 | tee -a "$LOG" - rm -rf Wallpaper-Bank 2>&1 # Remove cloned repository after copying wallpapers - break - else - echo "${ERROR} Copying wallpapers failed" 2>&1 | tee -a "$LOG" - fi + if cp -R Wallpaper-Bank/wallpapers/* "$HOME/Pictures/wallpapers/" >>"$LOG" 2>&1; then + echo "${OK} Wallpapers copied successfully." 2>&1 | tee -a "$LOG" + rm -rf Wallpaper-Bank 2>&1 # Remove cloned repository after copying wallpapers + break else - echo "${ERROR} Downloading additional wallpapers failed" 2>&1 | tee -a "$LOG" + echo "${ERROR} Copying wallpapers failed" 2>&1 | tee -a "$LOG" fi - ;; + else + echo "${ERROR} Downloading additional wallpapers failed" 2>&1 | tee -a "$LOG" + fi + ;; [Nn]) - echo "${NOTE} You chose not to download additional wallpapers." 2>&1 | tee -a "$LOG" - break - ;; + echo "${NOTE} You chose not to download additional wallpapers." 2>&1 | tee -a "$LOG" + break + ;; *) - echo "Please enter 'y' or 'n' to proceed." - ;; + echo "Please enter 'y' or 'n' to proceed." + ;; esac done @@ -1068,10 +1066,10 @@ cleanup_backups() { BACKUP_DIRS+=("$BACKUP") fi done - + # If more than one backup found if [ ${#BACKUP_DIRS[@]} -gt 1 ]; then - printf "\n%.0s" {1..2} + printf "\n%.0s" {1..2} echo -e "${INFO} Found ${MAGENTA}multiple backups${RESET} for: ${YELLOW}${DIR##*/}${RESET}" echo "${YELLOW}Backups: ${RESET}" @@ -1109,7 +1107,7 @@ cleanup_backups # Check if ~/.config/waybar/style.css does not exist or is a symlink if [ ! -e "$HOME/.config/waybar/style.css" ] || [ -L "$HOME/.config/waybar/style.css" ]; then - ln -sf "$waybar_style" "$HOME/.config/waybar/style.css" 2>&1 | tee -a "$LOG" + ln -sf "$waybar_style" "$HOME/.config/waybar/style.css" 2>&1 | tee -a "$LOG" fi printf "\n%.0s" {1..1} @@ -1123,4 +1121,5 @@ printf "\n%.0s" {1..1} printf "${INFO} However, it is ${MAGENTA}HIGHLY SUGGESTED${RESET} to logout and re-login or better reboot to avoid any issues" printf "\n%.0s" {1..1} printf "${SKY_BLUE}Thank you${RESET} for using ${MAGENTA}KooL's Hyprland Configuration${RESET}... ${YELLOW}ENJOY!!!${RESET}" -printf "\n%.0s" {1..3} \ No newline at end of file +printf "\n%.0s" {1..3} + diff --git a/release.sh b/release.sh index b3e77e9c..763e4bbe 100755 --- a/release.sh +++ b/release.sh @@ -18,15 +18,15 @@ BLUE="$(tput setaf 4)" SKY_BLUE="$(tput setaf 6)" RESET="$(tput sgr0)" -# Check /etc/os-release to see if this is an Ubuntu or Debian based distro -if grep -iq '^\(ID_LIKE\|ID\)=.*\(debian\|ubuntu\)' /etc/os-release >/dev/null 2>&1; then +# Check /etc/os-release to see if this is an Ubuntu based distro +if grep -iq '^\(ID_LIKE\|ID\)=.*ubuntu' /etc/os-release >/dev/null 2>&1; then printf "\n%.0s" {1..1} print_color $WARNING " █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ KOOL DOTS version INCOMPATIBLE █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ - Debian / Ubuntu detected. Refer to Hyprland-Dots README + Ubuntu detected. Refer to Hyprland-Dots README For instruction on how to update your KooL Hyprland Dots exiting .... -- cgit v1.2.3 From 40f124f2321a6af20bf4b7e2788195bebb1c0421 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 12 Oct 2025 06:30:14 -0400 Subject: Added Groups & keybinds from Matt @ The Linux cast On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: modified: config/hypr/UserConfigs/UserKeybinds.conf modified: config/hypr/UserConfigs/UserSettings.conf --- config/hypr/UserConfigs/UserKeybinds.conf | 11 +++++++++++ config/hypr/UserConfigs/UserSettings.conf | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index d8a1d52e..2f047093 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -78,6 +78,17 @@ bind = $mainMod CTRL, F10, movecurrentworkspacetomonitor, r #move current worksp bind = $mainMod CTRL, F11, movecurrentworkspacetomonitor, u #move current workspace to UP monitor bind = $mainMod CTRL, F12, movecurrentworkspacetomonitor, d #move current workspace to DOWN monitor +# Groups +bind = $mainMod, U, togglegroup, # Toggle group +bind = ALT, H, changegroupactive, b # Change group active +bind = ALT, L, changegroupactive, f # Change group active +bind = $mainMod, I, lockactivegroup, toggle # Lock active group +bind = $mainMod ALT, U, moveoutofgroup, # Move out of group +bind = $mainMod ALT, H, moveintogroup, l # Move into group left +bind = $mainMod ALT, L, moveintogroup, r # Move into group right +bind = $mainMod ALT, K, moveintogroup, u # Moving into group up +bind = $mainMod ALT, J, moveintogroup, d # Moving into group down + # For passthrough keyboard into a VM # bind = $mainMod ALT, P, submap, passthru #submap = passthru diff --git a/config/hypr/UserConfigs/UserSettings.conf b/config/hypr/UserConfigs/UserSettings.conf index 325d24f7..b9714a44 100644 --- a/config/hypr/UserConfigs/UserSettings.conf +++ b/config/hypr/UserConfigs/UserSettings.conf @@ -63,6 +63,29 @@ input { } } +group { + col.border_active = $color2 $color4 45deg + col.border_inactive = $color10 + + groupbar { + enabled = true + font_family = JetBrainsMono Nerd Font + font_size = 12 + gradients = true + height = 18 + priority = 3 + render_titles = true + scrolling = true + text_color = $foreground + + col.active = $color3 + col.inactive = $background + col.locked_active = $color6 + col.locked_inactive = $color8 + } +} + + gestures { gesture = 3, horizontal, workspace workspace_swipe_distance = 500 -- cgit v1.2.3 From 4d2ef003eabfd31591a0a04b17be7e8e5c869c0e Mon Sep 17 00:00:00 2001 From: Don Williams Date: Sun, 12 Oct 2025 06:50:14 -0400 Subject: Removed group feature --- config/hypr/UserConfigs/UserKeybinds.conf | 10 ---------- config/hypr/UserConfigs/UserSettings.conf | 22 ---------------------- 2 files changed, 32 deletions(-) diff --git a/config/hypr/UserConfigs/UserKeybinds.conf b/config/hypr/UserConfigs/UserKeybinds.conf index 2f047093..4d35a9c9 100644 --- a/config/hypr/UserConfigs/UserKeybinds.conf +++ b/config/hypr/UserConfigs/UserKeybinds.conf @@ -78,16 +78,6 @@ bind = $mainMod CTRL, F10, movecurrentworkspacetomonitor, r #move current worksp bind = $mainMod CTRL, F11, movecurrentworkspacetomonitor, u #move current workspace to UP monitor bind = $mainMod CTRL, F12, movecurrentworkspacetomonitor, d #move current workspace to DOWN monitor -# Groups -bind = $mainMod, U, togglegroup, # Toggle group -bind = ALT, H, changegroupactive, b # Change group active -bind = ALT, L, changegroupactive, f # Change group active -bind = $mainMod, I, lockactivegroup, toggle # Lock active group -bind = $mainMod ALT, U, moveoutofgroup, # Move out of group -bind = $mainMod ALT, H, moveintogroup, l # Move into group left -bind = $mainMod ALT, L, moveintogroup, r # Move into group right -bind = $mainMod ALT, K, moveintogroup, u # Moving into group up -bind = $mainMod ALT, J, moveintogroup, d # Moving into group down # For passthrough keyboard into a VM # bind = $mainMod ALT, P, submap, passthru diff --git a/config/hypr/UserConfigs/UserSettings.conf b/config/hypr/UserConfigs/UserSettings.conf index b9714a44..f81ccc6a 100644 --- a/config/hypr/UserConfigs/UserSettings.conf +++ b/config/hypr/UserConfigs/UserSettings.conf @@ -63,28 +63,6 @@ input { } } -group { - col.border_active = $color2 $color4 45deg - col.border_inactive = $color10 - - groupbar { - enabled = true - font_family = JetBrainsMono Nerd Font - font_size = 12 - gradients = true - height = 18 - priority = 3 - render_titles = true - scrolling = true - text_color = $foreground - - col.active = $color3 - col.inactive = $background - col.locked_active = $color6 - col.locked_inactive = $color8 - } -} - gestures { gesture = 3, horizontal, workspace -- cgit v1.2.3 From 2e24193c7322d29dbdd639b82251b808b0851043 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 13 Oct 2025 17:24:39 -0400 Subject: Removed v2.3.16 file in prep for release --- config/hypr/v2.3.16 | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 config/hypr/v2.3.16 diff --git a/config/hypr/v2.3.16 b/config/hypr/v2.3.16 deleted file mode 100644 index 31b3414d..00000000 --- a/config/hypr/v2.3.16 +++ /dev/null @@ -1,5 +0,0 @@ -### https://github.com/JaKooLit ### -## https://github.com/JaKooLit/Hyprland-Dots -## This is to have a reference of which version would be - -## note that this will always be higher than the released versions \ No newline at end of file -- cgit v1.2.3 From 2b6ae7120648aeaa612df088effa53ec3a75b691 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 13 Oct 2025 17:31:17 -0400 Subject: Updated CHANGELOG.md for Hyprsunet --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85d1559b..6fb9dc12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,31 @@ # Changelog — JAK's Hyprland Dotfiles -## 2025-10-06 +## October 2025 ### 🐛 Fixes + - Correct `windowrule` syntax error. - Ensure wallpaper selector applies wallpaper to SDDM. - Update theme colors when a new wallpaper is selected. ### đŸŒĻī¸ Weather.py -- â™ģī¸ Substantial refactor. + +- â™ģī¸ Substantial rewrite. - ✅ Launches properly in Waybar. - 📍 Improved location detection; overridable variables exposed. - 🌐 Switched to Open-Meteo for weather data with fallback providers. ### đŸ–Ĩī¸ Drop-down terminal + - 🔧 Start on login via `TerminalDropDown.sh` so first invocation works. - 🐱 Use Kitty explicitly instead of `$TERM` for consistent behavior. +### 🌇 HyprSunset + +- 🔧 Availble from waybar or`SUPER + N` + ### đŸ–ąī¸ Gestures + - 🔧 Updated to accommodate Hyprland 0.5x changes. ### đŸ‘Ĩ Contributors -- cgit v1.2.3 From 87c0db43a6fd53c4174158e0753941b8f5b6ee38 Mon Sep 17 00:00:00 2001 From: Don Williams Date: Mon, 13 Oct 2025 20:05:38 -0400 Subject: Upd scripts to be compatible with deb/ubuntu On branch ddubs-hyprsunset Your branch is up to date with 'origin/ddubs-hyprsunset'. Changes to be committed: modified: CHANGELOG.md modified: README.md modified: copy.sh modified: release.sh modified: upgrade.sh --- CHANGELOG.md | 4 ++++ README.md | 77 +++++++++++++++++++++++++++++++++++++++--------------------- copy.sh | 37 ++++++++++++++++++----------- release.sh | 39 ++++++++++++++++++------------ upgrade.sh | 25 ++++++++++++++++++++ 5 files changed, 126 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb9dc12..cd0effc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ - 📍 Improved location detection; overridable variables exposed. - 🌐 Switched to Open-Meteo for weather data with fallback providers. +### đŸ–Ĩī¸ Support for debian and ubuntu installs + +- Providing they are using Hyprland 0.51.1 or greater + ### đŸ–Ĩī¸ Drop-down terminal - 🔧 Start on login via `TerminalDropDown.sh` so first invocation works. diff --git a/README.md b/README.md index 42188073..df48058f 100644 --- a/README.md +++ b/README.md @@ -42,14 +42,18 @@ https://github.com/user-attachments/assets/49bc12b2-abaf-45de-a21c-67aacd9bb872 ### 📹 A video walkthroughs + - at the bottom --- + [![Typing SVG](https://readme-typing-svg.herokuapp.com?font=Fira+Code&weight=700&size=22&pause=1000&color=F7077E&vCenter=true&width=435&height=30&lines=INSTALLATION)](https://git.io/typing-svg) + ### 🚩 🏁 Auto Distro-Hyprland install scripts cloning and starting đŸ‡ĩ🇭 -> [!CAUTION] + +> [!CAUTION] > If you are using FISH SHELL, DO NOT use this function. Clone the Distro-Hyprland and ran install.sh instead - NOTE: you need package `curl` for this to work @@ -61,8 +65,8 @@ sh <(curl -L https://raw.githubusercontent.com/JaKooLit/Hyprland-Dots/main/Distr - you can now use above command to automatically clone the Distro-Hyprland install scripts below - it will clone the install scripts and start the `install.sh` 😎 - ### đŸ‘ī¸â€đŸ—¨ī¸ My Hyprland install Scripts đŸ‘ī¸â€đŸ—¨ī¸ + - Automated Hyprland Scripts for Distro of choice which will pull this dotfiles if opted to install these configurations - [Arch-Linux](https://github.com/JaKooLit/Arch-Hyprland) @@ -77,117 +81,136 @@ sh <(curl -L https://raw.githubusercontent.com/JaKooLit/Hyprland-Dots/main/Distr - [Ubuntu 24.04 LTS](https://github.com/JaKooLit/Ubuntu-Hyprland/tree/24.04) - [Ubuntu 24.10](https://github.com/JaKooLit/Ubuntu-Hyprland/tree/24.10) -- [Ubuntu 25.04 - (ALPHA STAGE)](https://github.com/JaKooLit/Ubuntu-Hyprland/tree/25.04) +- [Ubuntu 25.04](https://github.com/JaKooLit/Ubuntu-Hyprland/tree/25.04) +- [Ubuntu 25.10](https://github.com/JaKooLit/Ubuntu-Hyprland/tree/25.10) + --- ### đŸĒ§ Attention đŸĒ§ + - This repo does NOT contain or will NOT install any packages. These are only pre-configured-hyprland configs or dotfiles - refer to install scripts what packages needed to install... but atleast, Hyprland packages is needed 😏😏😏 duh!! - This repo will be pulled by the Distro-Hyprland install scripts above if you opt to download pre-configured dots ### 👀 Screenshots 👀 + - All screenshots are collected here [Screenshots](https://github.com/JaKooLit/screenshots/tree/main/Hyprland-ScreenShots) ### đŸ“Ļ Whats new? -- To easily track changes, I will be updating the [CHANGELOGS](https://github.com/JaKooLit/Hyprland-Dots/wiki/Changelogs) Screenshots will be included if worth mentioning the changes! + +- To easily track changes, I will be updating the [CHANGELOGS](https://github.com/JaKooLit/Hyprland-Dots/wiki/Changelogs) Screenshots will be included if worth mentioning the changes! > [!NOTE] -> Kindly note that by defeault, Kools Dots are adjusted / configured for 2k (1440p) display without scaling. +> Kindly note that by defeault, Kools Dots are adjusted / configured for 2k (1440p) display without scaling. ### đŸ’Ĩ Copying / Installation / Update instructions đŸ’Ĩ -- [`MORE INFO HERE`](https://github.com/JaKooLit/Hyprland-Dots/wiki/Install_&_Update) -> [!Note] -> The auto copy script "copy.sh" will create backups of intended directories to be copied. However, still a good idea to manually backup just incase script failed to backup! + +- [`MORE INFO HERE`](https://github.com/JaKooLit/Hyprland-Dots/wiki/Install_&_Update) + > [!Note] + > The auto copy script "copy.sh" will create backups of intended directories to be copied. However, still a good idea to manually backup just incase script failed to backup! - clone this repo by using git. Change directory, make executable and run the script > to download from Master branch + ```bash git clone --depth=1 https://github.com/JaKooLit/Hyprland-Dots.git cd Hyprland-Dots ``` > to download from Development branch (development and testing) + ```bash git clone --depth=1 https://github.com/JaKooLit/Hyprland-Dots.git -b development cd Hyprland-Dots ``` - automatic copy/install of pre-configured dots (recommended for updating) + ```bash chmod +x copy.sh ./copy.sh ``` - to copy/install from releases (stable) (note this is 1 version older than in main) + ```bash chmod +x release.sh ./release.sh ``` - UPGRADE.sh function -> [!IMPORTANT] -> You need rsync for it to work -> you should have already up and running KooL's Hyprland before using this function + > [!IMPORTANT] + > You need rsync for it to work + > you should have already up and running KooL's Hyprland before using this function + ```bash chmod +x upgrade.sh ./upgrade.sh ``` -## ❗❗❗ DEBIAN AND UBUNTU HEADS UP! -- I am getting ridiculously amount of messages for updating your KooL Hyprland dotfiles. I have made a BIG note on [`WIKI`](https://github.com/JaKooLit/Hyprland-Dots/wiki/Install_&_Update) +## ❗❗❗ DEBIAN AND UBUNTU! +- Debian 13 and ubuntu 25.10 +- You can now build Hyprland 0.51.1 from source using the `install.sh` script +- So the current version of Hyprland-Dots is compatible only in those cases. #### âš ī¸âš ī¸âš ī¸ ATTENTION - BACKUPS CREATED by SCRIPT + > [!CAUTION] > copy.sh, release.sh and even upgrade.sh creates a backup! > Kindly investigate manually contents on your $HOME/.config > Delete manually all the backups which you dont need #### đŸ›Žī¸ a small note on wallpapers + - by default, only few wallpapers will be copied (1 each dark and light plus 3 more). You will be offered to download more wallpapers. You can preview/check the additional wallpapers from [`THIS`](https://github.com/JaKooLit/Wallpaper-Bank/tree/main/wallpapers) Link +#### âš ī¸âš ī¸âš ī¸ A MUST! after copying / Installing these dots -#### âš ī¸âš ī¸âš ī¸ A MUST! after copying / Installing these dots -+ Press SUPER W and set a wallpaper. This is also to initiate wallust for waybar, kitty (tty) and rofi themes. However, If you use the copy.sh or the release.sh, there will be a preset initial Wallpaper and you dont have to do this +- Press SUPER W and set a wallpaper. This is also to initiate wallust for waybar, kitty (tty) and rofi themes. However, If you use the copy.sh or the release.sh, there will be a preset initial Wallpaper and you dont have to do this -+ Nvidia Owners. Make sure to edit your `~/.config/hypr/UserConfigs/ENVariables.conf` (highly recommended). -- NVIDIA users / owners, after installation, check [`THIS`](https://github.com/JaKooLit/Hyprland-Dots/wiki/Notes_to_remember#--for-nvidia-gpu-users) +- Nvidia Owners. Make sure to edit your `~/.config/hypr/UserConfigs/ENVariables.conf` (highly recommended). -+ If you have already set your own keybinds, monitors, etc.... Just copy over from backup created before log-out or reboot. (recommended) +* NVIDIA users / owners, after installation, check [`THIS`](https://github.com/JaKooLit/Hyprland-Dots/wiki/Notes_to_remember#--for-nvidia-gpu-users) +- If you have already set your own keybinds, monitors, etc.... Just copy over from backup created before log-out or reboot. (recommended) #### 📖 Known issues and possible solutions -- check out this page [FAQ](https://github.com/JaKooLit/Hyprland-Dots/wiki/FAQ) and [UNSOLVED ISSUES](https://github.com/JaKooLit/Hyprland-Dots/wiki/Known_Issues) +- check out this page [FAQ](https://github.com/JaKooLit/Hyprland-Dots/wiki/FAQ) and [UNSOLVED ISSUES](https://github.com/JaKooLit/Hyprland-Dots/wiki/Known_Issues) #### 🙋 QUESTIONS ?!?! â‰ī¸ + - FAQ! Yes you can use these dotfiles to other distro! Just ensure to install proper packages first! If it makes you feel better, I use same config on my Gentoo:) - QUICK HINT! Click the HINT! Waybar module (note only available in Waybar default and Simple-L [TOP] layout). Can be launched by Keybind `SUPER H` - More question? click here browse through this [WIKI](https://github.com/JaKooLit/Hyprland-Dots/wiki/) - If you want the old configs, it is collected on my "Archive" repo. See [HERE](https://github.com/JaKooLit/Hyprland-Dots-releases-Archive) #### ⌨ Keybinds + - Keybinds [`CLICK`](https://github.com/JaKooLit/Hyprland-Dots/wiki/Keybinds) #### 🙏 Special request -- If you have improvements on the dotfiles or configuration, feel free to submit a PR for improvement. I always welcome improvements as I am also just learning just like you guys! +- If you have improvements on the dotfiles or configuration, feel free to submit a PR for improvement. I always welcome improvements as I am also just learning just like you guys! #### âœī¸ Contributing -- Want to contribute? Click [`HERE`](https://github.com/JaKooLit/Hyprland-Dots/blob/main/CONTRIBUTING.md) for a guide how to contribute +- Want to contribute? Click [`HERE`](https://github.com/JaKooLit/Hyprland-Dots/blob/main/CONTRIBUTING.md) for a guide how to contribute #### đŸ¤ˇâ€â™‚ī¸ TO DO! -- [ ] Tweak dots - 🚧 in constant progress + +- [ ] Tweak dots - 🚧 in constant progress #### 🔮 Discord Server -- kindly join my [Discord](https://discord.com/invite/kool-tech-world) +- kindly join my [Discord](https://discord.com/invite/kool-tech-world) #### 💖 Support + - a Star on my Github repos would be nice 🌟 -- Subscribe to my Youtube Channel [YouTube](https://www.youtube.com/@Ja.KooLit) +- Subscribe to my Youtube Channel [YouTube](https://www.youtube.com/@Ja.KooLit) - you can also give support through coffee's or btc 😊 @@ -197,12 +220,12 @@ or [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/JaKooLit) -Or you can donate cryto on my btc wallet :) +Or you can donate cryto on my btc wallet :) + > 1N3MeV2dsX6gQB42HXU6MF2hAix1mqjo8i ![Bitcoin](https://github.com/user-attachments/assets/7ed32f8f-c499-46f0-a53c-3f6fbd343699) +## đŸĢ° Thank you for the stars 🩷 - -## đŸĢ° Thank you for the stars 🩷 [![Stargazers over time](https://starchart.cc/JaKooLit/Hyprland-Dots.svg?variant=adaptive)](https://starchart.cc/JaKooLit/Hyprland-Dots) diff --git a/copy.sh b/copy.sh index 77639bf9..d54402f3 100755 --- a/copy.sh +++ b/copy.sh @@ -35,21 +35,30 @@ print_color() { printf "%b%s%b\n" "$1" "$2" "$RESET" } -# Check /etc/os-release to see if this is an Ubuntu based distro -if grep -iq '^\(ID_LIKE\|ID\)=.*ubuntu' /etc/os-release >/dev/null 2>&1; then +# Check /etc/os-release for Ubuntu or Debian and warn about Hyprland version requirement +if grep -iqE '^(ID_LIKE|ID)=.*(ubuntu|debian)' /etc/os-release >/dev/null 2>&1; then printf "\n%.0s" {1..1} - print_color $WARNING " - █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ - KOOL DOTS version INCOMPATIBLE - █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ - - Ubuntu detected. Refer to Hyprland-Dots README - For instruction on how to update your KooL Hyprland Dots - - exiting .... - " - printf "\n%.0s" {1..3} - exit 1 + print_color $WARNING "\nThese Dotfiles are only supported on Hyprland 0.51.1 or greater. Do not install on older revisions.\n" + while true; do + echo -n "${CAT} Do you want to continue anyway? (y/N): " + read _continue + _continue=$(echo "${_continue}" | tr '[:upper:]' '[:lower:]') + case "${_continue}" in + y|yes) + echo "${NOTE} Proceeding on Ubuntu/Debian by user confirmation." + break + ;; + n|no|"") + printf "\n%.0s" {1..1} + echo "${INFO} Aborting per user choice. No changes made." + printf "\n%.0s" {1..1} + exit 1 + ;; + *) + echo "${WARN} Please answer 'y' or 'n'." + ;; + esac + done fi printf "\n%.0s" {1..1} diff --git a/release.sh b/release.sh index 763e4bbe..78063ee1 100755 --- a/release.sh +++ b/release.sh @@ -18,21 +18,30 @@ BLUE="$(tput setaf 4)" SKY_BLUE="$(tput setaf 6)" RESET="$(tput sgr0)" -# Check /etc/os-release to see if this is an Ubuntu based distro -if grep -iq '^\(ID_LIKE\|ID\)=.*ubuntu' /etc/os-release >/dev/null 2>&1; then - printf "\n%.0s" {1..1} - print_color $WARNING " - █▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█ - KOOL DOTS version INCOMPATIBLE - █▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█ - - Ubuntu detected. Refer to Hyprland-Dots README - For instruction on how to update your KooL Hyprland Dots - - exiting .... - " - printf "\n%.0s" {1..3} - exit 1 +# Check /etc/os-release for Ubuntu or Debian and warn about Hyprland version requirement +if grep -iqE '^(ID_LIKE|ID)=.*(ubuntu|debian)' /etc/os-release >/dev/null 2>&1; then + printf "\n%.0s" {1..1} + echo "${WARNING} These Dotfiles are only supported on Hyprland 0.51.1 or greater. Do not install on older revisions.${RESET}" + while true; do + echo -n "${CAT} Do you want to continue anyway? (y/N): ${RESET}" + read _continue + _continue=$(echo "${_continue}" | tr '[:upper:]' '[:lower:]') + case "${_continue}" in + y|yes) + echo "${NOTE} Proceeding on Ubuntu/Debian by user confirmation." + break + ;; + n|no|"") + printf "\n%.0s" {1..1} + echo "${INFO} Aborting per user choice. No changes made." + printf "\n%.0s" {1..1} + exit 1 + ;; + *) + echo "${WARN} Please answer 'y' or 'n'." + ;; + esac + done fi diff --git a/upgrade.sh b/upgrade.sh index 034d7a2c..a47bd48b 100755 --- a/upgrade.sh +++ b/upgrade.sh @@ -27,6 +27,31 @@ echo -e "\e[35m \e[0m" printf "\n%.0s" {1..1} +# On Ubuntu/Debian, warn about required Hyprland version and prompt to continue +if grep -iqE '^(ID_LIKE|ID)=.*(ubuntu|debian)' /etc/os-release >/dev/null 2>&1; then + echo "${WARNING} These Dotfiles are only supported on Hyprland 0.51.1 or greater. Do not install on older revisions.${RESET}" + while true; do + echo -n "${CAT} Do you want to continue anyway? (y/N): ${RESET}" + read _continue + _continue=$(echo "${_continue}" | tr '[:upper:]' '[:lower:]') + case "${_continue}" in + y|yes) + echo "${NOTE} Proceeding on Ubuntu/Debian by user confirmation." + break + ;; + n|no|"") + printf "\n%.0s" {1..1} + echo "${INFO} Aborting per user choice. No changes made." + printf "\n%.0s" {1..1} + exit 1 + ;; + *) + echo "${WARN} Please answer 'y' or 'n'." + ;; + esac + done +fi + echo "${WARNING}A T T E N T I O N !${RESET}" echo "${SKY_BLUE}This script is meant to manually upgrade your KooL Hyprland Dots${RESET}" echo "${YELLOW}NOTE that you should edit this script and assign an Directory or Files exclusion${RESET}" -- cgit v1.2.3