summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2026-07-24 13:02:08 +0200
committerYuval Adam <_@yuv.al>2026-07-24 13:02:08 +0200
commit213b7d931056e09d3b0f39c049f0fce9ead05911 (patch)
tree472e269f1684d4c0e1358ee362b7d459289e3306
parent6e88c83227a94b331bbb3ac08152fee77611ddd3 (diff)
Disable desktop notifications by default
-rw-r--r--README.md2
-rw-r--r--SPEC.md5
-rw-r--r--src/parley/config.py5
-rw-r--r--src/parley/daemon.py20
-rw-r--r--src/parley/ibus_engine.py25
5 files changed, 47 insertions, 10 deletions
diff --git a/README.md b/README.md
index 9c2ecdb..6ec19eb 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ With Parley installed and the shortcut configured:
4. Press `Super+Shift+D` again.
5. Wait for local transcription and insertion.
-GNOME displays its microphone privacy indicator while recording. The Parley top-bar indicator shows idle, recording, transcribing, and error states and provides start/stop, cancel, copy-last, and open-folder actions.
+GNOME displays its microphone privacy indicator while recording. The Parley top-bar indicator shows idle, recording, transcribing, and error states and provides start/stop, cancel, copy-last, and open-folder actions. Desktop notifications are disabled by default.
Useful fallback commands:
diff --git a/SPEC.md b/SPEC.md
index 0feee57..451ded5 100644
--- a/SPEC.md
+++ b/SPEC.md
@@ -327,8 +327,9 @@ The extension shortcut replaces or disables the development custom shortcut to p
## 9. Notifications and clipboard
- Copy every successful transcript to the clipboard.
-- Notify on transcription completion when no visible UI feedback is available.
-- Notify when insertion falls back to manual paste.
+- Desktop notifications are disabled by default and can be explicitly enabled.
+- When enabled, notify on transcription completion when no visible UI feedback is available.
+- When enabled, notify when insertion falls back to manual paste.
- Surface concise errors while retaining details in the journal.
- Avoid repeated noisy notifications when the top-bar state already makes progress obvious.
- Use a registered desktop application identity for production notifications; `notify-send` is acceptable during the prototype phase.
diff --git a/src/parley/config.py b/src/parley/config.py
index 89cf59c..4ba1202 100644
--- a/src/parley/config.py
+++ b/src/parley/config.py
@@ -33,6 +33,7 @@ class Config:
sample_rate: int = 16_000
auto_insert: bool = False
insertion_mode: str = "clipboard"
+ notifications_enabled: bool = False
@classmethod
def from_environment(cls) -> "Config":
@@ -65,6 +66,10 @@ class Config:
auto_insert=os.environ.get("PARLEY_AUTO_INSERT", "0").lower()
in {"1", "true", "yes", "on"},
insertion_mode=os.environ.get("PARLEY_INSERTION_MODE", "clipboard"),
+ notifications_enabled=os.environ.get(
+ "PARLEY_NOTIFICATIONS", "0"
+ ).lower()
+ in {"1", "true", "yes", "on"},
)
def validate(self) -> None:
diff --git a/src/parley/daemon.py b/src/parley/daemon.py
index 63d4697..121ff24 100644
--- a/src/parley/daemon.py
+++ b/src/parley/daemon.py
@@ -31,12 +31,16 @@ class Daemon:
).read_text(encoding="utf-8")
self.node_info = Gio.DBusNodeInfo.new_for_xml(xml)
self.interface_info = self.node_info.interfaces[0]
+ resolved_config = config or Config.from_environment()
self.loop = GLib.MainLoop()
- self.ibus = IBusIntegration()
+ self.ibus = IBusIntegration(
+ activate_on_connect=resolved_config.auto_insert
+ and resolved_config.insertion_mode == "ibus"
+ )
self.connection: Gio.DBusConnection | None = None
self.registration_id = 0
self.controller = Controller(
- config or Config.from_environment(),
+ resolved_config,
dispatch=lambda callback: GLib.idle_add(callback),
on_state=self._on_state,
on_transcript=self._on_transcript,
@@ -204,20 +208,24 @@ class Daemon:
GLib.Variant("(sbs)", ("ibus", False, str(error))),
)
fallback = "Copied; press Ctrl+V" if clipboard_error is None else "Transcript saved"
- notify("Automatic insertion unavailable", fallback)
+ self._notify("Automatic insertion unavailable", fallback)
else:
self._emit(
"InsertionFinished",
GLib.Variant("(sbs)", ("ibus", True, message)),
)
- notify("Transcription ready", "Inserted through IBus")
+ self._notify("Transcription ready", "Inserted through IBus")
elif clipboard_error is None:
- notify("Transcription ready", "Copied to the clipboard")
+ self._notify("Transcription ready", "Copied to the clipboard")
def _on_error(self, code: str, message: str) -> None:
self._emit("Error", GLib.Variant("(ss)", (code, message)))
self._properties_changed("LastError", GLib.Variant("s", message))
- notify("Parley error", message)
+ self._notify("Parley error", message)
+
+ def _notify(self, summary: str, body: str) -> None:
+ if self.controller.config.notifications_enabled:
+ notify(summary, body)
def _properties_changed(self, *items) -> None:
changed = {items[index]: items[index + 1] for index in range(0, len(items), 2)}
diff --git a/src/parley/ibus_engine.py b/src/parley/ibus_engine.py
index 7f65a99..86c155c 100644
--- a/src/parley/ibus_engine.py
+++ b/src/parley/ibus_engine.py
@@ -49,13 +49,15 @@ class ParleyIBusEngine(IBus.Engine):
class IBusIntegration:
"""Register Parley's engine and reconnect after a rare IBus restart."""
- def __init__(self) -> None:
+ def __init__(self, *, activate_on_connect: bool = False) -> None:
IBus.init()
+ self.activate_on_connect = activate_on_connect
self.bus: IBus.Bus | None = None
self.factory: IBus.Factory | None = None
self.component: IBus.Component | None = None
self._disconnect_signal = 0
self._reconnect_source = 0
+ self._activation_source = 0
self._closed = False
self._connect()
@@ -78,6 +80,9 @@ class IBusIntegration:
if self._reconnect_source:
GLib.source_remove(self._reconnect_source)
self._reconnect_source = 0
+ if self._activation_source:
+ GLib.source_remove(self._activation_source)
+ self._activation_source = 0
self._clear_connection()
def _connect(self) -> bool:
@@ -122,8 +127,26 @@ class IBusIntegration:
self.factory = factory
self.component = component
self._disconnect_signal = bus.connect("disconnected", self._on_disconnected)
+ if self.activate_on_connect:
+ self._activation_source = GLib.timeout_add(100, self._activate)
return GLib.SOURCE_REMOVE
+ def _activate(self) -> bool:
+ self._activation_source = 0
+ if self.available:
+ self.bus.set_global_engine_async(
+ "parley", 2_000, None, self._activation_finished
+ )
+ return GLib.SOURCE_REMOVE
+
+ def _activation_finished(self, bus: IBus.Bus, result) -> None:
+ try:
+ bus.set_global_engine_async_finish(result)
+ except GLib.Error:
+ # Insertion remains available as a clipboard fallback. A later
+ # explicit selection can activate the engine.
+ pass
+
def _on_disconnected(self, bus: IBus.Bus) -> None:
self._clear_connection()
self._schedule_reconnect()