From 920bb47bd7c58f44e44af84cc9c9066bd84986aa Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Fri, 20 Jun 2025 14:52:11 +0200 Subject: [PATCH] various updates --- .config/hypr/hyprland.colors.conf | 11 + .config/hypr/hyprland.conf | 17 +- .config/hypr/hyprlock.conf | 105 ++++-- .config/hypr/hyprlock/check-capslock.sh | 9 + .config/hypr/hyprlock/status.sh | 29 ++ .config/matugen/config.toml | 34 ++ .config/matugen/scripts/least_busy_region.py | 338 ++++++++++++++++++ .config/matugen/templates/ags/_material.scss | 70 ++++ .../templates/ags/sourceviewtheme-light.xml | 95 +++++ .../matugen/templates/ags/sourceviewtheme.xml | 121 +++++++ .config/matugen/templates/colors.json | 51 +++ .../matugen/templates/fuzzel/fuzzel_theme.ini | 8 + .config/matugen/templates/gtk/gtk-colors.css | 22 ++ .../templates/hyprland/hyprland.colors.conf | 10 + .../matugen/templates/hyprland/hyprlock.conf | 99 +++++ .config/matugen/templates/kde/color.txt | 1 + .../kde/kde-material-you-colors-wrapper.sh | 46 +++ .config/matugen/templates/wallpaper.txt | 1 + .../modules/common/ConfigOptions.qml | 24 +- .../modules/mediaControls/PlayerControl.qml | 2 +- .../quickshell/modules/sidebarLeft/Anime.qml | 2 +- .../sidebarLeft/SidebarLeftContent.qml | 2 +- .../scripts/colors/colors_from_image.sh | 16 + .../quickshell/scripts/colors/switchwall.sh | 26 +- .config/quickshell/services/SystemInfo.qml | 1 + README.md | 2 + configuration.nix | 22 +- home.nix | 10 + python/requirements.in | 10 + python/requirements.txt | 38 ++ 30 files changed, 1163 insertions(+), 59 deletions(-) create mode 100644 .config/hypr/hyprland.colors.conf create mode 100644 .config/hypr/hyprlock/check-capslock.sh create mode 100644 .config/hypr/hyprlock/status.sh create mode 100644 .config/matugen/config.toml create mode 100755 .config/matugen/scripts/least_busy_region.py create mode 100644 .config/matugen/templates/ags/_material.scss create mode 100644 .config/matugen/templates/ags/sourceviewtheme-light.xml create mode 100644 .config/matugen/templates/ags/sourceviewtheme.xml create mode 100644 .config/matugen/templates/colors.json create mode 100644 .config/matugen/templates/fuzzel/fuzzel_theme.ini create mode 100644 .config/matugen/templates/gtk/gtk-colors.css create mode 100644 .config/matugen/templates/hyprland/hyprland.colors.conf create mode 100644 .config/matugen/templates/hyprland/hyprlock.conf create mode 100644 .config/matugen/templates/kde/color.txt create mode 100755 .config/matugen/templates/kde/kde-material-you-colors-wrapper.sh create mode 100644 .config/matugen/templates/wallpaper.txt create mode 100755 .config/quickshell/scripts/colors/colors_from_image.sh create mode 100644 python/requirements.in create mode 100644 python/requirements.txt diff --git a/.config/hypr/hyprland.colors.conf b/.config/hypr/hyprland.colors.conf new file mode 100644 index 0000000..c9ad99f --- /dev/null +++ b/.config/hypr/hyprland.colors.conf @@ -0,0 +1,11 @@ + +general { + col.active_border = rgba(F7DCDE39) + col.inactive_border = rgba(A58A8D30) +} + +misc { + background_color = rgba(1D1011FF) +} + +windowrulev2 = bordercolor rgba(FFB2BCAA) rgba(FFB2BC77),pinned:1 \ No newline at end of file diff --git a/.config/hypr/hyprland.conf b/.config/hypr/hyprland.conf index 9d3f54a..e8d5771 100644 --- a/.config/hypr/hyprland.conf +++ b/.config/hypr/hyprland.conf @@ -24,6 +24,7 @@ cursor { source = ~/.config/hypr/monitors.conf source = ~/.config/hypr/workspaces.conf +source = ~/.config/hypr/hyprland.colors.conf debug:disable_logs = false @@ -67,7 +68,10 @@ exec-once=otd-daemon exec-once=lorri daemon #exec-once=wlsunset -S 6:00 -s 21:30 -exec-once=swaybg -m fill -i ~/.config/hypr/wallpapers/sunflowers.jpg +# exec-once=swaybg -m fill -i ~/.config/hypr/wallpapers/sunflowers.jpg +exec-once = swww-daemon --format xrgb --no-cache +# "$(cat ~/.local/state/quickshell/user/generated/wallpaper/path.txt)" +exec-once = sleep 0.5; swww img ~/.config/hypr/wallpapers/sunflowers.jpg --transition-step 100 --transition-fps 120 --transition-type grow --transition-angle 30 --transition-duration 1 # exec-once=waybar exec-once=quickshell exec-once=blueman-applet @@ -80,6 +84,10 @@ exec-once=sleep 10;aw-qt& sleep 2; awatcher exec-once = nm-applet --indicator # systray app for Network/Wifi exec-once=kdeconnect-indicator exec-once = hyprpm reload -n +exec-once = wl-paste --type text --watch cliphist store +exec-once = wl-paste --type image --watch cliphist store +exec-once = fcitx5 +exec-once = easyeffects --gapplication-service #$swaylock = swaylock --screenshots --clock --indicator --effect-blur 6x6 --fade-in 0.2 --ring-color 4e9dc2 --key-hl-color 71b0ce $swaylock = hyprlock @@ -156,8 +164,8 @@ general { gaps_in = 5 gaps_out = 10 border_size = 1 - col.active_border = rgba(75DDDD50) rgba(09BC8A50) 45deg - col.inactive_border = rgba(172A3AFF) + # col.active_border = rgba(75DDDD50) rgba(09BC8A50) 45deg + # col.inactive_border = rgba(172A3AFF) layout = master resize_on_border = true @@ -251,6 +259,9 @@ windowrule=float,title:Open File windowrule=float,title:branchdialog windowrule=tile,title:*vivaldi* +# can be more specific (:bar, :mediaControls, etc) +layerrule = blur, quickshell + blurls=gtk-layer-shell blurls=lockscreen blurls = rofi diff --git a/.config/hypr/hyprlock.conf b/.config/hypr/hyprlock.conf index c8c7bf9..0e67515 100644 --- a/.config/hypr/hyprlock.conf +++ b/.config/hypr/hyprlock.conf @@ -1,6 +1,10 @@ -general { - -} +$text_color = rgba(FFDAD6FF) +$entry_background_color = rgba(41000311) +$entry_border_color = rgba(896E6C55) +$entry_color = rgba(FFDAD6FF) +$font_family = Rubik Light +$font_family_clock = Rubik Light +$font_material_symbols = Material Symbols Rounded background { monitor = @@ -16,35 +20,80 @@ background { vibrancy = 0.1696 vibrancy_darkness = 0.0 } - input-field { monitor = - size = 200, 50 - outline_thickness = 3 - dots_size = 0.33 # Scale of input-field height, 0.2 - 0.8 - dots_spacing = 0.15 # Scale of dots' absolute size, 0.0 - 1.0 - dots_center = false - dots_rounding = -1 # -1 default circle, -2 follow input-field rounding - outer_color = rgb(151515) - inner_color = rgb(200, 200, 200) - font_color = rgb(10, 10, 10) + size = 250, 50 + outline_thickness = 2 + dots_size = 0.1 + dots_spacing = 0.3 + outer_color = $entry_border_color + inner_color = $entry_background_color + font_color = $entry_color fade_on_empty = true - fade_timeout = 1000 # Milliseconds before fade_on_empty is triggered. - placeholder_text = Password # Text rendered in the input box when it's empty. - hide_input = false - rounding = -1 # -1 means complete rounding (circle/oval) - check_color = rgb(204, 136, 34) - fail_color = rgb(204, 34, 34) # if authentication failed, changes outer_color and fail message color - fail_text = $FAIL ($ATTEMPTS) # can be set to empty - fail_timeout = 2000 # milliseconds before fail_text and fail_color disappears - fail_transition = 300 # transition time in ms between normal outer_color and fail_color - capslock_color = -1 - numlock_color = -1 - bothlock_color = -1 # when both locks are active. -1 means don't change outer color (same for above) - invert_numlock = false # change color if numlock is off - swap_font_color = false # see below - position = 0, -20 + position = 0, 20 halign = center valign = center +} + +label { # Caps Lock Warning + monitor = + text = cmd[update:250] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/check-capslock.sh + color = $text_color + font_size = 13 + font_family = $font_family + position = 0, -25 + halign = center + valign = center +} + + +label { # Clock + monitor = + text = $TIME + color = $text_color + font_size = 65 + font_family = $font_family_clock + + position = 0, 300 + halign = center + valign = center +} +label { # Date + monitor = + text = cmd[update:5000] date +"%A, %B %d" + color = $text_color + font_size = 17 + font_family = $font_family + + position = 0, 240 + halign = center + valign = center +} + +label { # User + monitor = + text =  $USER + color = $text_color + outline_thickness = 2 + dots_size = 0.2 # Scale of input-field height, 0.2 - 0.8 + dots_spacing = 0.2 # Scale of dots' absolute size, 0.0 - 1.0 + dots_center = true + font_size = 20 + font_family = $font_family + position = 0, 50 + halign = center + valign = bottom +} + +label { # Status + monitor = + text = cmd[update:5000] ${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hyprlock/status.sh + color = $text_color + font_size = 14 + font_family = $font_family + + position = 30, -30 + halign = left + valign = top } \ No newline at end of file diff --git a/.config/hypr/hyprlock/check-capslock.sh b/.config/hypr/hyprlock/check-capslock.sh new file mode 100644 index 0000000..ca56178 --- /dev/null +++ b/.config/hypr/hyprlock/check-capslock.sh @@ -0,0 +1,9 @@ +#!/bin/env bash + +MAIN_KB_CAPS=$(hyprctl devices | grep -B 6 "main: yes" | grep "capsLock" | head -1 | awk '{print $2}') + +if [ "$MAIN_KB_CAPS" = "yes" ]; then + echo "Caps Lock active" +else + echo "" +fi diff --git a/.config/hypr/hyprlock/status.sh b/.config/hypr/hyprlock/status.sh new file mode 100644 index 0000000..dd80633 --- /dev/null +++ b/.config/hypr/hyprlock/status.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +############ Variables ############ +enable_battery=false +battery_charging=false + +####### Check availability ######## +for battery in /sys/class/power_supply/*BAT*; do + if [[ -f "$battery/uevent" ]]; then + enable_battery=true + if [[ $(cat /sys/class/power_supply/*/status | head -1) == "Charging" ]]; then + battery_charging=true + fi + break + fi +done + +############# Output ############# +if [[ $enable_battery == true ]]; then + if [[ $battery_charging == true ]]; then + echo -n "(+) " + fi + echo -n "$(cat /sys/class/power_supply/*/capacity | head -1)"% + if [[ $battery_charging == false ]]; then + echo -n " remaining" + fi +fi + +echo '' \ No newline at end of file diff --git a/.config/matugen/config.toml b/.config/matugen/config.toml new file mode 100644 index 0000000..e920943 --- /dev/null +++ b/.config/matugen/config.toml @@ -0,0 +1,34 @@ +[config] +version_check = false + +[templates.m3colors] +input_path = '~/.config/matugen/templates/colors.json' +output_path = '~/.local/state/quickshell/user/generated/colors.json' + +[templates.hyprland] +input_path = '~/.config/matugen/templates/hyprland/hyprland.colors.conf' +output_path = '~/.config/hypr/hyprland.colors.conf' + +[templates.hyprlock] +input_path = '~/.config/matugen/templates/hyprland/hyprlock.conf' +output_path = '~/.config/hypr/hyprlock.conf' + +[templates.fuzzel] +input_path = '~/.config/matugen/templates/fuzzel/fuzzel_theme.ini' +output_path = '~/.config/fuzzel/fuzzel_theme.ini' + +[templates.gtk3] +input_path = '~/.config/matugen/templates/gtk/gtk-colors.css' +output_path = '~/.config/gtk-3.0/gtk.css' + +[templates.gtk4] +input_path = '~/.config/matugen/templates/gtk/gtk-colors.css' +output_path = '~/.config/gtk-4.0/gtk.css' + +[templates.kde_colors] +input_path = '~/.config/matugen/templates/kde/color.txt' +output_path = '~/.local/state/quickshell/user/generated/color.txt' + +[templates.wallpaper] +input_path = '~/.config/matugen/templates/wallpaper.txt' +output_path = '~/.local/state/quickshell/user/generated/wallpaper/path.txt' diff --git a/.config/matugen/scripts/least_busy_region.py b/.config/matugen/scripts/least_busy_region.py new file mode 100755 index 0000000..a1f2f47 --- /dev/null +++ b/.config/matugen/scripts/least_busy_region.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python3 +# Disclaimer: This script was ai-generated and went through minimal revision. + +import os +os.environ["OPENCV_LOG_LEVEL"] = "SILENT" +import cv2 +import numpy as np +import argparse +import json + +def center_crop(img, target_w, target_h): + h, w = img.shape[:2] + if w == target_w and h == target_h: + return img + x1 = max(0, (w - target_w) // 2) + y1 = max(0, (h - target_h) // 2) + x2 = x1 + target_w + y2 = y1 + target_h + return img[y1:y2, x1:x2] + +def find_least_busy_region(image_path, region_width=300, region_height=200, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", padding=50): + img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) + if img is None: + raise FileNotFoundError(f"Image not found: {image_path}") + orig_h, orig_w = img.shape + scale = 1.0 + if screen_width is not None and screen_height is not None: + scale_w = screen_width / orig_w + scale_h = screen_height / orig_h + if screen_mode == "fill": + scale = max(scale_w, scale_h) + else: + scale = min(scale_w, scale_h) + new_w = int(orig_w * scale) + new_h = int(orig_h * scale) + if verbose: + print(f"Scaling image from {orig_w}x{orig_h} to {new_w}x{new_h} (scale: {scale:.3f}, mode: {screen_mode})") + img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4) + img = center_crop(img, screen_width, screen_height) + if verbose: + print(f"Cropped image to {screen_width}x{screen_height}") + else: + if verbose: + print(f"Using original image size: {orig_w}x{orig_h}") + arr = img.astype(np.float64) + h, w = arr.shape + # Use OpenCV's integral for fast computation + integral = cv2.integral(arr, sdepth=cv2.CV_64F)[1:,1:] + integral_sq = cv2.integral(arr**2, sdepth=cv2.CV_64F)[1:,1:] + def region_sum(ii, x1, y1, x2, y2): + total = ii[y2, x2] + if x1 > 0: + total -= ii[y2, x1-1] + if y1 > 0: + total -= ii[y1-1, x2] + if x1 > 0 and y1 > 0: + total += ii[y1-1, x1-1] + return total + min_var = None + min_coords = (0, 0) + area = region_width * region_height + x_start = padding + y_start = padding + x_end = w - region_width - padding + 1 + y_end = h - region_height - padding + 1 + for y in range(y_start, max(y_end, y_start+1), stride): + for x in range(x_start, max(x_end, x_start+1), stride): + x1, y1 = x, y + x2, y2 = x + region_width - 1, y + region_height - 1 + s = region_sum(integral, x1, y1, x2, y2) + s2 = region_sum(integral_sq, x1, y1, x2, y2) + mean = s / area + var = (s2 / area) - (mean ** 2) + if (min_var is None) or (var < min_var): + min_var = var + min_coords = (x, y) + return min_coords, min_var + +def find_largest_region(image_path, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", threshold=100.0, aspect_ratio=1.0, padding=50): + img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) + if img is None: + raise FileNotFoundError(f"Image not found: {image_path}") + orig_h, orig_w = img.shape + scale = 1.0 + if screen_width is not None and screen_height is not None: + scale_w = screen_width / orig_w + scale_h = screen_height / orig_h + if screen_mode == "fill": + scale = max(scale_w, scale_h) + else: + scale = min(scale_w, scale_h) + new_w = int(orig_w * scale) + new_h = int(orig_h * scale) + if verbose: + print(f"Scaling image from {orig_w}x{orig_h} to {new_w}x{new_h} (scale: {scale:.3f}, mode: {screen_mode})") + img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4) + img = center_crop(img, screen_width, screen_height) + if verbose: + print(f"Cropped image to {screen_width}x{screen_height}") + else: + if verbose: + print(f"Using original image size: {orig_w}x{orig_h}") + arr = img.astype(np.float64) + h, w = arr.shape + # Use OpenCV's integral for fast computation + integral = cv2.integral(arr, sdepth=cv2.CV_64F)[1:,1:] + integral_sq = cv2.integral(arr**2, sdepth=cv2.CV_64F)[1:,1:] + def region_sum(ii, x1, y1, x2, y2): + total = ii[y2, x2] + if x1 > 0: + total -= ii[y2, x1-1] + if y1 > 0: + total -= ii[y1-1, x2] + if x1 > 0 and y1 > 0: + total += ii[y1-1, x1-1] + return total + min_size = 10 + max_size = min(h, int(w / aspect_ratio)) if aspect_ratio >= 1.0 else min(int(h * aspect_ratio), w) + best = None + best_size = min_size + while min_size <= max_size: + mid = (min_size + max_size) // 2 + if aspect_ratio >= 1.0: + region_h = mid + region_w = int(mid * aspect_ratio) + else: + region_w = mid + region_h = int(mid / aspect_ratio) + if region_w > w or region_h > h: + max_size = mid - 1 + continue + found = False + x_start = padding + y_start = padding + x_end = w - region_w - padding + 1 + y_end = h - region_h - padding + 1 + for y in range(y_start, max(y_end, y_start+1), stride): + for x in range(x_start, max(x_end, x_start+1), stride): + x1, y1 = x, y + x2, y2 = x + region_w - 1, y + region_h - 1 + s = region_sum(integral, x1, y1, x2, y2) + s2 = region_sum(integral_sq, x1, y1, x2, y2) + area = region_w * region_h + mean = s / area + var = (s2 / area) - (mean ** 2) + if var <= threshold: + found = True + best = (x, y, region_w, region_h, var) + break + if found: + break + if found: + best_size = mid + min_size = mid + 1 + else: + max_size = mid - 1 + if best: + x, y, region_w, region_h, var = best + center_x = x + region_w // 2 + center_y = y + region_h // 2 + return (center_x, center_y), (region_w, region_h), var + else: + return None, (0, 0), None + +def draw_region(image_path, coords, region_width=300, region_height=200, output_path='output.png', screen_width=None, screen_height=None, screen_mode="fill"): + img = cv2.imread(image_path) + if img is None: + raise FileNotFoundError(f"Image not found: {image_path}") + orig_h, orig_w = img.shape[:2] + if screen_width is not None and screen_height is not None: + scale_w = screen_width / orig_w + scale_h = screen_height / orig_h + if screen_mode == "fill": + scale = max(scale_w, scale_h) + else: + scale = min(scale_w, scale_h) + new_w = int(orig_w * scale) + new_h = int(orig_h * scale) + img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4) + img = center_crop(img, screen_width, screen_height) + x, y = coords + cv2.rectangle(img, (x, y), (x+region_width-1, y+region_height-1), (0,0,255), 3) + cv2.imwrite(output_path, img) + print(f"Saved output image with rectangle at {output_path}") + +def draw_largest_region(image_path, center, size, output_path='output.png', screen_width=None, screen_height=None, screen_mode="fill"): + img = cv2.imread(image_path) + if img is None: + raise FileNotFoundError(f"Image not found: {image_path}") + orig_h, orig_w = img.shape[:2] + if screen_width is not None and screen_height is not None: + scale_w = screen_width / orig_w + scale_h = screen_height / orig_h + if screen_mode == "fill": + scale = max(scale_w, scale_h) + else: + scale = min(scale_w, scale_h) + new_w = int(orig_w * scale) + new_h = int(orig_h * scale) + img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4) + img = center_crop(img, screen_width, screen_height) + cx, cy = center + region_w, region_h = size + x1 = cx - region_w // 2 + y1 = cy - region_h // 2 + x2 = cx + region_w // 2 - 1 + y2 = cy + region_h // 2 - 1 + cv2.rectangle(img, (x1, y1), (x2, y2), (255,0,0), 3) + cv2.imwrite(output_path, img) + print(f"Saved output image with largest region at {output_path}") + +def get_dominant_color(image_path, x, y, w, h, screen_width=None, screen_height=None, screen_mode="fill"): + img = cv2.imread(image_path) + if img is None: + raise FileNotFoundError(f"Image not found: {image_path}") + orig_h, orig_w = img.shape[:2] + if screen_width is not None and screen_height is not None: + scale_w = screen_width / orig_w + scale_h = screen_height / orig_h + if screen_mode == "fill": + scale = max(scale_w, scale_h) + else: + scale = min(scale_w, scale_h) + new_w = int(orig_w * scale) + new_h = int(orig_h * scale) + img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LANCZOS4) + img = center_crop(img, screen_width, screen_height) + # Ensure region is within bounds + x = max(0, x) + y = max(0, y) + w = max(1, min(w, img.shape[1] - x)) + h = max(1, min(h, img.shape[0] - y)) + region = img[y:y+h, x:x+w] + if region.size == 0 or region.shape[0] == 0 or region.shape[1] == 0: + return [0, 0, 0] + region = region.reshape((-1, 3)) + # Filter out black pixels (optional, improves accuracy for some images) + non_black = region[np.any(region > 10, axis=1)] + if non_black.shape[0] == 0: + non_black = region + region = np.float32(non_black) + if region.shape[0] < 3: + return [int(x) for x in np.mean(region, axis=0)] + # K-means to find dominant color + criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0) + K = min(3, region.shape[0]) + _, labels, centers = cv2.kmeans(region, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS) + counts = np.bincount(labels.flatten()) + dominant = centers[np.argmax(counts)] + return [int(x) for x in dominant] + +def main(): + parser = argparse.ArgumentParser(description="Find least busy region in an image and output a JSON. Made for determining a suitable position for a wallpaper widget.") + parser.add_argument("image_path", help="Path to the input image") + parser.add_argument("--width", type=int, default=300, help="Region width") + parser.add_argument("--height", type=int, default=200, help="Region height") + parser.add_argument("-v", "--visual-output", action="store_true", help="Output image with rectangle") + parser.add_argument("--screen-width", type=int, default=1920, help="Screen width for wallpaper scaling") + parser.add_argument("--screen-height", type=int, default=1080, help="Screen height for wallpaper scaling") + parser.add_argument("--stride", type=int, default=4, help="Step size for sliding window (higher is faster, less precise)") + parser.add_argument("--screen-mode", choices=["fill", "fit"], default="fill", help="Wallpaper scaling mode: 'fill' (default) or 'fit'") + parser.add_argument("--verbose", action="store_true", help="Print verbose output") + parser.add_argument("-l", "--largest-region", action="store_true", help="Find the largest region under the variance threshold and output its center") + parser.add_argument("-t", "--variance-threshold", type=float, default=1000.0, help="Variance threshold for largest region mode") + parser.add_argument("--aspect-ratio", type=float, default=1.78, help="Aspect ratio (width/height) for largest region mode") + parser.add_argument("--padding", type=int, default=50, help="Minimum distance from region to image edge (default: 50)") + args = parser.parse_args() + + if args.largest_region: + center, size, var = find_largest_region( + args.image_path, + screen_width=args.screen_width, + screen_height=args.screen_height, + verbose=args.verbose, + stride=args.stride, + screen_mode=args.screen_mode, + threshold=args.variance_threshold, + aspect_ratio=args.aspect_ratio, + padding=args.padding + ) + if center: + if args.visual_output: + draw_largest_region(args.image_path, center, size, screen_width=args.screen_width, screen_height=args.screen_height, screen_mode=args.screen_mode) + # Extract dominant color + cx, cy = center + region_w, region_h = size + x1 = cx - region_w // 2 + y1 = cy - region_h // 2 + dominant_color = get_dominant_color( + args.image_path, x1, y1, region_w, region_h, + screen_width=args.screen_width, screen_height=args.screen_height, screen_mode=args.screen_mode + ) + dominant_color_hex = '#{:02x}{:02x}{:02x}'.format(*dominant_color) + print(json.dumps({ + "center_x": center[0], + "center_y": center[1], + "width": size[0], + "height": size[1], + "variance": var, + "dominant_color": dominant_color_hex + })) + else: + print(json.dumps({"error": "No region found under the threshold."})) + return + + coords, variance = find_least_busy_region( + args.image_path, + region_width=args.width, + region_height=args.height, + screen_width=args.screen_width, + screen_height=args.screen_height, + verbose=args.verbose, + stride=args.stride, + screen_mode=args.screen_mode, + padding=args.padding + ) + if args.visual_output: + draw_region(args.image_path, coords, region_width=args.width, region_height=args.height, screen_width=args.screen_width, screen_height=args.screen_height, screen_mode=args.screen_mode) + # Output JSON with center point + center_x = coords[0] + args.width // 2 + center_y = coords[1] + args.height // 2 + dominant_color = get_dominant_color( + args.image_path, coords[0], coords[1], args.width, args.height, + screen_width=args.screen_width, screen_height=args.screen_height, screen_mode=args.screen_mode + ) + dominant_color_hex = '#{:02x}{:02x}{:02x}'.format(*dominant_color) + print(json.dumps({ + "center_x": center_x, + "center_y": center_y, + "width": args.width, + "height": args.height, + "variance": variance, + "dominant_color": dominant_color_hex + })) + +if __name__ == "__main__": + main() + diff --git a/.config/matugen/templates/ags/_material.scss b/.config/matugen/templates/ags/_material.scss new file mode 100644 index 0000000..81acbfe --- /dev/null +++ b/.config/matugen/templates/ags/_material.scss @@ -0,0 +1,70 @@ +$darkmode: False; +$transparent: False; +$background: {{colors.background.default.hex}}; +$onBackground: {{colors.on_background.default.hex}}; +$surface: {{colors.surface.default.hex}}; +$surfaceDim: {{colors.surface_dim.default.hex}}; +$surfaceBright: {{colors.surface_bright.default.hex}}; +$surfaceContainerLowest: {{colors.surface_container_lowest.default.hex}}; +$surfaceContainerLow: {{colors.surface_container_low.default.hex}}; +$surfaceContainer: {{colors.surface_container.default.hex}}; +$surfaceContainerHigh: {{colors.surface_container_high.default.hex}}; +$surfaceContainerHighest: {{colors.surface_container_highest.default.hex}}; +$onSurface: {{colors.on_surface.default.hex}}; +$surfaceVariant: {{colors.surface_variant.default.hex}}; +$onSurfaceVariant: {{colors.on_surface_variant.default.hex}}; +$inverseSurface: {{colors.inverse_surface.default.hex}}; +$inverseOnSurface: {{colors.inverse_on_surface.default.hex}}; +$outline: {{colors.outline.default.hex}}; +$outlineVariant: {{colors.outline_variant.default.hex}}; +$shadow: {{colors.shadow.default.hex}}; +$scrim: {{colors.scrim.default.hex}}; +$primary: {{colors.primary.default.hex}}; +$onPrimary: {{colors.on_primary.default.hex}}; +$primaryContainer: {{colors.primary_container.default.hex}}; +$onPrimaryContainer: {{colors.on_primary_container.default.hex}}; +$inversePrimary: {{colors.inverse_primary.default.hex}}; +$secondary: {{colors.secondary.default.hex}}; +$onSecondary: {{colors.on_secondary.default.hex}}; +$secondaryContainer: {{colors.secondary_container.default.hex}}; +$onSecondaryContainer: {{colors.on_secondary_container.default.hex}}; +$tertiary: {{colors.tertiary.default.hex}}; +$onTertiary: {{colors.on_tertiary.default.hex}}; +$tertiaryContainer: {{colors.tertiary_container.default.hex}}; +$onTertiaryContainer: {{colors.on_tertiary_container.default.hex}}; +$error: {{colors.error.default.hex}}; +$onError: {{colors.on_error.default.hex}}; +$errorContainer: {{colors.error_container.default.hex}}; +$onErrorContainer: {{colors.on_error_container.default.hex}}; +$primaryFixed: {{colors.primary_fixed.default.hex}}; +$primaryFixedDim: {{colors.primary_fixed_dim.default.hex}}; +$onPrimaryFixed: {{colors.on_primary_fixed.default.hex}}; +$onPrimaryFixedVariant: {{colors.on_primary_fixed_variant.default.hex}}; +$secondaryFixed: {{colors.secondary_fixed.default.hex}}; +$secondaryFixedDim: {{colors.secondary_fixed_dim.default.hex}}; +$onSecondaryFixed: {{colors.on_secondary_fixed.default.hex}}; +$onSecondaryFixedVariant: {{colors.on_secondary_fixed_variant.default.hex}}; +$tertiaryFixed: {{colors.tertiary_fixed.default.hex}}; +$tertiaryFixedDim: {{colors.tertiary_fixed_dim.default.hex}}; +$onTertiaryFixed: {{colors.on_tertiary_fixed.default.hex}}; +$onTertiaryFixedVariant: {{colors.on_tertiary_fixed_variant.default.hex}}; +$success: #B5CCBA; +$onSuccess: #213528; +$successContainer: #374B3E; +$onSuccessContainer: #D1E9D6; +$term0: #0D1C20; +$term1: #8383FF; +$term2: #63DFD4; +$term3: #75FCDD; +$term4: #76B4BD; +$term5: #7AAEEA; +$term6: #81D8D7; +$term7: #CCDBD5; +$term8: #B1BCB5; +$term9: #BCB9FF; +$term10: #F6FFFD; +$term11: #FFFFFF; +$term12: #BEE3E5; +$term13: #C8DAFF; +$term14: #E5FFFE; +$term15: #ADEDF6; diff --git a/.config/matugen/templates/ags/sourceviewtheme-light.xml b/.config/matugen/templates/ags/sourceviewtheme-light.xml new file mode 100644 index 0000000..d501c31 --- /dev/null +++ b/.config/matugen/templates/ags/sourceviewtheme-light.xml @@ -0,0 +1,95 @@ + + + end_4 + <_description>Catppuccin port but very random + +