15 Commits

Author SHA1 Message Date
github-actions d1c8c8fc09 [CI] chore: update flake 2026-06-02 04:44:32 +00:00
2 * r + 2 * t ad533a0dd4 fix: only screenshot focused monitor in fs mode 2026-06-01 21:06:04 +10:00
İlyas ccd2712982 fix: Lua dispatcher compat (#112)
* fix: temporary Lua dispatcher compat for workspace dispatchers

* fix(resizer): add Lua dispatcher compat for window resize/move/float/center

* feat(theme): write current.lua for Hyprland Lua config, current.conf for hyprlang

* fix: align gen_lua format with #111

* refactor address review feedback
refactor(hypr,theme): address review feedback

- cache is_lua_config result to avoid redundant socket calls
- remove is_lua_config wrapper, rename _is_lua_config to is_lua_config
- move hypr import to top of theme.py
- use single line syntax in apply_hypr and apply_colours

* restore original specialws behavior and some formatting
2026-05-31 23:48:33 +10:00
github-actions 1ea661859d [CI] chore: update flake 2026-05-31 04:25:32 +00:00
github-actions 64a5507e74 [CI] chore: update flake 2026-05-26 04:08:39 +00:00
github-actions 7fa3fc1bd0 [CI] chore: update flake 2026-05-24 04:20:58 +00:00
github-actions 7f30062670 [CI] chore: update flake 2026-05-23 04:01:11 +00:00
github-actions 04d286eaff [CI] chore: update flake 2026-05-17 04:09:50 +00:00
github-actions 2ce6213698 [CI] chore: update flake 2026-05-13 03:57:27 +00:00
Zynix 4b3ffcd644 fix: defer DynamicScheme annotation evaluation (#110) 2026-05-11 16:48:11 +10:00
github-actions 2621724c55 [CI] chore: update flake 2026-05-10 04:03:00 +00:00
github-actions 7b8a4281aa [CI] chore: update flake 2026-05-07 03:45:02 +00:00
github-actions 7452974dc9 [CI] chore: update flake 2026-05-06 03:53:45 +00:00
github-actions 544b567668 [CI] chore: update flake 2026-05-05 03:33:35 +00:00
github-actions 1f523c7556 [CI] chore: update flake 2026-05-03 04:00:27 +00:00
6 changed files with 83 additions and 19 deletions
Generated
+10 -10
View File
@@ -9,11 +9,11 @@
"quickshell": "quickshell" "quickshell": "quickshell"
}, },
"locked": { "locked": {
"lastModified": 1777688289, "lastModified": 1780196414,
"narHash": "sha256-2EaEVkT1oUpjLLp7uEY/hDYDOa2k5R1YgcJpHei+lUM=", "narHash": "sha256-iXmyWULTZuRd68xRL79e9GyYL9FZ6gfh6zl1PPlWX2A=",
"owner": "caelestia-dots", "owner": "caelestia-dots",
"repo": "shell", "repo": "shell",
"rev": "4e9e1f4b723f7e3a87cb280d67a25ee92c87fbff", "rev": "63bb82762bb29ac9b7fcd5b97839abae721ce860",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -24,11 +24,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1777268161, "lastModified": 1780243769,
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=", "narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76", "rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -46,11 +46,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1777341401, "lastModified": 1779430452,
"narHash": "sha256-QEAVYeXxvTamsYJVBq8+qSJV9ml2MxqRaZvkobfuPWA=", "narHash": "sha256-zTslhsxLqUlRTML506iougTGzyR38Fzhzn7t4KDEuuE=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "0baa81aa03559ca315668e5a306364cddf1a6f49", "rev": "4b4fca3224ab977dc515ac0bb78d00b3dfa71e00",
"revCount": 812, "revCount": 819,
"type": "git", "type": "git",
"url": "https://git.outfoxxed.me/outfoxxed/quickshell" "url": "https://git.outfoxxed.me/outfoxxed/quickshell"
}, },
+25 -5
View File
@@ -26,6 +26,26 @@ class Command:
self.timeout_tracker: dict[str, float] = {} self.timeout_tracker: dict[str, float] = {}
self.window_rules = self._load_window_rules() self.window_rules = self._load_window_rules()
def _make_resize_cmd(self, width: int | str, height: int | str, address: str) -> str:
if hypr.is_lua_config():
return f'dispatch hl.dsp.window.resize({{x = {width}, y = {height}, exact = true, window = "address:{address}"}})'
return f"dispatch resizewindowpixel exact {width} {height},address:{address}"
def _make_move_cmd(self, x: int, y: int, address: str) -> str:
if hypr.is_lua_config():
return f'dispatch hl.dsp.window.move({{x = {x}, y = {y}, window = "address:{address}"}})'
return f"dispatch movewindowpixel exact {x} {y},address:{address}"
def _make_float_cmd(self, address: str) -> str:
if hypr.is_lua_config():
return f'dispatch hl.dsp.window.float({{action = "toggle", window = "address:{address}"}})'
return f"dispatch togglefloating address:{address}"
def _make_center_cmd(self) -> str:
if hypr.is_lua_config():
return "dispatch hl.dsp.window.center()"
return "dispatch centerwindow"
def _load_window_rules(self) -> list[WindowRule]: def _load_window_rules(self) -> list[WindowRule]:
default_rules = [ default_rules = [
WindowRule("(Bitwarden", "titleContains", "20%", "54%", ["float", "center"]), WindowRule("(Bitwarden", "titleContains", "20%", "54%", ["float", "center"]),
@@ -164,8 +184,8 @@ class Command:
move_x = monitor_x + monitor_width - scaled_width - offset move_x = monitor_x + monitor_width - scaled_width - offset
move_y = monitor_y + monitor_height - scaled_height - offset move_y = monitor_y + monitor_height - scaled_height - offset
command1 = f"dispatch resizewindowpixel exact {scaled_width} {scaled_height},address:{address}" command1 = self._make_resize_cmd(scaled_width, scaled_height, address)
command2 = f"dispatch movewindowpixel exact {int(move_x)} {int(move_y)},address:{address}" command2 = self._make_move_cmd(int(move_x), int(move_y), address)
hypr.batch(command1, command2) hypr.batch(command1, command2)
log_message( log_message(
@@ -181,16 +201,16 @@ class Command:
if "float" in actions: if "float" in actions:
window_info = self._get_window_info(window_id) window_info = self._get_window_info(window_id)
if window_info and not window_info.get("floating", False): if window_info and not window_info.get("floating", False):
dispatch_commands.append(f"dispatch togglefloating address:0x{window_id}") dispatch_commands.append(self._make_float_cmd(f"0x{window_id}"))
if "pip" in actions: if "pip" in actions:
self._apply_pip_action(window_id) self._apply_pip_action(window_id)
return True return True
dispatch_commands.append(f"dispatch resizewindowpixel exact {width} {height},address:0x{window_id}") dispatch_commands.append(self._make_resize_cmd(width, height, f"0x{window_id}"))
if "center" in actions: if "center" in actions:
dispatch_commands.append("dispatch centerwindow") dispatch_commands.append(self._make_center_cmd())
try: try:
hypr.batch(*dispatch_commands) hypr.batch(*dispatch_commands)
+7 -1
View File
@@ -2,6 +2,7 @@ import subprocess
from argparse import Namespace from argparse import Namespace
from datetime import datetime from datetime import datetime
from caelestia.utils import hypr
from caelestia.utils.notify import notify from caelestia.utils.notify import notify
from caelestia.utils.paths import screenshots_cache_dir, screenshots_dir from caelestia.utils.paths import screenshots_cache_dir, screenshots_dir
@@ -33,7 +34,12 @@ class Command:
swappy.stdin.close() swappy.stdin.close()
def fullscreen(self) -> None: def fullscreen(self) -> None:
sc_data = subprocess.check_output(["grim", "-"]) cmd = ["grim"]
focused_monitor = next(monitor for monitor in hypr.message("monitors") if monitor["focused"])
if focused_monitor:
cmd += ["-o", focused_monitor["name"]]
cmd += ["-"]
sc_data = subprocess.check_output(cmd)
subprocess.run(["wl-copy"], input=sc_data) subprocess.run(["wl-copy"], input=sc_data)
+30
View File
@@ -7,6 +7,7 @@ socket_base = f"{os.getenv('XDG_RUNTIME_DIR')}/hypr/{os.getenv('HYPRLAND_INSTANC
socket_path = f"{socket_base}/.socket.sock" socket_path = f"{socket_base}/.socket.sock"
socket2_path = f"{socket_base}/.socket2.sock" socket2_path = f"{socket_base}/.socket2.sock"
_lua_config_cache: bool | None = None
def message(msg: str, is_json: bool = True) -> str | dict[str, Any]: def message(msg: str, is_json: bool = True) -> str | dict[str, Any]:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
@@ -26,7 +27,36 @@ def message(msg: str, is_json: bool = True) -> str | dict[str, Any]:
return json.loads(resp) if is_json else resp return json.loads(resp) if is_json else resp
def is_lua_config() -> bool:
global _lua_config_cache
if _lua_config_cache is not None:
return _lua_config_cache
try:
result = message("systeminfo", is_json=False)
for line in result.splitlines():
if "configProvider:" in line:
_lua_config_cache = "lua" in line.lower()
return _lua_config_cache
_lua_config_cache = False
return False
except Exception:
_lua_config_cache = False
return False
DISPATCHER_MAP_LUA = {
"togglespecialworkspace": lambda *a: f'hl.dsp.workspace.toggle_special("{a[0]}")' if a else 'hl.dsp.workspace.toggle_special()',
"movetoworkspacesilent": lambda *a: (
f'hl.dsp.window.move({{window = "address:{a[0].split(",")[1].replace("address:", "")}", workspace = "{a[0].split(",")[0]}", follow = false}})'
),
"exec": lambda *a: 'hl.dsp.exec_cmd("' + ' '.join(a).replace('\\', '\\\\').replace('"', '\\"') + '")',
}
def dispatch(dispatcher: str, *args: str) -> bool: def dispatch(dispatcher: str, *args: str) -> bool:
if is_lua_config() and dispatcher in DISPATCHER_MAP_LUA:
lua_dispatch = DISPATCHER_MAP_LUA[dispatcher](*args)
return message(f"dispatch {lua_dispatch}", is_json=False) == "ok"
return message(f"dispatch {dispatcher} {' '.join(map(str, args))}".rstrip(), is_json=False) == "ok" return message(f"dispatch {dispatcher} {' '.join(map(str, args))}".rstrip(), is_json=False) == "ok"
+1 -1
View File
@@ -18,7 +18,7 @@ from typing import Protocol, Any
# subclasses in get_scheme() handle that internally. This Protocol tells the type # subclasses in get_scheme() handle that internally. This Protocol tells the type
# checker to expect our specific 3-argument setup instead of the base class signature. # checker to expect our specific 3-argument setup instead of the base class signature.
class SchemeConstructor(Protocol): class SchemeConstructor(Protocol):
def __call__(self, source_color_hct: Any, is_dark: bool, contrast_level: float) -> DynamicScheme: ... def __call__(self, source_color_hct: Any, is_dark: bool, contrast_level: float) -> "DynamicScheme": ...
try: try:
from materialyoucolor.dynamiccolor.dynamic_scheme import DynamicScheme from materialyoucolor.dynamiccolor.dynamic_scheme import DynamicScheme
+10 -2
View File
@@ -19,6 +19,7 @@ from caelestia.utils.paths import (
user_templates_dir, user_templates_dir,
) )
from caelestia.utils.scheme import get_scheme from caelestia.utils.scheme import get_scheme
from caelestia.utils.hypr import is_lua_config
def gen_conf(colours: dict[str, str]) -> str: def gen_conf(colours: dict[str, str]) -> str:
@@ -27,6 +28,12 @@ def gen_conf(colours: dict[str, str]) -> str:
conf += f"${name} = {colour}\n" conf += f"${name} = {colour}\n"
return conf return conf
def gen_lua(colours: dict[str, str]) -> str:
lua = "return {\n"
for name, colour in colours.items():
lua += f' {name} = "{colour}",\n'
lua += "}"
return lua
def gen_scss(colours: dict[str, str]) -> str: def gen_scss(colours: dict[str, str]) -> str:
scss = "" scss = ""
@@ -144,7 +151,8 @@ def apply_terms(sequences: str) -> None:
@log_exception @log_exception
def apply_hypr(conf: str) -> None: def apply_hypr(conf: str) -> None:
write_file(config_dir / "hypr/scheme/current.conf", conf) ext = "lua" if is_lua_config() else "conf"
write_file(config_dir / f"hypr/scheme/current.{ext}", conf)
@log_exception @log_exception
@@ -428,7 +436,7 @@ def apply_colours(colours: dict[str, str], mode: str) -> None:
if check("enableTerm"): if check("enableTerm"):
apply_terms(gen_sequences(colours)) apply_terms(gen_sequences(colours))
if check("enableHypr"): if check("enableHypr"):
apply_hypr(gen_conf(colours)) apply_hypr(gen_lua(colours) if is_lua_config() else gen_conf(colours))
if check("enableDiscord"): if check("enableDiscord"):
apply_discord(gen_scss(colours)) apply_discord(gen_scss(colours))
if check("enableSpicetify"): if check("enableSpicetify"):