Remove standalone fetcher, add setup tool with install/uninstall workflow
All checks were successful
Release / build (push) Successful in 1m45s
All checks were successful
Release / build (push) Successful in 1m45s
Drop claude-fetcher binary and cron job — the widget's built-in BackgroundFetcher is the sole fetcher now. Add cmd/setup with cross-platform install and uninstall (--uninstall): kills widget, removes binaries + autostart, cleans Claude Code statusline setting, optionally removes config dir. Also includes: browser-based login (chromedp), ICO wrapper for Windows tray icon, and reduced icon size (64px). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,15 +2,17 @@ package renderer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"math"
|
||||
"runtime"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
)
|
||||
|
||||
const iconSize = 256
|
||||
const iconSize = 64
|
||||
|
||||
// Claude orange for the starburst logo.
|
||||
var claudeOrange = color.RGBA{224, 123, 83, 255}
|
||||
@@ -93,8 +95,8 @@ func drawArc(dc *gg.Context, pct int) {
|
||||
|
||||
cx := float64(iconSize) / 2
|
||||
cy := float64(iconSize) / 2
|
||||
radius := float64(iconSize)/2 - 14 // inset from edge
|
||||
arcWidth := 28.0
|
||||
radius := float64(iconSize)/2 - 4 // inset from edge
|
||||
arcWidth := 7.0
|
||||
|
||||
startAngle := -math.Pi / 2 // 12 o'clock
|
||||
endAngle := startAngle + (float64(pct)/100)*2*math.Pi
|
||||
@@ -114,7 +116,7 @@ func RenderIcon(pct int) image.Image {
|
||||
return dc.Image()
|
||||
}
|
||||
|
||||
// RenderIconPNG generates the icon as PNG bytes (for systray).
|
||||
// RenderIconPNG generates the icon as PNG bytes.
|
||||
func RenderIconPNG(pct int) ([]byte, error) {
|
||||
img := RenderIcon(pct)
|
||||
var buf bytes.Buffer
|
||||
@@ -123,3 +125,54 @@ func RenderIconPNG(pct int) ([]byte, error) {
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// RenderIconForTray returns icon bytes suitable for systray.SetIcon:
|
||||
// ICO (PNG-compressed) on Windows, raw PNG on other platforms.
|
||||
func RenderIconForTray(pct int) ([]byte, error) {
|
||||
pngData, err := RenderIconPNG(pct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if runtime.GOOS != "windows" {
|
||||
return pngData, nil
|
||||
}
|
||||
return wrapPNGInICO(pngData, iconSize, iconSize), nil
|
||||
}
|
||||
|
||||
// wrapPNGInICO wraps raw PNG bytes in a minimal ICO container.
|
||||
// Windows Vista+ supports PNG-compressed ICO entries.
|
||||
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 // 0 means 256
|
||||
}
|
||||
h := byte(height)
|
||||
if height >= 256 {
|
||||
h = 0
|
||||
}
|
||||
buf.WriteByte(w) // Width
|
||||
buf.WriteByte(h) // Height
|
||||
buf.WriteByte(0) // ColorCount (0 = no palette)
|
||||
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
|
||||
|
||||
// PNG image data
|
||||
buf.Write(pngData)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user