feat(notify): Cancel button on Windows toasts via the talk: protocol handler
Windows toasts cannot call back into the daemon, so the Cancel button opens a talk: link instead: `talk:cancel?id=<id>&port=<port>`. The daemon registers the talk: URL protocol for the current user (HKCU, no admin) and points it at `talk uri "%1"`; Windows starts that on the click, and `talk uri` posts to the new endpoint POST /cancel-item, which cancels only the announcement with that id (queue.CancelItem). The toast is tagged and removed again when the announcement ends. For now the toast is shown under Windows PowerShell's app identity, which unpackaged programs can use without a Start Menu shortcut. Verified on the Windows 11 VM: registry entry written by the daemon, Start-Process "talk:cancel?id=…&port=7800" (what the button does) stops the playing announcement within two seconds, no toast errors in the log. Tests: `talk uri` against a real handler on a loopback port (playing vs waiting id, dead daemon, talk:test, garbage links), POST /cancel-item process test (only the playing item, waiting one plays next, 400 without id). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
83a15af9c5
commit
00eb76128b
16 files changed
+446
-22
No files matched your search
@@ -20,6 +20,7 @@ dependencies.
|
||||
| `talk mute` / `talk unmute` | mute all announcements persistently / turn them back on |
|
||||
| `talk status` | daemon status (queue, mute, provider, laws, version) |
|
||||
| `talk cancel` | cancel the playing and all queued announcements |
|
||||
| `talk uri <talk:…>` | handler behind the `talk:` links of notification buttons (Windows); not called by hand |
|
||||
| `talk daemon run\|start\|stop` | run the daemon in the foreground / start it in the background / stop it (normally not needed, see below) |
|
||||
| `talk voices` | known voices per provider and language, default marked (`--provider`, `--lang`, `--json`) |
|
||||
| `talk test` | speak a test sentence; shows provider and timings (`--lang`, `--provider`, `--voice`); `--toast` sends a test notification instead |
|
||||
@@ -98,13 +99,22 @@ Keys never go into config.toml.
|
||||
### Notifications with a Cancel button
|
||||
|
||||
Every announcement that plays also shows a desktop notification with its
|
||||
text and, on Linux, a **Cancel** button (German: Abbrechen) that stops that
|
||||
text and a **Cancel** button (German: Abbrechen) that stops that
|
||||
announcement without a terminal. The notification disappears when the
|
||||
announcement ends. talk talks to the freedesktop notification service on the
|
||||
session bus directly (any daemon with the "actions" capability: mako, dunst,
|
||||
GNOME Shell, KDE, quickshell); without a session bus it falls back to
|
||||
notify-send, on macOS to Notification Center and on Windows to a toast, all
|
||||
without a button. Turn it off with:
|
||||
announcement ends.
|
||||
|
||||
- **Linux:** talk talks to the freedesktop notification service on the
|
||||
session bus directly (any daemon with the "actions" capability: mako,
|
||||
dunst, GNOME Shell, KDE, quickshell) and receives the click over D-Bus.
|
||||
Without a session bus it falls back to notify-send without a button.
|
||||
- **Windows:** a toast with a Cancel button. The button opens a `talk:`
|
||||
link; the daemon registers that URL protocol for the current user (HKCU,
|
||||
no admin rights) so Windows starts `talk uri talk:cancel?id=…`, which
|
||||
tells the daemon to stop the announcement. The toast appears under
|
||||
Windows PowerShell's identity for now.
|
||||
- **macOS:** Notification Center, no button yet.
|
||||
|
||||
Turn it off with:
|
||||
|
||||
```toml
|
||||
[notify]
|
||||
@@ -159,6 +169,7 @@ mute` writes the mute file directly.
|
||||
| `OPTIONS /speak` | CORS preflight → `204` with allow headers when the `Origin` is listed in `server.allowed_origins` (see below); otherwise `204` without CORS headers |
|
||||
| `GET /status` | `{"version", "queue", "queue_ids", "current", "muted", "provider", "rate_limit": {"interval", "allow_high_burst", "next_slot"}, "quiet_hours": {"window", "active", "mode", "until"}, "limits": {"max_text", "max_age"}, "mcp": true, "cors": [origins]}` |
|
||||
| `POST /cancel` | aborts the playing announcement and empties the queue → `200 {"cancelled":n}` |
|
||||
| `POST /cancel-item` | `{"id":"…"}` cancels only that announcement if it is playing (notification Cancel button) → `200 {"cancelled":bool}` |
|
||||
| `POST /mute` / `POST /unmute` | persistent mute state → `200 {"muted":bool}` |
|
||||
| `POST /shutdown` | shuts the daemon down (`talk daemon stop`) → `200 {"ok":true}` |
|
||||
| `POST /mcp` | MCP (Streamable HTTP): JSON-RPC `initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`; one tool `speak` (see below) |
|
||||
|
||||
Reference in new issue
Block a user