//go:build windows package platform import ( "os" "path/filepath" "sync" toast "git.sr.ht/~jackmordaunt/go-toast/v2" "golang.org/x/sys/windows/registry" ) const ( // toastAppID 是稳定的内部 AppUserModelID;Windows 通知设置中的用户可见名称由 // toastDisplayName 单独写入 DisplayName,避免为了改显示名破坏系统身份。 toastAppID = "net.commilitia.cdrop" toastDisplayName = "Commilitia Drop" // toastGUID 固定不变——它把通知归属到注册表里的本应用条目;更换会让既有通知 // 失去归属。 toastGUID = "{c4d8e2a1-6b3f-4e7a-9c2d-1f5b8a0e3d6c}" ) var toastInit sync.Once // InitializeNotifications registers the stable Windows notification identity // and corrects its user-visible name. It is safe to call repeatedly. func InitializeNotifications() { toastInit.Do(func() { data := toast.AppData{AppID: toastAppID, GUID: toastGUID} if exe, err := os.Executable(); err == nil { data.ActivationExe = exe } _ = toast.SetAppData(data) // go-toast v2.0.3 没有独立 DisplayName 字段,会把 AppID 写进 // DisplayName,且已有值时不更新;在同一稳定键上显式覆盖显示名。 appKey := filepath.Join( "SOFTWARE", "Classes", "AppUserModelId", toastAppID, ) if key, err := registry.OpenKey( registry.CURRENT_USER, appKey, registry.SET_VALUE, ); err == nil { _ = key.SetStringValue("DisplayName", toastDisplayName) _ = key.Close() } }) } // Notify shows a native Windows toast. go-toast renders via the WinRT/COM path // (PowerShell fallback when the AppID isn't registered), so it works without code // signing — SmartScreen only gates the installer, not notifications. Best-effort: // errors are swallowed. func Notify(title, body string) { InitializeNotifications() n := toast.Notification{ AppID: toastAppID, Title: title, Body: body, } _ = n.Push() }