Files
Modus/modules/launcher/plugins/scripts.py
T

266 lines
9.9 KiB
Python

"""
Scripts plugin for the launcher.
Provides access to various system scripts and utilities.
"""
import os
import subprocess
from typing import List
from fabric.utils import exec_shell_command_async
import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
class ScriptsPlugin(PluginBase):
"""
Plugin for executing system scripts and utilities.
"""
def __init__(self):
super().__init__()
self.display_name = "Scripts"
self.description = "System scripts and utilities"
def initialize(self):
"""Initialize the scripts plugin."""
self.set_triggers(["script", "scripts", "cmd", "util"])
def cleanup(self):
"""Cleanup the scripts plugin."""
pass
def _check_gamemode_status(self) -> bool:
"""Check if gamemode (animations disabled) is currently active."""
try:
# Use the gamemode script's check function
script_path = self._get_script_path("gamemode.sh")
result = subprocess.run(
["bash", script_path, "check"],
capture_output=True,
text=True,
timeout=2
)
if result.returncode == 0:
# Script returns "t" for true (gamemode on), "f" for false (gamemode off)
return result.stdout.strip() == "t"
except Exception:
pass
return False
def _toggle_gamemode(self):
"""Toggle gamemode using the gamemode script."""
script_path = self._get_script_path("gamemode.sh")
exec_shell_command_async(f"bash '{script_path}'")
def _run_colorpicker(self, format_type: str):
"""Run color picker with specified format."""
script_path = self._get_script_path("hyprpicker.sh")
exec_shell_command_async(f"bash '{script_path}' -{format_type}")
def _reload_css(self):
"""Reload application CSS."""
exec_shell_command_async("$fabricSend 'app.set_css()'")
def _restart_modus(self):
"""Restart Modus application."""
exec_shell_command_async("killall modus; uwsm-app python ~/Modus/main.py")
def _run_system_command(self, command: str):
"""Run a system command."""
exec_shell_command_async(command)
def _toggle_service(self, service_name: str):
"""Toggle a systemd service."""
exec_shell_command_async(f"systemctl --user is-active {service_name} && systemctl --user stop {service_name} || systemctl --user start {service_name}")
def _get_script_path(self, script_name: str) -> str:
"""Get the full path to a script from the scripts directory."""
# Get the current working directory (should be Modus root)
current_dir = os.getcwd()
# Try scripts directory relative to current working directory
script_path = os.path.join(current_dir, "scripts", script_name)
if os.path.exists(script_path):
return script_path
# Try relative to this file's location
plugin_dir = os.path.dirname(os.path.abspath(__file__))
modus_root = os.path.dirname(os.path.dirname(os.path.dirname(plugin_dir)))
script_path = os.path.join(modus_root, "scripts", script_name)
if os.path.exists(script_path):
return script_path
# Fallback to relative path
return f"scripts/{script_name}"
def query(self, query_string: str) -> List[Result]:
"""Search for script actions based on query."""
query = query_string.strip().lower()
results = []
# Check gamemode status for dynamic display
gamemode_active = self._check_gamemode_status()
gamemode_icon = icons.gamemode if gamemode_active else icons.gamemode_off
gamemode_status = "ON" if gamemode_active else "OFF"
# Define script actions
script_actions = {
# Gamemode
"gamemode": {
"title": f"Toggle Gamemode (Currently {gamemode_status})",
"subtitle": "Toggle Hyprland game mode - disable animations, gaps, and effects",
"icon": gamemode_icon,
"action": self._toggle_gamemode,
"keywords": ["gamemode", "game", "performance", "toggle"],
"relevance": 1.0
},
# Color Picker
"colorpicker-hex": {
"title": "Color Picker (HEX)",
"subtitle": "Pick a color and copy HEX value to clipboard",
"icon": icons.colorpicker,
"action": lambda: self._run_colorpicker("hex"),
"keywords": ["color", "picker", "hex", "colorpicker"],
"relevance": 0.9
},
"colorpicker-rgb": {
"title": "Color Picker (RGB)",
"subtitle": "Pick a color and copy RGB value to clipboard",
"icon": icons.colorpicker,
"action": lambda: self._run_colorpicker("rgb"),
"keywords": ["color", "picker", "rgb", "colorpicker"],
"relevance": 0.9
},
"colorpicker-hsv": {
"title": "Color Picker (HSV)",
"subtitle": "Pick a color and copy HSV value to clipboard",
"icon": icons.colorpicker,
"action": lambda: self._run_colorpicker("hsv"),
"keywords": ["color", "picker", "hsv", "colorpicker"],
"relevance": 0.9
},
# System Controls
"reload-css": {
"title": "Reload CSS",
"subtitle": "Reload application stylesheets",
"icon": icons.reload,
"action": self._reload_css,
"keywords": ["reload", "css", "style", "stylesheet"],
"relevance": 0.6
},
"restart-modus": {
"title": "Restart Modus",
"subtitle": "Restart the Modus application",
"icon": icons.reload,
"action": self._restart_modus,
"keywords": ["restart", "modus", "reload", "reboot"],
"relevance": 0.6
},
# System Utilities
"kill-all": {
"title": "Kill All Processes",
"subtitle": "Kill all user processes (useful for cleanup)",
"icon": icons.close,
"action": lambda: self._run_system_command("killall -u $USER"),
"keywords": ["kill", "killall", "processes", "cleanup"],
"relevance": 0.5
},
"clear-cache": {
"title": "Clear Cache",
"subtitle": "Clear system and user cache directories",
"icon": icons.trash,
"action": lambda: self._run_system_command("rm -rf ~/.cache/* /tmp/*"),
"keywords": ["clear", "cache", "clean", "tmp", "cleanup"],
"relevance": 0.5
},
# Night Light / Blue Light Filter
"night-light": {
"title": "Toggle Night Light",
"subtitle": "Toggle blue light filter for eye comfort",
"icon": icons.night,
"action": lambda: self._run_system_command("hyprctl keyword decoration:screen_shader ~/.config/hypr/shaders/blue_light_filter.glsl"),
"keywords": ["night", "light", "blue", "filter", "eye", "comfort"],
"relevance": 0.6
},
# Clipboard
"clear-clipboard": {
"title": "Clear Clipboard",
"subtitle": "Clear clipboard contents",
"icon": icons.clipboard,
"action": lambda: self._run_system_command("wl-copy --clear"),
"keywords": ["clipboard", "clear", "empty"],
"relevance": 0.5
},
}
# If no query, show all actions
if not query:
for action_id, action_data in script_actions.items():
results.append(Result(
title=action_data["title"],
subtitle=action_data["subtitle"],
icon_markup=action_data["icon"],
action=action_data["action"],
relevance=action_data["relevance"],
plugin_name=self.display_name,
data={"action_id": action_id}
))
else:
# Search through actions
for action_id, action_data in script_actions.items():
relevance = self._calculate_relevance(query, action_data["keywords"], action_data["title"])
if relevance > 0:
results.append(Result(
title=action_data["title"],
subtitle=action_data["subtitle"],
icon_markup=action_data["icon"],
action=action_data["action"],
relevance=relevance,
plugin_name=self.display_name,
data={"action_id": action_id}
))
return sorted(results, key=lambda x: x.relevance, reverse=True)
def _calculate_relevance(self, query: str, keywords: List[str], title: str) -> float:
"""Calculate relevance score for a script action."""
query_lower = query.lower()
title_lower = title.lower()
# Check for exact matches in keywords
for keyword in keywords:
if query_lower == keyword.lower():
return 1.0
# Check if query starts with any keyword
for keyword in keywords:
if keyword.lower().startswith(query_lower):
return 0.9
# Check if title contains query
if query_lower in title_lower:
return 0.8
# Check if any keyword contains query
for keyword in keywords:
if query_lower in keyword.lower():
return 0.7
return 0.0