P6: placeholder icon, README, packaged build verified
- assets/icon.png (generated by scripts/make-icon.py) + icon.iconset (scripts/gen-iconset.sh); electrobun compiles it to AppIcon.icns - branding.icon points at the iconset - README: features, stack, dev/build, rebranding, plaintext-password warning, shortcuts, v1 scope - verified: full production build (ng build + electrobun build) produces a launchable BunSCP.app that boots standalone from views://app Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#!/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})")
|
||||
Reference in New Issue
Block a user