"""Paint the app's brand art: the wheel mark and the app icon. python3 tools/brand.py paint [name ...] # these candidates, or all of them python3 tools/brand.py list Painted with gpt-image-2.5-sunburst, the full-quality image model the owner uses for product art. Every picture costs API credit, so paint only what is asked for. The key comes from OPENAI_API_KEY and is never printed. Masters land in art/brand/masters (1024 px); the chosen one becomes media/icon.png and `bun run icons` makes every size. """ from __future__ import annotations import argparse import base64 import json import os import sys import time import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parents[1] MASTERS = ROOT / "art" / "brand" / "masters" MODEL = os.environ.get("ICON_MODEL", "gpt-image-2.5-sunburst") WHEEL = ("a sports car wheel: a black tyre with a fine tread and a crisp sidewall, a five-spoke alloy rim " "in warm amber gold (#FFB224) with a machined silver lip, centre cap with no logo") FINISH = ("Polished glossy 3D render, soft studio lighting with a warm amber rim light, clean bevelled " "edges, high contrast, readable at 16 pixels. No text, no letters, no numbers, no brand logos, " "no watermark.") CANDIDATES: dict[str, tuple[str, str]] = { "icon-tile": ("opaque", f"An app icon: {WHEEL}, seen straight on and filling most of a dark graphite " "(#232427) rounded-square tile with a subtle vertical gradient and a thin lighter edge. " + FINISH), "mark": ("transparent", f"A logo mark: {WHEEL}, seen straight on, centred, filling the frame. Fully " "transparent background, no tile, no circle behind it, no ground shadow. " + FINISH), "motion": ("opaque", f"An app icon: {WHEEL}, in a slight three-quarter view as if turning, with two " "short amber speed streaks behind it, on a dark graphite (#232427) rounded-square tile. " + FINISH), "wireframe": ("opaque", f"An app icon: {WHEEL}, seen straight on; its left half is a glowing amber " "wireframe mesh of the same wheel, its right half the finished glossy wheel, split cleanly " "down the middle, on a dark graphite (#232427) rounded-square tile. " + FINISH), } def api_key() -> str: key = os.environ.get("OPENAI_API_KEY", "").strip() if not key: sys.exit("no API key: set OPENAI_API_KEY") return key def paint(name: str, key: str) -> str: background, prompt = CANDIDATES[name] body = json.dumps({"model": MODEL, "prompt": prompt, "size": "1024x1024", "quality": "high", "background": background, "output_format": "png", "n": 1}).encode() request = urllib.request.Request("https://api.openai.com/v1/images/generations", method="POST", data=body, headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"}) for attempt in range(6): try: with urllib.request.urlopen(request, timeout=400) as response: payload = json.load(response) break except Exception as exc: # the message, never the key detail = exc.read().decode("utf-8", "replace")[:200] if hasattr(exc, "read") else str(exc)[:200] if attempt == 5 or "rate" not in detail.lower(): return f"{name}: failed: {detail}" time.sleep(20 * (attempt + 1)) MASTERS.mkdir(parents=True, exist_ok=True) out = MASTERS / f"{name}.png" out.write_bytes(base64.b64decode(payload["data"][0]["b64_json"])) usage = payload.get("usage") or {} return f"{out.relative_to(ROOT)} (tokens in {usage.get('input_tokens', '?')}, out {usage.get('output_tokens', '?')})" def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("command", choices=["paint", "list"]) ap.add_argument("names", nargs="*") args = ap.parse_args() if args.command == "list": for name in CANDIDATES: print(name) return names = args.names or list(CANDIDATES) unknown = [n for n in names if n not in CANDIDATES] if unknown: sys.exit(f"unknown: {', '.join(unknown)}") key = api_key() for name in names: print(paint(name, key), flush=True) if __name__ == "__main__": main()