Menu ▾ ▴

#167 Linux: notification action buttons, KDE inline quick-reply, and sound hint

closed
nobody
2026-07-24
2026-07-22
Anonymous
No

Originally created by: kristofferR

Quick reply from notifications (#86) is scoped to Windows/macOS — Linux can have it too, plus a themed notification sound. (Ref [#86])

Part 1 — sound hint (tiny, ship first). In show_native_notification, when sound is on set Hint::SoundName("message-new-instant") (freedesktop sound naming spec); keep SuppressSound when off. KDE honors it with a sound theme installed; GNOME plays its default — both fine. Windows path untouched.

Part 2 — actions + inline reply.

Key constraint (verified in notify-rust 4.18 source): its XDG signal loop only surfaces ActionInvoked/NotificationClosed; KDE's inline-reply text arrives on a separate NotificationReplied(u32, String) signal it never exposes. So: show the notification via notify-rust, but wait on a custom zbus signal loop (NotificationHandle::id() is public for filtering). Add a Linux-only direct zbus = "5" dep — already in-tree via ksni/ashpd, zero build cost.

  • Capability probe: OnceLock-cached notify_rust::get_capabilities() → {actions, inline_reply}.
  • Reply eligibility: Linux ∧ !hide_notification_preview (never offer a blind reply on a redacted banner) ∧ a validated thread path. Message notifications only; sync alerts keep just the default action.
  • Notification build: keep action("default", "Open"); when eligible and the server supports inline reply, add action("inline-reply", "Reply") + x-kde-reply-placeholder-text hint. All gated #[cfg(all(unix, not(target_os = "macos")))] — Windows behavior unchanged.
  • Response loop: per-notification thread (same model as today) on a blocking zbus connection with match rules for ActionInvoked / NotificationClosed / NotificationReplied, filtered by id. ActionInvoked("inline-reply") without a reply signal within ~2 s degrades to open-thread-and-focus-composer. Reply text capped ~2000 chars; empty → treated as a click.
  • Reply delivery: Rust takes the stored notification route, evals window.__carrierQuickReply?.(path, text, id) (JSON-escaping is load-bearing) without raising the window, and blocks ~20 s on an ack via a new carrier:reply-result event ({id, ok}). On failure/timeout: focus the window, open the thread (text stays in the composer), and show one fixed-string "Reply not sent — opening the conversation" notification.
  • New inject/src/messenger/features/quick-reply.ts: __carrierOpenThread(path) → poll (~250 ms, 12 s budget) until the URL contains the /t/<id>/ and the composer exists ([role="main"] [contenteditable="true"][role="textbox"], same selector as conversation-actions.ts) → insert via document.execCommand("insertText") (the one path that round-trips into Lexical on WebKitGTK) → verify textContent → click Messenger's own send button via the existing buttonByLabel helper (avoids composer-keys.ts Enter interception) → confirm the composer empties → emit the ack. Every failure path emits ok: false with content-free diag() breadcrumbs. The URL thread-id check before insertion eliminates wrong-recipient risk.
  • Pure state machines go in inject/src/messenger/lib/quick-reply.ts for bun test; Rust unit tests for eligibility, response classification, ack routing, sound-hint selection.

Stretch (separate, after the reply path soaks): a Reply button on servers without inline reply (opens + focuses composer), then "Mark as read" via hover-menu automation (fragile).

Manual testing: KDE inline reply with the window hidden to tray (message sends, window stays hidden); failure path preserves text; hide-preview on → no reply field; two concurrent notifications route independently; a debug --features mcp build can drive the page flow via tauri-mcp.

Part of [#171].

Related

Tickets: #171
Tickets: #173
Tickets: #179
Tickets: #180
Tickets: #181
Tickets: #187
Tickets: #195
Tickets: #86

Discussion

  • Anonymous

    Anonymous - 2026-07-24

    Originally posted by: kristofferR

    Implemented and merged in [#195]: Linux notifications now support Open and Reply actions, KDE inline replies, sound hints, secure daemon-sender validation, and bounded response handling. Closing as complete.

     

    Related

    Tickets: #195

  • Anonymous

    Anonymous - 2026-07-24

    Ticket changed by: kristofferR

    • status: open --> closed
     

Log in to post a comment.