package icons import ( "bytes" _ "embed" "encoding/binary" "image/png" "runtime" ) // Pre-rendered tray icons (64px) generated from Stammtisch wood texture // with the Syncthing motif applied via darken blend. // Regenerate with: go run ./cmd/icongen/ -wood /path/to/stammtisch.png //go:embed tray_idle.png var trayIdlePNG []byte //go:embed tray_syncing.png var traySyncingPNG []byte //go:embed tray_paused.png var trayPausedPNG []byte //go:embed tray_error.png var trayErrorPNG []byte //go:embed tray_disconnected.png var trayDisconnectedPNG []byte // Render returns the tray icon for the given state. // Returns ICO bytes on Windows, PNG bytes on other platforms. func Render(state State) ([]byte, error) { pngData := trayPNG(state) if runtime.GOOS == "windows" { return wrapPNGInICO(pngData, 64, 64), nil } return pngData, nil } func trayPNG(state State) []byte { switch state { case StateIdle: return trayIdlePNG case StateSyncing: return traySyncingPNG case StatePaused: return trayPausedPNG case StateError: return trayErrorPNG case StateDisconnected: return trayDisconnectedPNG default: return trayDisconnectedPNG } } // RenderPNG returns a PNG icon at 64px for the given state. func RenderPNG(state State, size int) ([]byte, error) { // Pre-rendered icons are only 64px; return them directly. // For other sizes, the static assets in assets/ should be used. return trayPNG(state), nil } // IconSize returns the size of the pre-rendered tray icons. func IconSize() (int, int) { data := trayIdlePNG img, err := png.Decode(bytes.NewReader(data)) if err != nil { return 64, 64 } b := img.Bounds() return b.Dx(), b.Dy() } // wrapPNGInICO wraps raw PNG bytes in a minimal ICO container. func wrapPNGInICO(pngData []byte, width, height int) []byte { const headerSize = 6 const entrySize = 16 imageOffset := headerSize + entrySize buf := new(bytes.Buffer) // ICONDIR header binary.Write(buf, binary.LittleEndian, uint16(0)) // Reserved binary.Write(buf, binary.LittleEndian, uint16(1)) // Type: 1 = icon binary.Write(buf, binary.LittleEndian, uint16(1)) // Count: 1 image // ICONDIRENTRY w := byte(width) if width >= 256 { w = 0 } h := byte(height) if height >= 256 { h = 0 } buf.WriteByte(w) buf.WriteByte(h) buf.WriteByte(0) // ColorCount buf.WriteByte(0) // Reserved binary.Write(buf, binary.LittleEndian, uint16(1)) // Planes binary.Write(buf, binary.LittleEndian, uint16(32)) // BitCount binary.Write(buf, binary.LittleEndian, uint32(len(pngData))) // BytesInRes binary.Write(buf, binary.LittleEndian, uint32(imageOffset)) // ImageOffset buf.Write(pngData) return buf.Bytes() }