diff --git a/AGENTS.md b/AGENTS.md index ea22354..2b1b90c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,3 +41,4 @@ System packages (example names): - STT model and device are configured via the `stt` section in `config.json`. - LLM model settings are locked; model downloads to `~/.cache/lel/models/`. - `-v/--verbose` enables verbose logs (including llama.cpp) with `llama::` prefix. +- Press `Esc` while recording to cancel without processing. diff --git a/README.md b/README.md index e5aaddc..cd1a15d 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ systemctl --user enable --now lel - Press the hotkey once to start recording. - Press it again to stop and run STT. +- Press `Esc` while recording to cancel without processing. - Transcript contents are logged only when `logging.log_transcript` is enabled or `-v/--verbose` is used. Wayland note: diff --git a/src/desktop.py b/src/desktop.py index 3f321fa..3c89bb6 100644 --- a/src/desktop.py +++ b/src/desktop.py @@ -8,6 +8,9 @@ class DesktopAdapter(Protocol): def start_hotkey_listener(self, hotkey: str, callback: Callable[[], None]) -> None: raise NotImplementedError + def start_cancel_listener(self, callback: Callable[[], None]) -> None: + raise NotImplementedError + def inject_text(self, text: str, backend: str) -> None: raise NotImplementedError diff --git a/src/desktop_wayland.py b/src/desktop_wayland.py index 0b6903f..6d3c6c5 100644 --- a/src/desktop_wayland.py +++ b/src/desktop_wayland.py @@ -7,6 +7,9 @@ class WaylandAdapter: def start_hotkey_listener(self, _hotkey: str, _callback: Callable[[], None]) -> None: raise SystemExit("Wayland hotkeys are not supported yet.") + def start_cancel_listener(self, _callback: Callable[[], None]) -> None: + raise SystemExit("Wayland hotkeys are not supported yet.") + def inject_text(self, _text: str, _backend: str) -> None: raise SystemExit("Wayland text injection is not supported yet.") diff --git a/src/desktop_x11.py b/src/desktop_x11.py index 5024ec6..de8a288 100644 --- a/src/desktop_x11.py +++ b/src/desktop_x11.py @@ -66,6 +66,10 @@ class X11Adapter: thread = threading.Thread(target=self._listen, args=(hotkey, callback), daemon=True) thread.start() + def start_cancel_listener(self, callback: Callable[[], None]) -> None: + thread = threading.Thread(target=self._listen, args=("Escape", callback), daemon=True) + thread.start() + def inject_text(self, text: str, backend: str) -> None: backend = (backend or "").strip().lower() if backend in ("", "clipboard"): diff --git a/src/leld.py b/src/leld.py index f322aae..7bc41db 100755 --- a/src/leld.py +++ b/src/leld.py @@ -231,6 +231,12 @@ class Daemon: stream, record = payload self._start_stop_worker(stream, record, trigger, process_audio) + def cancel_recording(self): + with self.lock: + if self.state != State.RECORDING: + return + self.stop_recording(trigger="cancel", process_audio=False) + def shutdown(self, timeout: float = 5.0) -> bool: self.request_shutdown() self.stop_recording(trigger="shutdown", process_audio=False) @@ -348,6 +354,7 @@ def main(): cfg.daemon.hotkey, lambda: logging.info("hotkey pressed (dry-run)") if args.dry_run else daemon.toggle(), ) + desktop.start_cancel_listener(lambda: daemon.cancel_recording()) logging.info("ready") try: desktop.run_tray(daemon.get_state, lambda: shutdown("quit requested"))