package tray import ( "log" "sync" "github.com/energye/systray" "git.davoryn.de/calic/syncwarden/internal/config" "git.davoryn.de/calic/syncwarden/internal/icons" stClient "git.davoryn.de/calic/syncwarden/internal/syncthing" ) // App manages the tray icon and Syncthing monitoring. type App struct { mu sync.Mutex cfg config.Config client *stClient.Client state icons.State statusItem *systray.MenuItem } // Run starts the tray application (blocking). func Run() { app := &App{} systray.Run(app.onReady, app.onExit) } func (a *App) onReady() { a.cfg = config.Load() // Auto-discover API key if not configured if a.cfg.SyncthingAPIKey == "" { if key, err := stClient.DiscoverAPIKey(); err == nil && key != "" { a.cfg.SyncthingAPIKey = key _ = config.Save(a.cfg) log.Printf("auto-discovered Syncthing API key") } } a.client = stClient.NewClient(a.cfg.BaseURL(), a.cfg.SyncthingAPIKey) // Set initial icon a.setState(icons.StateDisconnected) systray.SetTitle("SyncWarden") systray.SetTooltip("SyncWarden: connecting...") // Right-click shows menu systray.SetOnRClick(func(menu systray.IMenu) { menu.ShowMenu() }) // Double-click opens admin panel systray.SetOnDClick(func(menu systray.IMenu) { a.openPanel() }) // Build menu a.buildMenu() // Check connection go a.initialCheck() } func (a *App) onExit() { log.Println("SyncWarden exiting") } func (a *App) setState(s icons.State) { a.mu.Lock() a.state = s a.mu.Unlock() iconData, err := icons.Render(s) if err != nil { log.Printf("icon render error: %v", err) return } systray.SetIcon(iconData) } func (a *App) initialCheck() { _, err := a.client.Health() if err != nil { log.Printf("Syncthing not reachable: %v", err) a.setState(icons.StateDisconnected) systray.SetTooltip("SyncWarden: Syncthing not reachable") a.mu.Lock() if a.statusItem != nil { a.statusItem.SetTitle("Status: Disconnected") } a.mu.Unlock() return } log.Println("Syncthing is reachable") a.setState(icons.StateIdle) systray.SetTooltip("SyncWarden: Idle") a.mu.Lock() if a.statusItem != nil { a.statusItem.SetTitle("Status: Idle") } a.mu.Unlock() } func (a *App) openPanel() { launchPanel(a.cfg.BaseURL()) }