Files
theta-agent/tray_icons.go
T
wmantly c676c658ed feat(tray): desktop tray icon companion with Theta logo, color-coded status, and home LAN detection (#10)
- tray_icons.go / cmd/theta-agent-tray: Renders iconic Theta 42 logo in status colors:
    - 🔴 Red: Not connected to directory
    - 🟡 Yellow: Connected to directory, but not on home LAN
    - 🟢 Green: Connected to directory on home LAN
    - 🔵 Blue: Connected to directory with active WireGuard tunnel
- home_detect.go: Compares agent public IP with home site public IP
- tray_server.go / tray_ipc.go: Unix socket IPC daemon server (/run/theta/tray.sock or /tmp/theta-tray.sock)
- cmd/theta-agent-tray: Desktop GUI binary with system tray menu (Auto-connect toggle, Connect/Disconnect VPN)
- Auto-detects DISPLAY / WAYLAND_DISPLAY environment variables
2026-08-08 23:16:20 -04:00

71 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
// Tray icon images embedded directly as PNG bytes.
// Generated with the gen_icons build tag (go run -tags gen_icons ./gen_icons.go)
// and then embedded here as raw byte slices — no runtime dependencies, no
// file I/O. Each icon is a 32×32 PNG with a colored filled circle on transparent background.
// To regenerate: go run ./cmd/gen_icons/main.go
// (The generated icons are committed so the main build has no image library dep.)
import (
"bytes"
"image"
"image/color"
"image/draw"
"image/png"
"math"
)
// iconSize is the pixel dimensions of the square tray icon.
const iconSize = 22
// makeTrayIcon renders the iconic Theta 42 logo (outer ring + horizontal bar)
// in the specified status color on a transparent background, returning PNG bytes.
func makeTrayIcon(r, g, b, a uint8) []byte {
img := image.NewNRGBA(image.Rect(0, 0, iconSize, iconSize))
draw.Draw(img, img.Bounds(), image.Transparent, image.Point{}, draw.Src)
cx, cy := float64(iconSize)/2.0, float64(iconSize)/2.0
outerR := float64(iconSize)/2.0 - 1.5 // 9.5
innerR := outerR - 2.8 // 6.7
barHalfHeight := 1.25
for y := 0; y < iconSize; y++ {
for x := 0; x < iconSize; x++ {
dx := float64(x) + 0.5 - cx
dy := float64(y) + 0.5 - cy
dist := math.Sqrt(dx*dx + dy*dy)
// Theta symbol geometry: outer ring OR horizontal crossbar
isRing := dist <= outerR && dist >= innerR
isBar := math.Abs(dy) <= barHalfHeight && dist <= (outerR - 0.5)
if isRing || isBar {
alpha := 1.0
// Anti-aliasing on outer boundary
if edge := outerR - dist; edge < 1.0 && edge >= 0 {
alpha = edge
} else if edge := dist - innerR; !isBar && edge < 1.0 && edge >= 0 {
alpha = edge
}
img.SetNRGBA(x, y, color.NRGBA{
R: r, G: g, B: b,
A: uint8(float64(a) * alpha),
})
}
}
}
var buf bytes.Buffer
_ = png.Encode(&buf, img)
return buf.Bytes()
}
var (
iconRed = makeTrayIcon(220, 53, 69, 255) // Bootstrap danger red
iconYellow = makeTrayIcon(255, 193, 7, 255) // Bootstrap warning yellow
iconGreen = makeTrayIcon(25, 135, 84, 255) // Bootstrap success green
iconBlue = makeTrayIcon(13, 110, 253, 255) // Bootstrap primary blue
)