update
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
Ax-Shell configuration package.
|
||||
"""
|
||||
# Import only specific names actually defined in data.py
|
||||
# This prevents circular imports by not importing everything
|
||||
from .data import APP_NAME, APP_NAME_CAP, CACHE_DIR, CONFIG_FILE
|
||||
@@ -0,0 +1,36 @@
|
||||
[general]
|
||||
bars = 24
|
||||
|
||||
sensitivity = 100
|
||||
framerate = 60
|
||||
lower_cutoff_freq = 50
|
||||
higher_cutoff_freq = 8000
|
||||
autosens = 1
|
||||
|
||||
# these parameters should not be changed
|
||||
# bar width and spacing goes wrong with raw output
|
||||
bar_width = 1
|
||||
bar_spacing = 0
|
||||
|
||||
[output]
|
||||
method = raw
|
||||
raw_target = /tmp/cava.fifo
|
||||
bit_format = 16bit
|
||||
channels = stereo
|
||||
|
||||
[smoothing]
|
||||
gravity = 200
|
||||
integral = 70
|
||||
ignore = 0
|
||||
monstercat = 1
|
||||
|
||||
[eq]
|
||||
1 = 1.00
|
||||
2 = 1.00
|
||||
3 = 1.00
|
||||
4 = 1.00
|
||||
5 = 1.00
|
||||
6 = 1.00
|
||||
7 = 1.00
|
||||
8 = 1.00
|
||||
9 = 1.00
|
||||
@@ -0,0 +1,81 @@
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _configure_sys_path_for_direct_execution():
|
||||
"""
|
||||
Ajusta sys.path si este script se ejecuta directamente,
|
||||
para asegurar que las importaciones relativas dentro del paquete 'config' funcionen.
|
||||
Esto permite ejecutar `python config/config.py` desde cualquier directorio.
|
||||
"""
|
||||
if __name__ == "__main__":
|
||||
current_file_dir = Path(__file__).resolve().parent
|
||||
project_root = current_file_dir.parent
|
||||
|
||||
if str(project_root) not in sys.path:
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
_configure_sys_path_for_direct_execution()
|
||||
|
||||
import shutil
|
||||
|
||||
from fabric import Application
|
||||
|
||||
if __name__ == "__main__" and not __package__:
|
||||
from config.data import APP_NAME, APP_NAME_CAP
|
||||
from config.settings_gui import HyprConfGUI
|
||||
from config.settings_utils import load_bind_vars
|
||||
else:
|
||||
from .data import APP_NAME, APP_NAME_CAP
|
||||
from .settings_gui import HyprConfGUI
|
||||
from .settings_utils import load_bind_vars
|
||||
|
||||
|
||||
def open_config():
|
||||
"""
|
||||
Entry point for opening the configuration GUI using Fabric Application.
|
||||
"""
|
||||
load_bind_vars()
|
||||
|
||||
show_lock_checkbox = True
|
||||
dest_lock = os.path.expanduser("~/.config/hypr/hyprlock.conf")
|
||||
src_lock = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/config/hypr/hyprlock.conf")
|
||||
if not os.path.exists(dest_lock) and os.path.exists(src_lock):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(dest_lock), exist_ok=True)
|
||||
shutil.copy(src_lock, dest_lock)
|
||||
show_lock_checkbox = False
|
||||
print(f"Copied default hyprlock config to {dest_lock}")
|
||||
except Exception as e:
|
||||
print(f"Error copying default hyprlock config: {e}")
|
||||
show_lock_checkbox = os.path.exists(src_lock)
|
||||
|
||||
show_idle_checkbox = True
|
||||
dest_idle = os.path.expanduser("~/.config/hypr/hypridle.conf")
|
||||
src_idle = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/config/hypr/hypridle.conf")
|
||||
if not os.path.exists(dest_idle) and os.path.exists(src_idle):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(dest_idle), exist_ok=True)
|
||||
shutil.copy(src_idle, dest_idle)
|
||||
show_idle_checkbox = False
|
||||
print(f"Copied default hypridle config to {dest_idle}")
|
||||
except Exception as e:
|
||||
print(f"Error copying default hypridle config: {e}")
|
||||
show_idle_checkbox = os.path.exists(src_idle)
|
||||
|
||||
app = Application(f"{APP_NAME}-settings")
|
||||
window = HyprConfGUI(
|
||||
show_lock_checkbox=show_lock_checkbox,
|
||||
show_idle_checkbox=show_idle_checkbox,
|
||||
application=app,
|
||||
on_destroy=lambda *_: app.quit()
|
||||
)
|
||||
app.add_window(window)
|
||||
|
||||
window.show_all()
|
||||
app.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
open_config()
|
||||
@@ -0,0 +1,106 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from fabric.utils.helpers import get_relative_path
|
||||
from gi.repository import Gdk, GLib
|
||||
|
||||
APP_NAME_CAP = "Ax-Shell"
|
||||
APP_NAME = APP_NAME_CAP.lower()
|
||||
|
||||
CACHE_DIR = str(GLib.get_user_cache_dir()) + f"/{APP_NAME}"
|
||||
|
||||
USERNAME = os.getlogin()
|
||||
HOSTNAME = os.uname().nodename
|
||||
HOME_DIR = os.path.expanduser("~")
|
||||
|
||||
CONFIG_DIR = os.path.expanduser(f"~/.config/{APP_NAME}")
|
||||
|
||||
screen = Gdk.Screen.get_default()
|
||||
CURRENT_WIDTH = screen.get_width()
|
||||
CURRENT_HEIGHT = screen.get_height()
|
||||
|
||||
CONFIG_FILE = get_relative_path("../config/config.json")
|
||||
MATUGEN_STATE_FILE = os.path.join(CONFIG_DIR, "matugen")
|
||||
|
||||
|
||||
def load_config():
|
||||
"""Load the configuration from config.json"""
|
||||
config_path = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/config/config.json")
|
||||
config = {}
|
||||
|
||||
if os.path.exists(config_path):
|
||||
try:
|
||||
with open(config_path, "r") as f:
|
||||
config = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading config: {e}")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
# Import defaults from settings_constants to avoid duplication
|
||||
from .settings_constants import DEFAULTS
|
||||
|
||||
# Load configuration once and use throughout the module
|
||||
config = {}
|
||||
if os.path.exists(CONFIG_FILE):
|
||||
try:
|
||||
with open(CONFIG_FILE, "r") as f:
|
||||
config = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error loading config file: {e}")
|
||||
|
||||
|
||||
def get_default(setting_str: str):
|
||||
return DEFAULTS[setting_str] if setting_str in DEFAULTS else ""
|
||||
|
||||
|
||||
def _get_config_var(setting_str: str):
|
||||
return config.get(setting_str, get_default(setting_str))
|
||||
|
||||
|
||||
# Set configuration values using defaults from settings_constants
|
||||
WALLPAPERS_DIR = _get_config_var("wallpapers_dir")
|
||||
BAR_POSITION = _get_config_var("bar_position")
|
||||
VERTICAL = BAR_POSITION in ["Left", "Right"]
|
||||
CENTERED_BAR = _get_config_var("centered_bar")
|
||||
DATETIME_12H_FORMAT = _get_config_var("datetime_12h_format")
|
||||
TERMINAL_COMMAND = _get_config_var("terminal_command")
|
||||
DOCK_ENABLED = _get_config_var("dock_enabled")
|
||||
DOCK_ALWAYS_SHOW = _get_config_var("dock_always_show")
|
||||
DOCK_ICON_SIZE = _get_config_var("dock_icon_size")
|
||||
BAR_WORKSPACE_SHOW_NUMBER = _get_config_var("bar_workspace_show_number")
|
||||
BAR_WORKSPACE_USE_CHINESE_NUMERALS = _get_config_var(
|
||||
"bar_workspace_use_chinese_numerals"
|
||||
)
|
||||
BAR_HIDE_SPECIAL_WORKSPACE = _get_config_var("bar_hide_special_workspace")
|
||||
BAR_THEME = _get_config_var("bar_theme")
|
||||
DOCK_THEME = _get_config_var("dock_theme")
|
||||
PANEL_THEME = _get_config_var("panel_theme")
|
||||
PANEL_POSITION = _get_config_var("panel_position")
|
||||
NOTIF_POS = _get_config_var("notif_pos")
|
||||
|
||||
BAR_COMPONENTS_VISIBILITY = {
|
||||
"button_apps": _get_config_var("bar_button_apps_visible"),
|
||||
"systray": _get_config_var("bar_systray_visible"),
|
||||
"control": _get_config_var("bar_control_visible"),
|
||||
"network": _get_config_var("bar_network_visible"),
|
||||
"button_tools": _get_config_var("bar_button_tools_visible"),
|
||||
"sysprofiles": _get_config_var("bar_sysprofiles_visible"),
|
||||
"button_overview": _get_config_var("bar_button_overview_visible"),
|
||||
"ws_container": _get_config_var("bar_ws_container_visible"),
|
||||
"weather": _get_config_var("bar_weather_visible"),
|
||||
"battery": _get_config_var("bar_battery_visible"),
|
||||
"metrics": _get_config_var("bar_metrics_visible"),
|
||||
"language": _get_config_var("bar_language_visible"),
|
||||
"date_time": _get_config_var("bar_date_time_visible"),
|
||||
"button_power": _get_config_var("bar_button_power_visible"),
|
||||
}
|
||||
|
||||
BAR_METRICS_DISKS = _get_config_var("bar_metrics_disks")
|
||||
METRICS_VISIBLE = _get_config_var("metrics_visible")
|
||||
METRICS_SMALL_VISIBLE = _get_config_var("metrics_small_visible")
|
||||
SELECTED_MONITORS = _get_config_var("selected_monitors")
|
||||
@@ -0,0 +1,27 @@
|
||||
general {
|
||||
lock_cmd = pidof hyprlock || hyprlock # avoid starting multiple hyprlock instances.
|
||||
before_sleep_cmd = loginctl lock-session # lock before suspend.
|
||||
after_sleep_cmd = hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display.
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 150 # 2.5min.
|
||||
on-timeout = brightnessctl -s set 10 # set monitor backlight to minimum, avoid 0 on OLED monitor.
|
||||
on-resume = brightnessctl -r # monitor backlight restore.
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 300 # 5min
|
||||
on-timeout = loginctl lock-session # lock screen when timeout has passed
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 330 # 5.5min
|
||||
on-timeout = hyprctl dispatch dpms off # screen off when timeout has passed
|
||||
on-resume = hyprctl dispatch dpms on # screen on when activity is detected after timeout has fired.
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 1800 # 30min
|
||||
on-timeout = systemctl suspend # suspend pc
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
source = ~/.config/Ax-Shell/config/hypr/colors.conf
|
||||
|
||||
# BACKGROUND
|
||||
background {
|
||||
monitor =
|
||||
path = ~/.current.wall #path to background image
|
||||
blur_passes = 3
|
||||
blur_size = 3
|
||||
contrast = 1.0
|
||||
brightness = 0.5
|
||||
vibrancy = 0.0
|
||||
vibrancy_darkness = 0.0
|
||||
}
|
||||
|
||||
# GENERAL
|
||||
general {
|
||||
grace = 0
|
||||
hide_cursor = true
|
||||
}
|
||||
|
||||
# INPUT FIELD
|
||||
input-field {
|
||||
monitor =
|
||||
size = 256, 48
|
||||
outline_thickness = 0
|
||||
dots_size = 0.2 # Scale of input-field height, 0.2 - 0.8
|
||||
dots_spacing = 0.5 # Scale of dots' absolute size, 0.0 - 1.0
|
||||
dots_center = true
|
||||
outer_color = rgba(00000000)
|
||||
inner_color = rgba(0, 0, 0, 1)
|
||||
font_color = rgb($foreground)
|
||||
fail_color = rgb($error)
|
||||
check_color = rgb($tertiary)
|
||||
capslock_color = rgb($secondary)
|
||||
fade_on_empty = false
|
||||
font_family = Iosevka Nerd Font
|
||||
placeholder_text = ... #text for input password
|
||||
hide_input = false
|
||||
position = 0, -100
|
||||
halign = center
|
||||
valign = center
|
||||
shadow_passes = 1
|
||||
shadow_size = 5
|
||||
shadow_boost = 0.5
|
||||
}
|
||||
|
||||
# TIME
|
||||
label {
|
||||
monitor =
|
||||
text = cmd[update:1000] echo "$(date +"%H:%M:%S")"
|
||||
color = rgb($foreground)
|
||||
font_size = 14
|
||||
font_family = Iosevka Nerd Font Bold
|
||||
position = 0, -150
|
||||
halign = center
|
||||
valign = center
|
||||
shadow_passes = 1
|
||||
shadow_size = 5
|
||||
shadow_boost = 0.5
|
||||
}
|
||||
|
||||
# USER
|
||||
label {
|
||||
monitor =
|
||||
text = cmd[update:1000] echo "$USER@$(hostname)"
|
||||
color = rgb($foreground)
|
||||
font_size = 14
|
||||
font_family = Iosevka Nerd Font Bold Italic
|
||||
position = 0, -50
|
||||
halign = center
|
||||
valign = center
|
||||
shadow_passes = 1
|
||||
shadow_size = 5
|
||||
shadow_boost = 0.5
|
||||
}
|
||||
|
||||
# PICTURE
|
||||
image {
|
||||
path = .face.icon
|
||||
size = 200
|
||||
position = 0, 75
|
||||
halign = center
|
||||
valign = center
|
||||
border_size = 3
|
||||
border_color = rgb($primary)
|
||||
shadow_passes = 1
|
||||
shadow_size = 5
|
||||
shadow_boost = 0.5
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
:vars {
|
||||
--foreground: {{colors.on_background.default.hex}};
|
||||
--background: {{colors.background.default.hex}};
|
||||
--cursor: {{colors.on_background.default.hex}};
|
||||
--primary: {{colors.primary.default.hex}};
|
||||
--on-primary: {{colors.on_primary.default.hex}};
|
||||
--secondary: {{colors.secondary.default.hex}};
|
||||
--on-secondary: {{colors.on_secondary.default.hex}};
|
||||
--tertiary: {{colors.tertiary.default.hex}};
|
||||
--on-tertiary: {{colors.on_tertiary.default.hex}};
|
||||
--surface: {{colors.surface.default.hex}};
|
||||
--surface-bright: {{colors.surface_bright.default.hex}};
|
||||
--error: {{colors.error.default.hex}};
|
||||
--error-dim: {{colors.error.default.hex | set_lightness: -10.0}};
|
||||
--on-error: {{colors.on_error.default.hex}};
|
||||
--error-container: {{colors.error_container.default.hex}};
|
||||
--outline: {{colors.outline.default.hex}};
|
||||
--shadow: {{colors.shadow.default.hex}};
|
||||
--red: {{colors.red.default.hex}};
|
||||
--red-dim: {{colors.red.default.hex | set_lightness: -10.0}};
|
||||
--green: {{colors.green.default.hex}};
|
||||
--green-dim: {{colors.green.default.hex | set_lightness: -10.0}};
|
||||
--yellow: {{colors.yellow.default.hex}};
|
||||
--yellow-dim: {{colors.yellow.default.hex | set_lightness: -10.0}};
|
||||
--blue: {{colors.blue.default.hex}};
|
||||
--blue-dim: {{colors.blue.default.hex | set_lightness: -10.0}};
|
||||
--magenta: {{colors.magenta.default.hex}};
|
||||
--magenta-dim: {{colors.magenta.default.hex | set_lightness: -10.0}};
|
||||
--cyan: {{colors.cyan.default.hex}};
|
||||
--cyan-dim: {{colors.cyan.default.hex | set_lightness: -10.0}};
|
||||
--white: {{colors.white.default.hex}};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
$wallpaper = {{image}}
|
||||
$background = {{colors.background.default.hex_stripped}}
|
||||
$foreground = {{colors.on_background.default.hex_stripped}}
|
||||
|
||||
$primary = {{colors.primary.default.hex_stripped}}
|
||||
$secondary = {{colors.secondary.default.hex_stripped}}
|
||||
$tertiary = {{colors.tertiary.default.hex_stripped}}
|
||||
$surface = {{colors.surface.default.hex_stripped}}
|
||||
$surface_bright = {{colors.surface_bright.default.hex_stripped}}
|
||||
$outline = {{colors.outline.default.hex_stripped}}
|
||||
$error = {{colors.error.default.hex_stripped | set_lightness: -20.0}}
|
||||
|
||||
$shadow = {{colors.shadow.default.hex_stripped}}
|
||||
|
||||
$red = {{colors.red.default.hex_stripped}}
|
||||
$green = {{colors.green.default.hex_stripped}}
|
||||
$yellow = {{colors.yellow.default.hex_stripped}}
|
||||
$blue = {{colors.blue.default.hex_stripped}}
|
||||
$magenta = {{colors.magenta.default.hex_stripped}}
|
||||
$cyan = {{colors.cyan.default.hex_stripped}}
|
||||
$white = {{colors.white.default.hex_stripped}}
|
||||
$red_dim = {{colors.red.default.hex_stripped | set_lightness: -20.0}}
|
||||
$green_dim = {{colors.green.default.hex_stripped | set_lightness: -20.0}}
|
||||
$yellow_dim = {{colors.yellow.default.hex_stripped | set_lightness: -20.0}}
|
||||
$blue_dim = {{colors.blue.default.hex_stripped | set_lightness: -20.0}}
|
||||
$magenta_dim = {{colors.magenta.default.hex_stripped | set_lightness: -20.0}}
|
||||
$cyan_dim = {{colors.cyan.default.hex_stripped | set_lightness: -20.0}}
|
||||
@@ -0,0 +1,103 @@
|
||||
from fabric.utils.helpers import get_relative_path
|
||||
|
||||
from .data import (
|
||||
APP_NAME,
|
||||
APP_NAME_CAP,
|
||||
)
|
||||
|
||||
SOURCE_STRING = f"""
|
||||
# {APP_NAME_CAP}
|
||||
source = ~/.config/{APP_NAME_CAP}/config/hypr/{APP_NAME}.conf
|
||||
"""
|
||||
|
||||
DEFAULTS = {
|
||||
"prefix_restart": "SUPER ALT",
|
||||
"suffix_restart": "B",
|
||||
"prefix_axmsg": "SUPER",
|
||||
"suffix_axmsg": "A",
|
||||
"prefix_dash": "SUPER",
|
||||
"suffix_dash": "D",
|
||||
"prefix_bluetooth": "SUPER",
|
||||
"suffix_bluetooth": "B",
|
||||
"prefix_pins": "SUPER",
|
||||
"suffix_pins": "Q",
|
||||
"prefix_kanban": "SUPER",
|
||||
"suffix_kanban": "N",
|
||||
"prefix_launcher": "SUPER",
|
||||
"suffix_launcher": "R",
|
||||
"prefix_tmux": "SUPER",
|
||||
"suffix_tmux": "T",
|
||||
"prefix_cliphist": "SUPER",
|
||||
"suffix_cliphist": "V",
|
||||
"prefix_toolbox": "SUPER",
|
||||
"suffix_toolbox": "S",
|
||||
"prefix_overview": "SUPER",
|
||||
"suffix_overview": "TAB",
|
||||
"prefix_wallpapers": "SUPER",
|
||||
"suffix_wallpapers": "COMMA",
|
||||
"prefix_randwall": "SUPER SHIFT",
|
||||
"suffix_randwall": "COMMA",
|
||||
"prefix_mixer": "SUPER",
|
||||
"suffix_mixer": "M",
|
||||
"prefix_emoji": "SUPER",
|
||||
"suffix_emoji": "PERIOD",
|
||||
"prefix_power": "SUPER",
|
||||
"suffix_power": "ESCAPE",
|
||||
"prefix_caffeine": "SUPER SHIFT",
|
||||
"suffix_caffeine": "M",
|
||||
"prefix_toggle": "SUPER CTRL",
|
||||
"suffix_toggle": "B",
|
||||
"prefix_css": "SUPER SHIFT",
|
||||
"suffix_css": "B",
|
||||
"wallpapers_dir": get_relative_path("../assets/wallpapers_example"),
|
||||
"prefix_restart_inspector": "SUPER CTRL ALT",
|
||||
"suffix_restart_inspector": "B",
|
||||
"bar_position": "Top",
|
||||
"vertical": False,
|
||||
"centered_bar": False,
|
||||
"datetime_12h_format": False,
|
||||
"terminal_command": "kitty -e",
|
||||
"auto_append_hyprland": True,
|
||||
"dock_enabled": True,
|
||||
"dock_icon_size": 28,
|
||||
"dock_always_show": False,
|
||||
"bar_workspace_show_number": False,
|
||||
"bar_workspace_use_chinese_numerals": False,
|
||||
"bar_hide_special_workspace": True,
|
||||
"bar_theme": "Pills",
|
||||
"dock_theme": "Pills",
|
||||
"panel_theme": "Notch",
|
||||
"panel_position": "Center",
|
||||
"notif_pos": "Top",
|
||||
"bar_button_apps_visible": True,
|
||||
"bar_systray_visible": True,
|
||||
"bar_control_visible": True,
|
||||
"bar_network_visible": True,
|
||||
"bar_button_tools_visible": True,
|
||||
"bar_sysprofiles_visible": True,
|
||||
"bar_button_overview_visible": True,
|
||||
"bar_ws_container_visible": True,
|
||||
"bar_weather_visible": True,
|
||||
"bar_battery_visible": True,
|
||||
"bar_metrics_visible": True,
|
||||
"bar_language_visible": True,
|
||||
"bar_date_time_visible": True,
|
||||
"bar_button_power_visible": True,
|
||||
"corners_visible": True,
|
||||
"bar_metrics_disks": ["/"],
|
||||
"metrics_visible": {
|
||||
"cpu": True,
|
||||
"ram": True,
|
||||
"disk": True,
|
||||
"gpu": True,
|
||||
},
|
||||
"metrics_small_visible": {
|
||||
"cpu": True,
|
||||
"ram": True,
|
||||
"disk": True,
|
||||
"gpu": True,
|
||||
},
|
||||
"limited_apps_history": ["Spotify"],
|
||||
"history_ignored_apps": ["Hyprshot"],
|
||||
"selected_monitors": [],
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,405 @@
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import gi
|
||||
import toml
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from fabric.utils.helpers import exec_shell_command_async
|
||||
from gi.repository import GLib
|
||||
|
||||
# Importar settings_constants para DEFAULTS
|
||||
from . import settings_constants
|
||||
from .data import ( # CONFIG_DIR, HOME_DIR no se usan aquí directamente
|
||||
APP_NAME,
|
||||
APP_NAME_CAP,
|
||||
get_default,
|
||||
)
|
||||
|
||||
# Global variable to store binding variables, managed by this module
|
||||
bind_vars = {} # Se inicializa vacío, load_bind_vars lo poblará
|
||||
|
||||
|
||||
def get_bind_var(setting_str: str):
|
||||
return bind_vars.get(setting_str, get_default(setting_str))
|
||||
|
||||
|
||||
def deep_update(target: dict, update: dict) -> dict:
|
||||
"""
|
||||
Recursively update a nested dictionary with values from another dictionary.
|
||||
Modifies target in-place.
|
||||
"""
|
||||
for key, value in update.items():
|
||||
if isinstance(value, dict) and key in target and isinstance(target[key], dict):
|
||||
# Si el valor es un diccionario y la clave ya existe en target como diccionario,
|
||||
# entonces actualiza recursivamente.
|
||||
deep_update(target[key], value)
|
||||
else:
|
||||
# De lo contrario, simplemente establece/sobrescribe el valor.
|
||||
target[key] = value
|
||||
return target # Aunque modifica in-place, devolverlo es una convención común
|
||||
|
||||
|
||||
def ensure_matugen_config():
|
||||
"""
|
||||
Ensure that the matugen configuration file exists and is updated
|
||||
with the expected settings.
|
||||
"""
|
||||
expected_config = {
|
||||
"config": {
|
||||
"reload_apps": True,
|
||||
"wallpaper": {
|
||||
"command": "awww",
|
||||
"arguments": [
|
||||
"img",
|
||||
"-t",
|
||||
"fade",
|
||||
"--transition-duration",
|
||||
"0.5",
|
||||
"--transition-step",
|
||||
"255",
|
||||
"--transition-fps",
|
||||
"60",
|
||||
"-f",
|
||||
"Nearest",
|
||||
],
|
||||
"set": True,
|
||||
},
|
||||
"custom_colors": {
|
||||
"red": {"color": "#FF0000", "blend": True},
|
||||
"green": {"color": "#00FF00", "blend": True},
|
||||
"yellow": {"color": "#FFFF00", "blend": True},
|
||||
"blue": {"color": "#0000FF", "blend": True},
|
||||
"magenta": {"color": "#FF00FF", "blend": True},
|
||||
"cyan": {"color": "#00FFFF", "blend": True},
|
||||
"white": {"color": "#FFFFFF", "blend": True},
|
||||
},
|
||||
},
|
||||
"templates": {
|
||||
"hyprland": {
|
||||
"input_path": f"~/.config/{APP_NAME_CAP}/config/matugen/templates/hyprland-colors.conf",
|
||||
"output_path": f"~/.config/{APP_NAME_CAP}/config/hypr/colors.conf",
|
||||
},
|
||||
f"{APP_NAME}": {
|
||||
"input_path": f"~/.config/{APP_NAME_CAP}/config/matugen/templates/{APP_NAME}.css",
|
||||
"output_path": f"~/.config/{APP_NAME_CAP}/styles/colors.css",
|
||||
"post_hook": f"fabric-cli exec {APP_NAME} 'app.set_css()' &",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
config_path = os.path.expanduser("~/.config/matugen/config.toml")
|
||||
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||
|
||||
existing_config = {}
|
||||
if os.path.exists(config_path):
|
||||
try:
|
||||
with open(config_path, "r") as f:
|
||||
existing_config = toml.load(f)
|
||||
shutil.copyfile(config_path, config_path + ".bak")
|
||||
except toml.TomlDecodeError:
|
||||
print(
|
||||
f"Warning: Could not decode TOML from {config_path}. A new default config will be created."
|
||||
)
|
||||
existing_config = {} # Resetear si está corrupto
|
||||
except Exception as e:
|
||||
print(f"Error reading or backing up {config_path}: {e}")
|
||||
# existing_config podría estar parcialmente cargado o vacío.
|
||||
# Continuar para intentar fusionar con defaults.
|
||||
|
||||
# Usamos una copia de existing_config para deep_update si no queremos modificarlo directamente
|
||||
# o asegurarse que deep_update no lo haga si no es deseado.
|
||||
# La implementación actual de deep_update modifica 'target'.
|
||||
# Para ser más seguros, podemos pasar una copia si existing_config no debe cambiar.
|
||||
# merged_config = deep_update(existing_config.copy(), expected_config)
|
||||
# O si existing_config puede ser modificado:
|
||||
merged_config = deep_update(
|
||||
existing_config, expected_config
|
||||
) # existing_config se modifica in-place
|
||||
|
||||
try:
|
||||
with open(config_path, "w") as f:
|
||||
toml.dump(merged_config, f)
|
||||
except Exception as e:
|
||||
print(f"Error writing matugen config to {config_path}: {e}")
|
||||
|
||||
current_wall = os.path.expanduser("~/.current.wall")
|
||||
hypr_colors = os.path.expanduser(
|
||||
f"~/.config/{APP_NAME_CAP}/config/hypr/colors.conf"
|
||||
)
|
||||
css_colors = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/styles/colors.css")
|
||||
|
||||
if (
|
||||
not os.path.exists(current_wall)
|
||||
or not os.path.exists(hypr_colors)
|
||||
or not os.path.exists(css_colors)
|
||||
):
|
||||
os.makedirs(os.path.dirname(hypr_colors), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(css_colors), exist_ok=True)
|
||||
|
||||
image_path = ""
|
||||
if not os.path.exists(current_wall):
|
||||
example_wallpaper_path = os.path.expanduser(
|
||||
f"~/.config/{APP_NAME_CAP}/assets/wallpapers_example/example-1.jpg"
|
||||
)
|
||||
if os.path.exists(example_wallpaper_path):
|
||||
try:
|
||||
# Si ya existe (posiblemente un enlace roto o archivo regular), eliminar y re-enlazar
|
||||
if os.path.lexists(
|
||||
current_wall
|
||||
): # lexists para no seguir el enlace si es uno
|
||||
os.remove(current_wall)
|
||||
os.symlink(example_wallpaper_path, current_wall)
|
||||
image_path = example_wallpaper_path
|
||||
except Exception as e:
|
||||
print(f"Error creating symlink for wallpaper: {e}")
|
||||
else:
|
||||
image_path = (
|
||||
os.path.realpath(current_wall)
|
||||
if os.path.islink(current_wall)
|
||||
else current_wall
|
||||
)
|
||||
|
||||
if image_path and os.path.exists(image_path):
|
||||
print(f"Generating color theme from wallpaper: {image_path}")
|
||||
try:
|
||||
matugen_cmd = f"matugen image '{image_path}'"
|
||||
exec_shell_command_async(matugen_cmd)
|
||||
print("Matugen color theme generation initiated.")
|
||||
except FileNotFoundError:
|
||||
print("Error: matugen command not found. Please install matugen.")
|
||||
except Exception as e:
|
||||
print(f"Error initiating matugen: {e}")
|
||||
elif not image_path:
|
||||
print(
|
||||
"Warning: No wallpaper path determined to generate matugen theme from."
|
||||
)
|
||||
else: # image_path existe pero el archivo no
|
||||
print(
|
||||
f"Warning: Wallpaper at {image_path} not found. Cannot generate matugen theme."
|
||||
)
|
||||
|
||||
|
||||
def load_bind_vars():
|
||||
"""
|
||||
Load saved key binding variables from JSON, if available.
|
||||
Populates the global `bind_vars` in-place.
|
||||
"""
|
||||
global bind_vars # Necesario para modificar el objeto global bind_vars
|
||||
|
||||
# 1. Limpiar el diccionario bind_vars existente.
|
||||
bind_vars.clear()
|
||||
# 2. Actualizarlo con una copia de DEFAULTS.
|
||||
bind_vars.update(
|
||||
settings_constants.DEFAULTS.copy()
|
||||
) # Usar .copy() para no modificar DEFAULTS accidentalmente
|
||||
|
||||
config_json = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/config/config.json")
|
||||
if os.path.exists(config_json):
|
||||
try:
|
||||
with open(config_json, "r") as f:
|
||||
saved_vars = json.load(f)
|
||||
# 3. Usar deep_update para fusionar saved_vars en el bind_vars existente.
|
||||
deep_update(bind_vars, saved_vars)
|
||||
|
||||
# La lógica para asegurar la estructura de diccionarios anidados
|
||||
# como 'metrics_visible' y 'metrics_small_visible'
|
||||
# debe operar sobre el 'bind_vars' ya actualizado.
|
||||
for vis_key in ["metrics_visible", "metrics_small_visible"]:
|
||||
# Asegurar que la clave exista en DEFAULTS como referencia de estructura
|
||||
if vis_key in settings_constants.DEFAULTS:
|
||||
default_sub_dict = settings_constants.DEFAULTS[vis_key]
|
||||
# Si la clave no está en bind_vars o no es un diccionario después de deep_update,
|
||||
# restaurarla desde una copia de DEFAULTS para esa clave.
|
||||
if not isinstance(bind_vars.get(vis_key), dict):
|
||||
bind_vars[vis_key] = default_sub_dict.copy()
|
||||
else:
|
||||
# Si es un diccionario, asegurar que todas las sub-claves de DEFAULTS estén presentes.
|
||||
current_sub_dict = bind_vars[vis_key]
|
||||
for m_key, m_val in default_sub_dict.items():
|
||||
if m_key not in current_sub_dict:
|
||||
current_sub_dict[m_key] = m_val
|
||||
except json.JSONDecodeError:
|
||||
print(
|
||||
f"Warning: Could not decode JSON from {config_json}. Using defaults (already initialized)."
|
||||
)
|
||||
# bind_vars ya está poblado con DEFAULTS, no se necesita acción adicional aquí.
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Error loading config from {config_json}: {e}. Using defaults (already initialized)."
|
||||
)
|
||||
# bind_vars ya está poblado con DEFAULTS.
|
||||
# else:
|
||||
# Si config_json no existe, bind_vars ya está poblado con DEFAULTS.
|
||||
# print(f"Config file {config_json} not found. Using defaults (already initialized).")
|
||||
|
||||
|
||||
def generate_hyprconf() -> str:
|
||||
"""
|
||||
Generate the Hypr configuration string using the current bind_vars.
|
||||
"""
|
||||
home = os.path.expanduser("~")
|
||||
# Determine animation type based on bar position
|
||||
bar_position = get_bind_var("bar_position")
|
||||
is_vertical = bar_position in ["Left", "Right"]
|
||||
animation_type = "slidefadevert" if is_vertical else "slidefade"
|
||||
|
||||
return f"""exec-once = uwsm-app $(python {home}/.config/{APP_NAME_CAP}/main.py)
|
||||
exec = pgrep -x "hypridle" > /dev/null || uwsm app -- hypridle
|
||||
exec = uwsm app -- awww-daemon
|
||||
exec-once = wl-paste --type text --watch cliphist store
|
||||
exec-once = wl-paste --type image --watch cliphist store
|
||||
|
||||
$fabricSend = fabric-cli exec {APP_NAME}
|
||||
$axMessage = notify-send "Axenide" "FIRE IN THE HOLE‼️🗣️🔥🕳️" -i "{home}/.config/{APP_NAME_CAP}/assets/ax.png" -A "🗣️" -A "🔥" -A "🕳️" -a "Source Code"
|
||||
|
||||
bind = {get_bind_var("prefix_restart")}, {get_bind_var("suffix_restart")}, exec, killall {APP_NAME}; uwsm-app $(python {home}/.config/{APP_NAME_CAP}/main.py) # Reload {APP_NAME_CAP}
|
||||
bind = {get_bind_var("prefix_axmsg")}, {get_bind_var("suffix_axmsg")}, exec, $axMessage # Message
|
||||
bind = {get_bind_var("prefix_dash")}, {get_bind_var("suffix_dash")}, exec, $fabricSend 'notch.open_notch("dashboard")' # Dashboard
|
||||
bind = {get_bind_var("prefix_bluetooth")}, {get_bind_var("suffix_bluetooth")}, exec, $fabricSend 'notch.open_notch("bluetooth")' # Bluetooth
|
||||
bind = {get_bind_var("prefix_pins")}, {get_bind_var("suffix_pins")}, exec, $fabricSend 'notch.open_notch("pins")' # Pins
|
||||
bind = {get_bind_var("prefix_kanban")}, {get_bind_var("suffix_kanban")}, exec, $fabricSend 'notch.open_notch("kanban")' # Kanban
|
||||
bind = {get_bind_var("prefix_launcher")}, {get_bind_var("suffix_launcher")}, exec, $fabricSend 'notch.open_notch("launcher")' # App Launcher
|
||||
bind = {get_bind_var("prefix_tmux")}, {get_bind_var("suffix_tmux")}, exec, $fabricSend 'notch.open_notch("tmux")' # Tmux
|
||||
bind = {get_bind_var("prefix_cliphist")}, {get_bind_var("suffix_cliphist")}, exec, $fabricSend 'notch.open_notch("cliphist")' # Clipboard History
|
||||
bind = {get_bind_var("prefix_toolbox")}, {get_bind_var("suffix_toolbox")}, exec, $fabricSend 'notch.open_notch("tools")' # Toolbox
|
||||
bind = {get_bind_var("prefix_overview")}, {get_bind_var("suffix_overview")}, exec, $fabricSend 'notch.open_notch("overview")' # Overview
|
||||
bind = {get_bind_var("prefix_wallpapers")}, {get_bind_var("suffix_wallpapers")}, exec, $fabricSend 'notch.open_notch("wallpapers")' # Wallpapers
|
||||
bind = {get_bind_var("prefix_randwall")}, {get_bind_var("suffix_randwall")}, exec, $fabricSend 'notch.dashboard.wallpapers.set_random_wallpaper(None, external=True)' # Random Wallpaper
|
||||
bind = {get_bind_var("prefix_mixer")}, {get_bind_var("suffix_mixer")}, exec, $fabricSend 'notch.open_notch("mixer")' # Audio Mixer
|
||||
bind = {get_bind_var("prefix_emoji")}, {get_bind_var("suffix_emoji")}, exec, $fabricSend 'notch.open_notch("emoji")' # Emoji Picker
|
||||
bind = {get_bind_var("prefix_power")}, {get_bind_var("suffix_power")}, exec, $fabricSend 'notch.open_notch("power")' # Power Menu
|
||||
bind = {get_bind_var("prefix_caffeine")}, {get_bind_var("suffix_caffeine")}, exec, $fabricSend 'notch.dashboard.widgets.buttons.caffeine_button.toggle_inhibit(external=True)' # Toggle Caffeine
|
||||
bind = {get_bind_var("prefix_toggle")}, {get_bind_var("suffix_toggle")}, exec, $fabricSend 'from utils.global_keybinds import get_global_keybind_handler; get_global_keybind_handler().toggle_bar()' # Toggle Bar
|
||||
bind = {get_bind_var("prefix_css")}, {get_bind_var("suffix_css")}, exec, $fabricSend 'app.set_css()' # Reload CSS
|
||||
bind = {get_bind_var("prefix_restart_inspector")}, {get_bind_var("suffix_restart_inspector")}, exec, killall {APP_NAME}; uwsm-app $(GTK_DEBUG=interactive python {home}/.config/{APP_NAME_CAP}/main.py) # Restart with inspector
|
||||
|
||||
# Wallpapers directory: {get_bind_var("wallpapers_dir")}
|
||||
|
||||
source = {home}/.config/{APP_NAME_CAP}/config/hypr/colors.conf
|
||||
|
||||
layerrule = noanim, fabric
|
||||
|
||||
exec = cp $wallpaper ~/.current.wall
|
||||
|
||||
general {{
|
||||
col.active_border = rgb($primary)
|
||||
col.inactive_border = rgb($surface)
|
||||
gaps_in = 2
|
||||
gaps_out = 4
|
||||
border_size = 2
|
||||
layout = dwindle
|
||||
}}
|
||||
|
||||
cursor {{
|
||||
no_warps=true
|
||||
}}
|
||||
|
||||
decoration {{
|
||||
blur {{
|
||||
enabled = yes
|
||||
size = 1
|
||||
passes = 3
|
||||
new_optimizations = yes
|
||||
contrast = 1
|
||||
brightness = 1
|
||||
}}
|
||||
rounding = 14
|
||||
shadow {{
|
||||
enabled = true
|
||||
range = 10
|
||||
render_power = 2
|
||||
color = rgba(0, 0, 0, 0.25)
|
||||
}}
|
||||
}}
|
||||
|
||||
animations {{
|
||||
enabled = yes
|
||||
bezier = myBezier, 0.4, 0.0, 0.2, 1.0
|
||||
animation = windows, 1, 2.5, myBezier, popin 80%
|
||||
animation = border, 1, 2.5, myBezier
|
||||
animation = fade, 1, 2.5, myBezier
|
||||
animation = workspaces, 1, 2.5, myBezier, {animation_type} 20%
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def ensure_face_icon():
|
||||
"""
|
||||
Ensure the face icon exists. If not, copy the default icon.
|
||||
"""
|
||||
face_icon_path = os.path.expanduser("~/.face.icon")
|
||||
default_icon_path = os.path.expanduser(
|
||||
f"~/.config/{APP_NAME_CAP}/assets/default.png"
|
||||
)
|
||||
if not os.path.exists(face_icon_path) and os.path.exists(default_icon_path):
|
||||
try:
|
||||
shutil.copy(default_icon_path, face_icon_path)
|
||||
except Exception as e:
|
||||
print(f"Error copying default face icon: {e}")
|
||||
|
||||
|
||||
def backup_and_replace(src: str, dest: str, config_name: str):
|
||||
"""
|
||||
Backup the existing configuration file and replace it with a new one.
|
||||
"""
|
||||
try:
|
||||
if os.path.exists(dest):
|
||||
backup_path = dest + ".bak"
|
||||
# Asegurarse que el directorio de backup existe si es diferente
|
||||
# os.makedirs(os.path.dirname(backup_path), exist_ok=True)
|
||||
shutil.copy(dest, backup_path)
|
||||
print(f"{config_name} config backed up to {backup_path}")
|
||||
os.makedirs(
|
||||
os.path.dirname(dest), exist_ok=True
|
||||
) # Ensure dest directory exists
|
||||
shutil.copy(src, dest)
|
||||
print(f"{config_name} config replaced from {src}")
|
||||
except Exception as e:
|
||||
print(f"Error backing up/replacing {config_name} config: {e}")
|
||||
|
||||
|
||||
def start_config():
|
||||
"""
|
||||
Run final configuration steps: ensure necessary configs, write the hyprconf, and reload.
|
||||
"""
|
||||
print(f"{time.time():.4f}: start_config: Ensuring matugen config...")
|
||||
ensure_matugen_config()
|
||||
print(f"{time.time():.4f}: start_config: Ensuring face icon...")
|
||||
ensure_face_icon()
|
||||
print(f"{time.time():.4f}: start_config: Generating hypr conf...")
|
||||
|
||||
hypr_config_dir = os.path.expanduser(f"~/.config/{APP_NAME_CAP}/config/hypr/")
|
||||
os.makedirs(hypr_config_dir, exist_ok=True)
|
||||
# Usar APP_NAME para el nombre del archivo .conf para que coincida con SOURCE_STRING corregido
|
||||
hypr_conf_path = os.path.join(hypr_config_dir, f"{APP_NAME}.conf")
|
||||
try:
|
||||
with open(hypr_conf_path, "w") as f:
|
||||
f.write(generate_hyprconf())
|
||||
print(f"Generated Hyprland config at {hypr_conf_path}")
|
||||
except Exception as e:
|
||||
print(f"Error writing Hyprland config: {e}")
|
||||
print(f"{time.time():.4f}: start_config: Finished generating hypr conf.")
|
||||
|
||||
print(f"{time.time():.4f}: start_config: Initiating hyprctl reload...")
|
||||
try:
|
||||
# subprocess.run(["hyprctl", "reload"], check=True, capture_output=True, text=True)
|
||||
exec_shell_command_async("hyprctl reload") # Mantener async para no bloquear
|
||||
print(
|
||||
f"{time.time():.4f}: start_config: Hyprland configuration reload initiated."
|
||||
)
|
||||
except FileNotFoundError:
|
||||
print("Error: hyprctl command not found. Cannot reload Hyprland.")
|
||||
except (
|
||||
subprocess.CalledProcessError
|
||||
) as e: # Si usáramos subprocess.run con check=True
|
||||
print(
|
||||
f"Error reloading Hyprland with hyprctl: {e}\nOutput:\n{e.stdout}\n{e.stderr}"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"An error occurred initiating hyprctl reload: {e}")
|
||||
print(f"{time.time():.4f}: start_config: Finished initiating hyprctl reload.")
|
||||
Reference in New Issue
Block a user