Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions pkg/tray/tray.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ package tray

import (
"context"
"errors"
"os"
"os/exec"
"path/filepath"
"runtime"
"sync"

Expand Down Expand Up @@ -134,10 +137,7 @@ func openBrowser(url string) error {
cmd = "open"
args = []string{url}
case "windows":
// rundll32 invokes the URL handler directly, avoiding cmd.exe's
// metacharacter parsing that affects "cmd /c start <url>".
cmd = "rundll32"
args = []string{"url.dll,FileProtocolHandler", url}
return openBrowserWindows(url)
default:
cmd = "xdg-open"
args = []string{url}
Expand All @@ -146,6 +146,37 @@ func openBrowser(url string) error {
return exec.CommandContext(context.Background(), cmd, args...).Start()
}

func openBrowserWindows(url string) error {
var launchErrs []error

if windir := os.Getenv("WINDIR"); windir != "" {
rundll32Path := filepath.Join(windir, "System32", "rundll32.exe")
if _, err := os.Stat(rundll32Path); err == nil {
if err := exec.CommandContext(context.Background(), rundll32Path, "url.dll,FileProtocolHandler", url).Start(); err == nil {
return nil
} else {
launchErrs = append(launchErrs, err)
}
}
}

// Fallback to PATH lookup.
if err := exec.CommandContext(context.Background(), "rundll32", "url.dll,FileProtocolHandler", url).Start(); err == nil {
return nil
} else {
launchErrs = append(launchErrs, err)
}

// Final fallback for hardened environments where rundll32 is blocked.
if err := exec.CommandContext(context.Background(), "cmd", "/c", "start", "", url).Start(); err == nil {
return nil
} else {
launchErrs = append(launchErrs, err)
}

return errors.Join(launchErrs...)
}

// getIcon returns the icon bytes for the system tray.
func getIcon() []byte {
return iconData
Expand Down
Loading