package renderer import ( "bytes" "image" "image/color" "image/png" "math" "github.com/fogleman/gg" ) const iconSize = 256 // Claude orange for the starburst logo. var claudeOrange = color.RGBA{224, 123, 83, 255} // arcColors maps usage percentage thresholds to colors. var arcColors = []struct { threshold int color color.RGBA }{ {10, color.RGBA{76, 175, 80, 255}}, // green {20, color.RGBA{67, 160, 71, 255}}, // darker green {30, color.RGBA{124, 179, 66, 255}}, // light green {40, color.RGBA{192, 202, 51, 255}}, // lime {50, color.RGBA{253, 216, 53, 255}}, // yellow {60, color.RGBA{255, 193, 7, 255}}, // amber {70, color.RGBA{255, 179, 0, 255}}, // darker amber {80, color.RGBA{255, 152, 0, 255}}, // orange {90, color.RGBA{255, 87, 34, 255}}, // deep orange {100, color.RGBA{244, 67, 54, 255}}, // red } func getArcColor(pct int) color.RGBA { for _, ac := range arcColors { if pct <= ac.threshold { return ac.color } } return arcColors[len(arcColors)-1].color } // drawStarburst draws the 8-petal Claude logo. func drawStarburst(dc *gg.Context) { cx := float64(iconSize) / 2 cy := float64(iconSize) / 2 petalLen := float64(iconSize) * 0.38 petalWidth := float64(iconSize) * 0.10 centerRadius := float64(iconSize) * 0.04 dc.SetColor(claudeOrange) for i := 0; i < 8; i++ { angle := float64(i) * (2 * math.Pi / 8) // Tip of the petal tipX := cx + petalLen*math.Cos(angle) tipY := cy + petalLen*math.Sin(angle) // Base points (perpendicular to angle) perpAngle := angle + math.Pi/2 baseX1 := cx + petalWidth*math.Cos(perpAngle) baseY1 := cy + petalWidth*math.Sin(perpAngle) baseX2 := cx - petalWidth*math.Cos(perpAngle) baseY2 := cy - petalWidth*math.Sin(perpAngle) // Inner point (slightly behind center for petal shape) innerX := cx - petalWidth*0.5*math.Cos(angle) innerY := cy - petalWidth*0.5*math.Sin(angle) dc.MoveTo(innerX, innerY) dc.LineTo(baseX1, baseY1) dc.LineTo(tipX, tipY) dc.LineTo(baseX2, baseY2) dc.ClosePath() dc.Fill() } // Center dot dc.DrawCircle(cx, cy, centerRadius) dc.Fill() } // drawArc draws a circular progress arc. func drawArc(dc *gg.Context, pct int) { if pct <= 0 { return } if pct > 100 { pct = 100 } cx := float64(iconSize) / 2 cy := float64(iconSize) / 2 radius := float64(iconSize)/2 - 14 // inset from edge arcWidth := 28.0 startAngle := -math.Pi / 2 // 12 o'clock endAngle := startAngle + (float64(pct)/100)*2*math.Pi dc.SetColor(getArcColor(pct)) dc.SetLineWidth(arcWidth) dc.SetLineCap(gg.LineCapButt) dc.DrawArc(cx, cy, radius, startAngle, endAngle) dc.Stroke() } // RenderIcon generates a 256x256 PNG icon with starburst and usage arc. func RenderIcon(pct int) image.Image { dc := gg.NewContext(iconSize, iconSize) drawStarburst(dc) drawArc(dc, pct) return dc.Image() } // RenderIconPNG generates the icon as PNG bytes (for systray). func RenderIconPNG(pct int) ([]byte, error) { img := RenderIcon(pct) var buf bytes.Buffer if err := png.Encode(&buf, img); err != nil { return nil, err } return buf.Bytes(), nil }