#!/usr/bin/env python3 """Generate a placeholder app icon (1024x1024 PNG) with a dual-panel + transfer motif. No third-party deps — raw RGBA encoded via zlib. Replace assets/icon.png (and re-run scripts/gen-iconset.sh) to rebrand.""" import struct import zlib import sys N = 1024 def lerp(a, b, t): return tuple(round(a[i] + (b[i] - a[i]) * t) for i in range(3)) def rounded_rect_alpha(x, y, x0, y0, x1, y1, r): """Coverage (0..1) for a rounded rect with ~1px antialiasing.""" # distance outside the rect's rounded boundary dx = max(x0 + r - x, 0, x - (x1 - r)) dy = max(y0 + r - y, 0, y - (y1 - r)) if x < x0 or x > x1 or y < y0 or y > y1: return 0.0 # corner regions cx = None if x < x0 + r and y < y0 + r: cx, cy = x0 + r, y0 + r elif x > x1 - r and y < y0 + r: cx, cy = x1 - r, y0 + r elif x < x0 + r and y > y1 - r: cx, cy = x0 + r, y1 - r elif x > x1 - r and y > y1 - r: cx, cy = x1 - r, y1 - r if cx is not None: d = ((x - cx) ** 2 + (y - cy) ** 2) ** 0.5 return max(0.0, min(1.0, r - d + 0.5)) return 1.0 BG_TOP = (0x4F, 0x46, 0xE5) # indigo-600 BG_BOT = (0x18, 0x18, 0x1B) # zinc-900 PANEL = (0xFA, 0xFA, 0xFA) # near white ROW = (0xC7, 0xCC, 0xD6) ARROW = (0xFB, 0xBF, 0x24) # amber-400 buf = bytearray() margin = 96 radius = 205 # panels pgap = 70 ptop = 340 pbot = 760 pw = (N - 2 * 300 - pgap) / 2 lx0 = 300 lx1 = lx0 + pw rx0 = lx1 + pgap rx1 = rx0 + pw for y in range(N): buf.append(0) # PNG filter byte per scanline for x in range(N): outer = rounded_rect_alpha(x + 0.5, y + 0.5, margin, margin, N - margin, N - margin, radius) t = y / N col = lerp(BG_TOP, BG_BOT, t) r, g, b = col a = outer # panels for (x0, x1) in ((lx0, lx1), (rx0, rx1)): pa = rounded_rect_alpha(x + 0.5, y + 0.5, x0, ptop, x1, pbot, 34) if pa > 0: r = round(r * (1 - pa) + PANEL[0] * pa) g = round(g * (1 - pa) + PANEL[1] * pa) b = round(b * (1 - pa) + PANEL[2] * pa) # file rows for row in range(4): ry = ptop + 70 + row * 74 if ry <= y <= ry + 26 and x0 + 34 <= x <= x1 - 34: r, g, b = ROW # transfer arrows in the gap cx0, cx1 = lx1 + 6, rx0 - 6 midy = (ptop + pbot) / 2 if cx0 <= x <= cx1 and abs(y - midy) < 90: # two chevrons pointing right and left, forming <-> feel rel = (x - cx0) / (cx1 - cx0) band = abs(y - midy) if band < 46 - abs(rel - 0.5) * 40: r, g, b = ARROW a = max(a, outer) buf.extend((r, g, b, round(255 * a))) def chunk(tag, data): return struct.pack(">I", len(data)) + tag + data + struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF) png = b"\x89PNG\r\n\x1a\n" png += chunk(b"IHDR", struct.pack(">IIBBBBB", N, N, 8, 6, 0, 0, 0)) png += chunk(b"IDAT", zlib.compress(bytes(buf), 9)) png += chunk(b"IEND", b"") out = sys.argv[1] if len(sys.argv) > 1 else "assets/icon.png" with open(out, "wb") as f: f.write(png) print(f"wrote {out} ({N}x{N})")