Files
talk/internal/notify/identity_windows.go
m3ta-chironandClaude Fable 5.1 2d9d2a86e9
release / release (push) Canceled after 0s
test / test (push) Canceled after 0s
feat(notify): Windows toasts under talk's own identity, icon on Linux
The daemon (and talk test --toast) now register the AppUserModelID
m3ta.talk under HKCU with display name and the installed icon, so
toasts appear as "talk" with the icon instead of Windows PowerShell;
the PowerShell identity stays as fallback until the registration has
run once. The redundant "talk" title line is dropped under the own
identity. notify.Setup bundles icon install, identity and the talk:
protocol handler for both callers. Linux notifications carry the
installed icon as app_icon. doctor names the active identity.

Verified on the Windows 11 VM: registry key, icon file, toast with
working Cancel button, exe icon; Linux via quickshell.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-25 12:43:14 +01:00

61 lines
1.7 KiB
Go

//go:build windows
package notify
import (
"fmt"
"sync"
"golang.org/x/sys/windows/registry"
)
// appIDKey is where Windows looks up the identity (name, icon) of an
// unpackaged program that shows toasts: HKCU, so no admin rights.
const appIDKey = `Software\Classes\AppUserModelId\` + AppID
var (
identityMu sync.Mutex
identityKnown bool
identityOK bool
)
// UseIcon registers talk's own toast identity for the current user: the
// AppUserModelID AppID with display name "talk" and the icon at iconPath.
// Toasts then appear as coming from talk instead of Windows PowerShell.
// Idempotent; rewritten on every call so it follows the icon's location.
func UseIcon(iconPath string) error {
setIconPath(iconPath)
k, _, err := registry.CreateKey(registry.CURRENT_USER, appIDKey, registry.SET_VALUE)
if err != nil {
return fmt.Errorf("registering toast identity: %w", err)
}
defer k.Close()
if err := k.SetStringValue("DisplayName", Title); err != nil {
return fmt.Errorf("registering toast identity: %w", err)
}
if err := k.SetStringValue("IconUri", iconPath); err != nil {
return fmt.Errorf("registering toast identity: %w", err)
}
identityMu.Lock()
identityKnown, identityOK = true, true
identityMu.Unlock()
return nil
}
// ownIdentity reports whether AppID is registered for this user (by this
// process or an earlier daemon start); checked once per process.
func ownIdentity() bool {
identityMu.Lock()
defer identityMu.Unlock()
if !identityKnown {
identityKnown = true
identityOK = false
if k, err := registry.OpenKey(registry.CURRENT_USER, appIDKey, registry.QUERY_VALUE); err == nil {
_, _, err := k.GetStringValue("IconUri")
identityOK = err == nil
k.Close()
}
}
return identityOK
}