From c488529e14f89d4707c5f4add4698a63506459e6 Mon Sep 17 00:00:00 2001 From: teahubxyz Date: Wed, 21 Oct 2026 01:58:01 +0000 Subject: [PATCH] =?UTF-8?q?icons:=20drawn=20by=20tools/icons.py=20(Pillow,?= =?UTF-8?q?=20supersampled),=20not=20screenshotted=20=E2=80=94=20the=20run?= =?UTF-8?q?ner's=20Chrome=20cropped=20them?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6 --- icons/icon.html | 5 ++--- tools/icons.py | 42 +++++++++++++++++++++++++++++++++++++++++ tools/shots.sh | 50 ++++++++++++++++++++++--------------------------- 3 files changed, 66 insertions(+), 31 deletions(-) create mode 100644 tools/icons.py diff --git a/icons/icon.html b/icons/icon.html index 631cff4..f883004 100644 --- a/icons/icon.html +++ b/icons/icon.html @@ -1,6 +1,5 @@ icon - - - + +the gandora mark diff --git a/tools/icons.py b/tools/icons.py new file mode 100644 index 0000000..ebbcaa1 --- /dev/null +++ b/tools/icons.py @@ -0,0 +1,42 @@ +"""The app icons: the asterisk, white with square ends, on the ground colour. Same geometry as icons/icon.svg. + + python3 tools/icons.py -> icons/icon-512.png, icons/icon-192.png, icons/apple-touch-icon.png + +Needs Pillow (pip install pillow). Drawn at 4x and downsampled so the edges are clean. +""" +import math +import sys +from pathlib import Path + +try: + from PIL import Image, ImageDraw +except ImportError: # pragma: no cover + sys.exit("pip install pillow") + +ROOT = Path(__file__).resolve().parent.parent +GROUND, INK = (11, 11, 14, 255), (237, 240, 240, 255) + + +def asterisk(size: int) -> Image.Image: + ss = 4 + W = size * ss + im = Image.new("RGBA", (W, W), GROUND) + d = ImageDraw.Draw(im) + c = W / 2 + half, width = 150 / 512 * W, 58 / 512 * W # arm half-length and stroke width, as in icon.svg + for k in range(3): + a = math.radians(90 + 60 * k) + dx, dy = math.cos(a), math.sin(a) + nx, ny = -dy * width / 2, dx * width / 2 + p1 = (c - dx * half, c - dy * half) + p2 = (c + dx * half, c + dy * half) + d.polygon([(p1[0] + nx, p1[1] + ny), (p2[0] + nx, p2[1] + ny), (p2[0] - nx, p2[1] - ny), (p1[0] - nx, p1[1] - ny)], fill=INK) + return im.resize((size, size), Image.LANCZOS) + + +if __name__ == "__main__": + out = ROOT / "icons" + out.mkdir(exist_ok=True) + for name, size in (("icon-512.png", 512), ("icon-192.png", 192), ("apple-touch-icon.png", 180)): + asterisk(size).convert("RGB").save(out / name, optimize=True) + print("built: icons/" + name) diff --git a/tools/shots.sh b/tools/shots.sh index 6ac2d7d..fab7349 100644 --- a/tools/shots.sh +++ b/tools/shots.sh @@ -1,11 +1,13 @@ #!/bin/sh -# tools/shots.sh: shots/desktop.png, the first screen at 1440x900, and the app icons (icons/*.png from icons/icon.svg), -# rendered from this folder by headless Chrome. -# Needs python3 (for a local server) and Chrome or Chromium: set CHROME=/path/to/chrome if it isn't found, and -# CHROME_FLAGS=--no-sandbox when running as root. +# tools/shots.sh: shots/desktop.png, the first screen at 1440x900, rendered from this folder by headless Chrome, and +# the app icons, drawn by tools/icons.py. +# Needs python3 (for a local server; Pillow for the icons) and Chrome or Chromium: set CHROME=/path/to/chrome if it +# isn't found, and CHROME_FLAGS=--no-sandbox when running as root. set -eu cd "$(dirname "$0")/.." +python3 tools/icons.py + CHROME=${CHROME:-} for c in "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" "$(command -v chromium || true)" \ "$(command -v chromium-browser || true)" "$(command -v google-chrome || true)" "$(command -v google-chrome-stable || true)"; do @@ -22,27 +24,19 @@ until curl -fs -o /dev/null --max-time 2 "http://127.0.0.1:$PORT/"; do n=$((n + 1)); [ "$n" -lt 40 ] || { echo "the local server on port $PORT never answered" >&2; exit 1; }; sleep 0.5 done -# shoot : one headless screenshot, waited for. The icon page draws its own box at the top left, -# so the crop is exact whatever viewport the runner's Chromium decides on. -shoot() { - prof=$(mktemp -d) - rm -f "$3" - # shellcheck disable=SC2086 - "$CHROME" --headless=new --hide-scrollbars --no-first-run --no-default-browser-check --disable-background-networking \ - --disable-component-update --force-device-scale-factor=1 --user-data-dir="$prof" --virtual-time-budget=6000 \ - --window-size="$2" --screenshot="$3" ${CHROME_FLAGS:-} "$1" >"$prof.log" 2>&1 & - pid=$! - n=0 - while kill -0 "$pid" 2>/dev/null && [ ! -s "$3" ]; do n=$((n + 1)); [ "$n" -lt 120 ] || break; sleep 0.5; done - sleep 1 - kill "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true - [ -s "$3" ] || { echo "Chrome made no screenshot for $1:" >&2; tail -5 "$prof.log" >&2; exit 1; } - rm -rf "$prof" "$prof.log" - echo "built: ${3#$PWD/}" -} - -mkdir -p shots icons -shoot "http://127.0.0.1:$PORT/" 1440,900 "$PWD/shots/desktop.png" -shoot "http://127.0.0.1:$PORT/icons/icon.html?s=512" 512,512 "$PWD/icons/icon-512.png" -shoot "http://127.0.0.1:$PORT/icons/icon.html?s=192" 192,192 "$PWD/icons/icon-192.png" -shoot "http://127.0.0.1:$PORT/icons/icon.html?s=180" 180,180 "$PWD/icons/apple-touch-icon.png" +mkdir -p shots +out=$PWD/shots/desktop.png +prof=$(mktemp -d) +rm -f "$out" +# shellcheck disable=SC2086 +"$CHROME" --headless=new --hide-scrollbars --no-first-run --no-default-browser-check --disable-background-networking \ + --disable-component-update --user-data-dir="$prof" --virtual-time-budget=6000 --window-size=1440,900 \ + --screenshot="$out" ${CHROME_FLAGS:-} "http://127.0.0.1:$PORT/" >"$prof.log" 2>&1 & +pid=$! +n=0 +while kill -0 "$pid" 2>/dev/null && [ ! -s "$out" ]; do n=$((n + 1)); [ "$n" -lt 120 ] || break; sleep 0.5; done +sleep 1 +kill "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true +[ -s "$out" ] || { echo "Chrome made no screenshot:" >&2; tail -5 "$prof.log" >&2; exit 1; } +rm -rf "$prof" "$prof.log" +echo "built: shots/desktop.png"