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
@@ -15,6 +15,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -84,6 +85,16 @@ func Run(ctx context.Context, opts Options) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Windows: the toast's Cancel button opens a talk: link; register the
|
||||
// protocol handler for this user so the click starts `talk uri`.
|
||||
if cfg.Notify.OnSpeak && runtime.GOOS == "windows" {
|
||||
if exe, err := os.Executable(); err == nil {
|
||||
if err := notify.RegisterProtocol(exe); err != nil {
|
||||
logger.Printf("warning: %v; the toast's Cancel button will not work", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Desktop notification per announcement (notify.on_speak): shown when the
|
||||
// announcement starts, with a Cancel button where the platform has one,
|
||||
// closed when it ends. Best effort: the first failure is logged, later
|
||||
@@ -98,7 +109,12 @@ func Run(ctx context.Context, opts Options) error {
|
||||
if lang == "de" {
|
||||
label = "Abbrechen"
|
||||
}
|
||||
closeFn, err := notify.Announce(ctx, item.Text, label, func() {
|
||||
closeFn, err := notify.Announce(ctx, notify.Announcement{
|
||||
Text: item.Text,
|
||||
CancelLabel: label,
|
||||
CancelURI: fmt.Sprintf("talk:cancel?id=%s&port=%d", item.ID, cfg.Server.Port),
|
||||
Tag: item.ID,
|
||||
}, func() {
|
||||
if q.CancelItem(item.ID) {
|
||||
logger.Printf("announcement %s cancelled from its notification", item.ID)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user