//go:build windows package notify import ( "fmt" "os/exec" "strings" ) // PowerShellToast raises a Windows toast notification by invoking // PowerShell with an AppId-tagged script — the same mechanism go-toast // uses, without the dependency. type PowerShellToast struct { AppID string } // Platform returns the PowerShell toast sender on Windows. func Platform() Sender { return PowerShellToast{AppID: "agent-lib"} } var toastScript = `$app = '%[1]s' [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null $tmpl = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02) $txt = $tmpl.GetElementsByTagName('text') $txt.Item(0).AppendChild($tmpl.CreateTextNode('%[2]s')) | Out-Null $txt.Item(1).AppendChild($tmpl.CreateTextNode('%[3]s')) | Out-Null $toast = [Windows.UI.Notifications.ToastNotification]::new($tmpl) [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($app).Show($toast)` // Send fires one toast. It requires an interactive user session; callers // must treat errors as non-fatal. func (p PowerShellToast) Send(title, body string) error { appID := p.AppID if appID == "" { appID = "agent-lib" } script := fmt.Sprintf(toastScript, escape(appID), escape(title), escape(body)) cmd := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command", script) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("toast failed: %v: %s", err, strings.TrimSpace(string(out))) } return nil } // escape makes s safe inside a PowerShell single-quoted string. func escape(s string) string { r := strings.NewReplacer("'", "''", "\n", " ", "\r", " ") return r.Replace(s) }