From 2ec14f35f2cd0b9d1377748483f4dc553e95e3c1 Mon Sep 17 00:00:00 2001 From: Thales Maciel Date: Tue, 24 Feb 2026 12:00:33 -0300 Subject: [PATCH] Align log formatting --- AGENTS.md | 2 ++ README.md | 2 +- src/aiprocess.py | 6 +++++- src/leld.py | 45 +++++++++++++++++++++++++++++++++++++++------ 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 778cac5..78aab63 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,8 @@ - Install deps: `uv sync`. - Run daemon: `uv run python3 src/leld.py --config ~/.config/lel/config.json`. +System packages (example names): `portaudio`/`libportaudio2`, `libayatana-appindicator3`. + ## Coding Style & Naming Conventions - Shell scripts use Bash with `set -euo pipefail`. diff --git a/README.md b/README.md index 69f5613..394e020 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Python X11 STT daemon that records audio, runs Whisper, logs the transcript, and - X11 (not Wayland) - `sounddevice` (PortAudio) - `faster-whisper` -- Tray icon deps: `gtk3` +- Tray icon deps: `gtk3`, `libayatana-appindicator3` - Python deps: `pillow`, `python-xlib`, `faster-whisper`, `PyGObject`, `sounddevice` System packages (example names): `portaudio`/`libportaudio2`. diff --git a/src/aiprocess.py b/src/aiprocess.py index 0172089..ea6b3ed 100644 --- a/src/aiprocess.py +++ b/src/aiprocess.py @@ -150,7 +150,11 @@ def main() -> int: parser.add_argument("text", nargs="?", default="", help="text to process (or stdin)") args = parser.parse_args() - logging.basicConfig(stream=sys.stderr, level=logging.INFO, format="ai: %(asctime)s %(message)s") + logging.basicConfig( + stream=sys.stderr, + level=logging.INFO, + format="lel: %(asctime)s %(levelname)s %(message)s", + ) cfg = load(args.config) logging.info( diff --git a/src/leld.py b/src/leld.py index d08ae71..95982c7 100755 --- a/src/leld.py +++ b/src/leld.py @@ -7,6 +7,7 @@ import signal import sys import threading import time +import warnings from pathlib import Path import gi @@ -19,6 +20,11 @@ from inject import inject from x11_hotkey import listen gi.require_version("Gtk", "3.0") +try: + gi.require_version("AppIndicator3", "0.1") + from gi.repository import AppIndicator3 # type: ignore[import-not-found] +except ValueError: + AppIndicator3 = None from gi.repository import GLib, Gtk # type: ignore[import-not-found] @@ -57,14 +63,32 @@ class Daemon: device=cfg.stt.get("device", "cpu"), compute_type=_compute_type(cfg.stt.get("device", "cpu")), ) - self.icon = Gtk.StatusIcon() - self.icon.set_visible(True) - self.icon.connect("popup-menu", self._on_tray_menu) + self.indicator = None + self.status_icon = None + if AppIndicator3 is not None: + self.indicator = AppIndicator3.Indicator.new( + "lel", + self._icon_path(State.IDLE), + AppIndicator3.IndicatorCategory.APPLICATION_STATUS, + ) + self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE) + else: + logging.warning("AppIndicator3 unavailable; falling back to deprecated Gtk.StatusIcon") + warnings.filterwarnings( + "ignore", + message=".*Gtk.StatusIcon.*", + category=DeprecationWarning, + ) + self.status_icon = Gtk.StatusIcon() + self.status_icon.set_visible(True) + self.status_icon.connect("popup-menu", self._on_tray_menu) self.menu = Gtk.Menu() quit_item = Gtk.MenuItem(label="Quit") quit_item.connect("activate", lambda *_: self._quit()) self.menu.append(quit_item) self.menu.show_all() + if self.indicator is not None: + self.indicator.set_menu(self.menu) def set_state(self, state: str): with self.lock: @@ -228,8 +252,13 @@ class Daemon: def _update_tray(self): state = self.get_state() - self.icon.set_from_file(self._icon_path(state)) - self.icon.set_tooltip_text(self._title(state)) + icon_path = self._icon_path(state) + if self.indicator is not None: + self.indicator.set_icon_full(icon_path, self._title(state)) + self.indicator.set_label(self._title(state), "") + elif self.status_icon is not None: + self.status_icon.set_from_file(icon_path) + self.status_icon.set_tooltip_text(self._title(state)) return True def run_tray(self): @@ -259,7 +288,11 @@ def main(): parser.add_argument("--dry-run", action="store_true", help="log hotkey only") args = parser.parse_args() - logging.basicConfig(stream=sys.stderr, level=logging.INFO, format="leld: %(asctime)s %(message)s") + logging.basicConfig( + stream=sys.stderr, + level=logging.INFO, + format="lel: %(asctime)s %(levelname)s %(message)s", + ) cfg = load(args.config) _lock_single_instance()