Files
akukanara f4a88f4b2c
golangci-lint / lint (push) Failing after 4s
feat(gui): redesign kanholec GUI with modern interface and WiX v7 installer
- Complete GUI overhaul with Fyne framework
  * Modern dark theme with indigo/pink accent colors
  * 4-step setup wizard (Welcome → Server → Auth → Finish)
  * Dashboard with proxy list, real-time logs, and config viewer
  * Windows Service manager (install/start/stop/uninstall)
  * System tray integration with quick controls

- WiX v7 MSI installer
  * Auto-registers as Windows Service (manual start)
  * Feature tree UI for optional components
  * Desktop shortcut and PATH options
  * Build script: packaging/windows/build-msi.ps1

- Build system updates
  * Added kanholec-windows-gui target (CGO required)
  * Added kanholec-windows-msi target
  * Separate main_gui.go entry point for GUI builds

GUI binary: bin/kanholec-windows-amd64.exe (31.4 MB)
MSI installer: bin/kanholec-0.69.0-amd64.msi (12.1 MB)
2026-05-29 22:53:41 +07:00

134 lines
2.8 KiB
Go

//go:build kanholec_gui
package gui
import (
"fyne.io/fyne/v2"
"fyne.io/systray"
)
type TrayManager struct {
app *App
running bool
mShow *systray.MenuItem
mStatus *systray.MenuItem
mStart *systray.MenuItem
mStop *systray.MenuItem
mQuit *systray.MenuItem
}
func NewTrayManager(app *App) *TrayManager {
return &TrayManager{app: app}
}
func (t *TrayManager) Start() {
go t.run()
}
func (t *TrayManager) run() {
defer func() {
recover()
}()
systray.Run(t.onReady, t.onExit)
}
func (t *TrayManager) onReady() {
t.running = true
systray.SetIcon(trayIconData())
systray.SetTitle("kanholec")
systray.SetTooltip("kanholec - kanhole Client")
t.mShow = systray.AddMenuItem("Show Dashboard", "Open kanholec GUI")
systray.AddSeparator()
t.mStatus = systray.AddMenuItem("Status: Disconnected", "")
t.mStatus.Disable()
systray.AddSeparator()
t.mStart = systray.AddMenuItem("Start Client", "Start the kanholec client")
t.mStop = systray.AddMenuItem("Stop Client", "Stop the kanholec client")
t.mStop.Disable()
systray.AddSeparator()
t.mQuit = systray.AddMenuItem("Quit", "Exit kanholec")
go func() {
for {
select {
case <-t.mShow.ClickedCh:
if t.app.window != nil {
t.app.window.Show()
t.app.window.RequestFocus()
}
case <-t.mStart.ClickedCh:
t.app.startClient()
t.mStart.Disable()
t.mStop.Enable()
t.mStatus.SetTitle("Status: Running")
case <-t.mStop.ClickedCh:
t.app.stopClient()
t.mStart.Enable()
t.mStop.Disable()
t.mStatus.SetTitle("Status: Stopped")
case <-t.mQuit.ClickedCh:
t.app.Quit()
systray.Quit()
return
}
}
}()
}
func (t *TrayManager) onExit() {
t.running = false
}
func (t *TrayManager) SetStatus(status string) {
if !t.running || t.mStatus == nil {
return
}
t.mStatus.SetTitle("Status: " + status)
}
func (t *TrayManager) SetRunning(running bool) {
if !t.running {
return
}
if running {
t.mStart.Disable()
t.mStop.Enable()
t.mStatus.SetTitle("Status: Running")
} else {
t.mStart.Enable()
t.mStop.Disable()
t.mStatus.SetTitle("Status: Stopped")
}
}
func (t *TrayManager) Stop() {
if t.running {
systray.Quit()
}
}
func (t *TrayManager) IsSupported() bool {
return t.running
}
func trayIconResource() *fyne.StaticResource {
return &fyne.StaticResource{
StaticName: "kanholec.ico",
StaticContent: trayIconData(),
}
}
func trayIconData() []byte {
return []byte{
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, 0x00,
0x20, 0x00, 0x68, 0x04, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
}