Files
claude-statusline/claude_usage_widget/renderer.py
Axel Meyer 6a1b4bd022 Add desktop tray widget + installer wizard
- Desktop widget (Python/pystray): system tray icon showing 5h usage as
  circular progress bar with Claude starburst logo, 10-step green-to-red
  color scale, right-click menu with usage stats and configuration
- Shared cache: both widget and CLI statusline read/write the same
  /tmp/claude_usage.json — only one fetcher needs to run
- Installer wizard (install_wizard.py): interactive cross-platform setup
  with component selection, session key prompt, cron/autostart config
- OS wrappers: install.sh (Linux/macOS) and install.ps1 (Windows) find
  Python 3.9+ and launch the wizard
- README with topology diagram, usage docs, and configuration reference

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:54:32 +00:00

125 lines
3.6 KiB
Python

"""Icon renderer: Claude starburst logo + circular usage arc."""
import math
from PIL import Image, ImageDraw
SIZE = 128
CENTER = SIZE // 2
ARC_WIDTH = 16
ARC_RADIUS = (SIZE - ARC_WIDTH) // 2 # inner edge of arc
# Color thresholds for the usage arc (10% increments)
_COLORS = [
(10, "#4CAF50"), # green
(20, "#43A047"), # green (darker)
(30, "#7CB342"), # light green
(40, "#C0CA33"), # lime
(50, "#FDD835"), # yellow
(60, "#FFC107"), # amber
(70, "#FFB300"), # amber (darker)
(80, "#FF9800"), # orange
(90, "#FF5722"), # deep orange
(100, "#F44336"), # red
]
def _hex_to_rgba(hex_color, alpha=255):
h = hex_color.lstrip("#")
return (int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16), alpha)
def _arc_color(pct):
for threshold, color in _COLORS:
if pct <= threshold:
return color
return _COLORS[-1][1]
def _draw_starburst(img):
"""Draw 8-petal starburst logo at 50% opacity."""
layer = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
draw = ImageDraw.Draw(layer)
num_petals = 8
# Petal geometry: elongated ellipse approximated as polygon
petal_length = 38
petal_width = 10
for i in range(num_petals):
angle = math.radians(i * (360 / num_petals))
# Build petal as 4-point diamond shape
# Tip of petal (far from center)
tip_x = CENTER + math.cos(angle) * petal_length
tip_y = CENTER + math.sin(angle) * petal_length
# Base points (perpendicular to petal direction, near center)
perp = angle + math.pi / 2
base_offset = 6 # distance from center to base of petal
bx = CENTER + math.cos(angle) * base_offset
by = CENTER + math.sin(angle) * base_offset
left_x = bx + math.cos(perp) * (petal_width / 2)
left_y = by + math.sin(perp) * (petal_width / 2)
right_x = bx - math.cos(perp) * (petal_width / 2)
right_y = by - math.sin(perp) * (petal_width / 2)
# Inner point (towards center, slight taper)
inner_x = CENTER - math.cos(angle) * 2
inner_y = CENTER - math.sin(angle) * 2
polygon = [(inner_x, inner_y), (left_x, left_y), (tip_x, tip_y), (right_x, right_y)]
draw.polygon(polygon, fill=(255, 255, 255, 128))
# Center dot
dot_r = 4
draw.ellipse(
[CENTER - dot_r, CENTER - dot_r, CENTER + dot_r, CENTER + dot_r],
fill=(255, 255, 255, 128),
)
img = Image.alpha_composite(img, layer)
return img
def _draw_arc(img, pct):
"""Draw circular progress arc from 12 o'clock, clockwise."""
if pct <= 0:
return img
layer = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
draw = ImageDraw.Draw(layer)
color = _hex_to_rgba(_arc_color(pct))
pct = min(pct, 100)
# Pillow arc: 0 = 3 o'clock, angles go counter-clockwise for arc()
# We want: start at 12 o'clock (-90), sweep clockwise
# Pillow draws counter-clockwise from start to end, so we use:
# start = -90, end = -90 + (pct/100)*360
start_angle = -90
end_angle = -90 + (pct / 100) * 360
margin = ARC_WIDTH // 2
bbox = [margin, margin, SIZE - margin, SIZE - margin]
draw.arc(bbox, start_angle, end_angle, fill=color, width=ARC_WIDTH)
img = Image.alpha_composite(img, layer)
return img
def render_icon(pct):
"""Render a 128x128 RGBA icon with starburst + usage arc.
Args:
pct: Usage percentage (0-100). Arc is invisible at 0.
Returns:
PIL.Image.Image (RGBA)
"""
img = Image.new("RGBA", (SIZE, SIZE), (0, 0, 0, 0))
img = _draw_starburst(img)
img = _draw_arc(img, pct)
return img