Add clipboard cleanup option for clipboard backend
This commit is contained in:
parent
1423e44008
commit
ccf968a410
9 changed files with 114 additions and 11 deletions
|
|
@ -38,6 +38,7 @@ class SttConfig:
|
|||
@dataclass
|
||||
class InjectionConfig:
|
||||
backend: str = DEFAULT_INJECTION_BACKEND
|
||||
remove_transcription_from_clipboard: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -115,6 +116,8 @@ def validate(cfg: Config) -> None:
|
|||
allowed = ", ".join(sorted(ALLOWED_INJECTION_BACKENDS))
|
||||
raise ValueError(f"injection.backend must be one of: {allowed}")
|
||||
cfg.injection.backend = backend
|
||||
if not isinstance(cfg.injection.remove_transcription_from_clipboard, bool):
|
||||
raise ValueError("injection.remove_transcription_from_clipboard must be boolean")
|
||||
|
||||
if not isinstance(cfg.ai.enabled, bool):
|
||||
raise ValueError("ai.enabled must be boolean")
|
||||
|
|
@ -180,6 +183,11 @@ def _from_dict(data: dict[str, Any], cfg: Config) -> Config:
|
|||
cfg.stt.device = _as_nonempty_str(stt["device"], "stt.device")
|
||||
if "backend" in injection:
|
||||
cfg.injection.backend = _as_nonempty_str(injection["backend"], "injection.backend")
|
||||
if "remove_transcription_from_clipboard" in injection:
|
||||
cfg.injection.remove_transcription_from_clipboard = _as_bool(
|
||||
injection["remove_transcription_from_clipboard"],
|
||||
"injection.remove_transcription_from_clipboard",
|
||||
)
|
||||
if "enabled" in ai:
|
||||
cfg.ai.enabled = _as_bool(ai["enabled"], "ai.enabled")
|
||||
if "replacements" in vocabulary:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,13 @@ class DesktopAdapter(Protocol):
|
|||
def start_cancel_listener(self, callback: Callable[[], None]) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def inject_text(self, text: str, backend: str) -> None:
|
||||
def inject_text(
|
||||
self,
|
||||
text: str,
|
||||
backend: str,
|
||||
*,
|
||||
remove_transcription_from_clipboard: bool = False,
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def run_tray(self, state_getter: Callable[[], str], on_quit: Callable[[], None]) -> None:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,14 @@ class WaylandAdapter:
|
|||
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:
|
||||
def inject_text(
|
||||
self,
|
||||
_text: str,
|
||||
_backend: str,
|
||||
*,
|
||||
remove_transcription_from_clipboard: bool = False,
|
||||
) -> None:
|
||||
_ = remove_transcription_from_clipboard
|
||||
raise SystemExit("Wayland text injection is not supported yet.")
|
||||
|
||||
def run_tray(self, _state_getter: Callable[[], str], _on_quit: Callable[[], None]) -> None:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import warnings
|
||||
from typing import Callable, Iterable
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ MOD_MAP = {
|
|||
"cmd": X.Mod4Mask,
|
||||
"command": X.Mod4Mask,
|
||||
}
|
||||
CLIPBOARD_RESTORE_DELAY_SEC = 0.15
|
||||
|
||||
|
||||
class X11Adapter:
|
||||
|
|
@ -70,17 +72,35 @@ class X11Adapter:
|
|||
thread = threading.Thread(target=self._listen, args=("Escape", callback), daemon=True)
|
||||
thread.start()
|
||||
|
||||
def inject_text(self, text: str, backend: str) -> None:
|
||||
def inject_text(
|
||||
self,
|
||||
text: str,
|
||||
backend: str,
|
||||
*,
|
||||
remove_transcription_from_clipboard: bool = False,
|
||||
) -> None:
|
||||
backend = (backend or "").strip().lower()
|
||||
if backend in ("", "clipboard"):
|
||||
previous_clipboard = None
|
||||
if remove_transcription_from_clipboard:
|
||||
previous_clipboard = self._read_clipboard_text()
|
||||
self._write_clipboard(text)
|
||||
self._paste_clipboard()
|
||||
if remove_transcription_from_clipboard:
|
||||
time.sleep(CLIPBOARD_RESTORE_DELAY_SEC)
|
||||
self._restore_clipboard_text(previous_clipboard)
|
||||
return
|
||||
if backend == "injection":
|
||||
self._type_text(text)
|
||||
return
|
||||
raise ValueError(f"unknown injection backend: {backend}")
|
||||
|
||||
def _read_clipboard_text(self) -> str | None:
|
||||
Gtk.init([])
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
text = clipboard.wait_for_text()
|
||||
return str(text) if text is not None else None
|
||||
|
||||
def run_tray(self, state_getter: Callable[[], str], on_quit: Callable[[], None]) -> None:
|
||||
self.menu = Gtk.Menu()
|
||||
quit_item = Gtk.MenuItem(label="Quit")
|
||||
|
|
@ -165,6 +185,14 @@ class X11Adapter:
|
|||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
def _restore_clipboard_text(self, text: str | None) -> None:
|
||||
Gtk.init([])
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
clipboard.set_text(text or "", -1)
|
||||
clipboard.store()
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
def _paste_clipboard(self) -> None:
|
||||
dpy = display.Display()
|
||||
self._send_combo(dpy, ["Control_L", "Shift_L", "v"])
|
||||
|
|
|
|||
|
|
@ -226,7 +226,13 @@ class Daemon:
|
|||
self.set_state(State.OUTPUTTING)
|
||||
logging.info("outputting started")
|
||||
backend = self.cfg.injection.backend
|
||||
self.desktop.inject_text(text, backend)
|
||||
self.desktop.inject_text(
|
||||
text,
|
||||
backend,
|
||||
remove_transcription_from_clipboard=(
|
||||
self.cfg.injection.remove_transcription_from_clipboard
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
logging.error("output failed: %s", exc)
|
||||
finally:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue