Files
Modus/modules/launcher/components/sh.py
T
2025-04-25 06:33:51 +05:45

140 lines
4.1 KiB
Python

import os
import subprocess
from typing import List
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.entry import Entry
from fabric.widgets.label import Label
from fabric.widgets.scrolledwindow import ScrolledWindow
import config.data as data
BINS = f"{data.CACHE_DIR}/binaries"
class Sh(Box):
def __init__(self, **kwargs):
super().__init__(
all_visible=False,
visible=False,
**kwargs,
)
self.launcher = kwargs["launcher"]
self._arranger_handler: int = 0
self.shell_command_manager = ShellCommandManager(self)
self.viewport = None
self.search_entry = Entry(
name="search-entry",
placeholder="Search Shell Commands...",
h_expand=True,
notify_text=lambda entry, *_: self.handle_search_input(entry.get_text()),
on_activate=lambda entry, *_: self.handle_search_input(entry.get_text()),
)
self.search_entry.props.xalign = 0.5
self.header_box = Box(
spacing=10,
orientation="h",
children=[self.search_entry],
)
self.launcher_box = Box(
spacing=10,
orientation="v",
h_expand=True,
children=[self.header_box],
)
self.add(self.launcher_box)
def open_sh(self):
if not self.viewport:
self.viewport = Box(name="viewport", spacing=10, orientation="v")
self.scrolled_window = ScrolledWindow(
name="scrolled-window",
spacing=10,
h_scrollbar_policy="never",
v_scrollbar_policy="never",
# min_content_size=(-1, -1),
child=self.viewport,
)
self.launcher_box.add(self.scrolled_window)
self.viewport.children = []
self.shell_command_manager.show_shell_commands(self.viewport, "")
self.viewport.show()
self.search_entry.grab_focus()
def handle_search_input(self, text: str):
self.shell_command_manager.show_shell_commands(self.viewport, text)
class ShellCommandManager:
def __init__(self, launcher):
self.launcher = launcher
self.binaries = set()
self.reload_binaries()
def reload_binaries(self):
paths = os.getenv("PATH", "").split(":")
self.binaries = set()
for path in paths:
self.binaries.update(self.list_binaries_in_path(path))
with open(BINS, "w") as f:
f.write("\n".join(self.binaries))
def list_binaries_in_path(self, path):
if not os.path.exists(path):
return []
try:
return [
entry
for entry in os.listdir(path)
if os.access(os.path.join(path, entry), os.X_OK)
]
except OSError:
return []
def show_shell_commands(self, viewport, search_query: str):
if not search_query:
return
viewport.children = []
results = self.query_binaries(search_query)
self.display_results(viewport, results)
def query_binaries(self, filter_str: str) -> List[str]:
filter_str = filter_str.lower()
filtered_bins = (cmd for cmd in self.binaries if filter_str in cmd)
return list(dict.fromkeys(filtered_bins))[:8]
def display_results(self, viewport, results: List[str]):
for command in results:
button = Button(
child=Label(
label=command,
v_align="center",
h_align="start",
),
on_clicked=lambda _, cmd=command: self.execute_command(cmd),
name="app-slot-button",
)
viewport.add(button)
def close_menu(self):
self.launcher.close()
def execute_command(self, cmd: str):
try:
output = subprocess.check_output(cmd, shell=True, text=True)
print(output)
except subprocess.CalledProcessError as e:
print(e.output)
finally:
self.close_menu()