summaryrefslogtreecommitdiff
path: root/src/caelestia/subcommands/toggle.py
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-08-04 17:33:43 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-08-04 17:33:43 +1000
commitae8deb35a7c1288b7262731de1991d084fe0f00f (patch)
treeb3c5c18da9a5dbb80a998efc0295d2d038f6c50f /src/caelestia/subcommands/toggle.py
parentpaths: use xdg user paths (diff)
downloadcaelestia-cli-ae8deb35a7c1288b7262731de1991d084fe0f00f.tar.gz
caelestia-cli-ae8deb35a7c1288b7262731de1991d084fe0f00f.tar.bz2
caelestia-cli-ae8deb35a7c1288b7262731de1991d084fe0f00f.zip
toggle: allow configuring
Closes #33
Diffstat (limited to 'src/caelestia/subcommands/toggle.py')
-rw-r--r--src/caelestia/subcommands/toggle.py154
1 files changed, 119 insertions, 35 deletions
diff --git a/src/caelestia/subcommands/toggle.py b/src/caelestia/subcommands/toggle.py
index e045c43..74be80e 100644
--- a/src/caelestia/subcommands/toggle.py
+++ b/src/caelestia/subcommands/toggle.py
@@ -1,18 +1,120 @@
+import json
+import shutil
import subprocess
from argparse import Namespace
+from collections import ChainMap
from caelestia.utils import hypr
+from caelestia.utils.paths import user_config_path
+
+
+def is_subset(superset, subset):
+ for key, value in subset.items():
+ if key not in superset:
+ return False
+
+ if isinstance(value, dict):
+ if not is_subset(superset[key], value):
+ return False
+
+ elif isinstance(value, str):
+ if value not in superset[key]:
+ return False
+
+ elif isinstance(value, list):
+ if not set(value) <= set(superset[key]):
+ return False
+ elif isinstance(value, set):
+ if not value <= superset[key]:
+ return False
+
+ else:
+ if not value == superset[key]:
+ return False
+
+ return True
+
+
+class DeepChainMap(ChainMap):
+ def __getitem__(self, key):
+ values = (mapping[key] for mapping in self.maps if key in mapping)
+ try:
+ first = next(values)
+ except StopIteration:
+ return self.__missing__(key)
+ if isinstance(first, dict):
+ return self.__class__(first, *values)
+ return first
+
+ def __repr__(self):
+ return repr(dict(self))
class Command:
args: Namespace
+ cfg: dict[str, dict[str, dict[str, any]]] | DeepChainMap
clients: list[dict[str, any]] = None
def __init__(self, args: Namespace) -> None:
self.args = args
+ self.cfg = {
+ "communication": {
+ "discord": {
+ "enable": True,
+ "match": [{"class": "discord"}],
+ "command": ["discord"],
+ "move": True,
+ },
+ "whatsapp": {
+ "enable": True,
+ "match": [{"class": "whatsapp"}],
+ "move": True,
+ },
+ },
+ "music": {
+ "spotify": {
+ "enable": True,
+ "match": [{"class": "Spotify"}, {"initialTitle": "Spotify"}, {"initialTitle": "Spotify Free"}],
+ "command": ["spicetify", "watch", "-s"],
+ "move": True,
+ },
+ "feishin": {
+ "enable": True,
+ "match": [{"class": "feishin"}],
+ "move": True,
+ },
+ },
+ "sysmon": {
+ "btop": {
+ "enable": True,
+ "match": [{"class": "btop", "title": "btop", "workspace": {"name": "special:sysmon"}}],
+ "command": ["foot", "-a", "btop", "-T", "btop", "fish", "-C", "exec btop"],
+ },
+ },
+ "todo": {
+ "todoist": {
+ "enable": True,
+ "match": [{"class": "Todoist"}],
+ "command": ["todoist"],
+ "move": True,
+ },
+ },
+ }
+ try:
+ self.cfg = DeepChainMap(json.loads(user_config_path.read_text())["toggles"], self.cfg)
+ except (FileNotFoundError, json.JSONDecodeError, KeyError):
+ pass
+
def run(self) -> None:
- getattr(self, self.args.workspace)()
+ if self.args.workspace == "specialws":
+ self.specialws()
+ return
+
+ for client in self.cfg[self.args.workspace].values():
+ if "enable" in client and client["enable"]:
+ self.handle_client_config(client)
+ hypr.dispatch("togglespecialworkspace", self.args.workspace)
def get_clients(self) -> list[dict[str, any]]:
if self.clients is None:
@@ -22,45 +124,27 @@ class Command:
def move_client(self, selector: callable, workspace: str) -> None:
for client in self.get_clients():
- if selector(client):
+ if selector(client) and client["workspace"]["name"] != f"special:{workspace}":
hypr.dispatch("movetoworkspacesilent", f"special:{workspace},address:{client['address']}")
- def spawn_client(self, selector: callable, spawn: list[str]) -> bool:
- exists = any(selector(client) for client in self.get_clients())
-
- if not exists:
+ def spawn_client(self, selector: callable, spawn: list[str]) -> None:
+ if (spawn[0].endswith(".desktop") or shutil.which(spawn[0])) and not any(
+ selector(client) for client in self.get_clients()
+ ):
subprocess.Popen(["app2unit", "--", *spawn], start_new_session=True)
- return not exists
-
- def spawn_or_move(self, selector: callable, spawn: list[str], workspace: str) -> None:
- if not self.spawn_client(selector, spawn):
- self.move_client(selector, workspace)
-
- def communication(self) -> None:
- self.spawn_or_move(lambda c: c["class"] == "discord", ["discord"], "communication")
- self.move_client(lambda c: c["class"] == "whatsapp", "communication")
- hypr.dispatch("togglespecialworkspace", "communication")
-
- def music(self) -> None:
- self.spawn_or_move(
- lambda c: c["class"] == "Spotify" or c["initialTitle"] == "Spotify" or c["initialTitle"] == "Spotify Free",
- ["spicetify", "watch", "-s"],
- "music",
- )
- self.move_client(lambda c: c["class"] == "feishin", "music")
- hypr.dispatch("togglespecialworkspace", "music")
-
- def sysmon(self) -> None:
- self.spawn_client(
- lambda c: c["class"] == "btop" and c["title"] == "btop" and c["workspace"]["name"] == "special:sysmon",
- ["foot", "-a", "btop", "-T", "btop", "fish", "-C", "exec btop"],
- )
- hypr.dispatch("togglespecialworkspace", "sysmon")
+ def handle_client_config(self, client: dict[str, any]) -> None:
+ def selector(c: dict[str, any]) -> bool:
+ # Each match is or, inside matches is and
+ for match in client["match"]:
+ if is_subset(c, match):
+ return True
+ return False
- def todo(self) -> None:
- self.spawn_or_move(lambda c: c["class"] == "Todoist", ["todoist"], "todo")
- hypr.dispatch("togglespecialworkspace", "todo")
+ if "command" in client and client["command"]:
+ self.spawn_client(selector, client["command"])
+ if "move" in client and client["move"]:
+ self.move_client(selector, self.args.workspace)
def specialws(self) -> None:
workspaces = hypr.message("workspaces")