fa168271e1
M1 — PWA mechanics: - Generate real app icons (apple-touch-icon + manifest 192/512/maskable) via pure-stdlib gen_icons.py; iOS uses apple-touch-icon, not manifest icons. - viewport-fit=cover + env(safe-area-inset-*) on header/input/menu so content clears the notch and home indicator. - Dynamic height pinned to the VisualViewport (height + offsetTop, re-measured across the keyboard animation) so the input stays above the iOS keyboard; 100dvh fallback. Kills the squish/gap bugs in standalone mode. - overscroll containment; flesh out manifest (scope, portrait, maskable). M2 — visual redesign: - Realign style.css to the warm low-glow RTO palette already used by the standalone pages (#0e0e0e panels, #2a1d12 borders); remove the neon saturated-orange borders and ~15 glow shadows. - Reserve filled accent for one element (Send); glow only on status pulse + input focus. Flat warm message bubbles with tail corners. - Reclaim the mobile header into [≡] Lyra · [status dot]; drop the redundant status bar (relay status now the header dot, updated in checkHealth). - prefers-reduced-motion support; fix undefined var(--text); real light-mode tokens. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate Lyra PWA icons with no third-party deps (pure stdlib PNG writer).
|
|
|
|
Design: RTO warm/low-glow — near-black field, a soft orange ambient glow, and a
|
|
luminous gold-orange ring (the "orb/portal"). iOS masks corners itself, so icons
|
|
are full-bleed squares. Run from anywhere; writes PNGs into ./static.
|
|
"""
|
|
import math
|
|
import os
|
|
import struct
|
|
import zlib
|
|
|
|
HERE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
|
|
|
|
BG = (7, 7, 7) # #070707
|
|
ORANGE = (255, 122, 0) # #ff7a00 accent
|
|
GOLD = (255, 179, 71) # #ffb347 hot core
|
|
|
|
|
|
def _png(width, height, rgb_rows):
|
|
def chunk(tag, data):
|
|
return (struct.pack(">I", len(data)) + tag + data
|
|
+ struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF))
|
|
|
|
raw = bytearray()
|
|
for row in rgb_rows:
|
|
raw.append(0) # filter type 0 (None)
|
|
raw.extend(row)
|
|
ihdr = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0) # 8-bit RGB
|
|
return (b"\x89PNG\r\n\x1a\n"
|
|
+ chunk(b"IHDR", ihdr)
|
|
+ chunk(b"IDAT", zlib.compress(bytes(raw), 9))
|
|
+ chunk(b"IEND", b""))
|
|
|
|
|
|
def render(n):
|
|
c = (n - 1) / 2.0
|
|
sigma_glow = n * 0.30
|
|
ring_r = n * 0.30
|
|
ring_w = n * 0.050
|
|
core_sigma = n * 0.11
|
|
rows = []
|
|
for y in range(n):
|
|
row = bytearray()
|
|
for x in range(n):
|
|
dx, dy = x - c, y - c
|
|
d = math.hypot(dx, dy)
|
|
r, g, b = BG
|
|
# ambient orange glow
|
|
glow = math.exp(-(d * d) / (2 * sigma_glow * sigma_glow)) * 0.50
|
|
# soft hot core
|
|
core = math.exp(-(d * d) / (2 * core_sigma * core_sigma)) * 0.45
|
|
# luminous ring
|
|
rr = d - ring_r
|
|
ring = math.exp(-(rr * rr) / (2 * ring_w * ring_w))
|
|
r += ORANGE[0] * glow + GOLD[0] * (ring + core)
|
|
g += ORANGE[1] * glow + GOLD[1] * (ring + core)
|
|
b += ORANGE[2] * glow + GOLD[2] * (ring + core)
|
|
row += bytes((min(255, int(r)), min(255, int(g)), min(255, int(b))))
|
|
rows.append(row)
|
|
return rows
|
|
|
|
|
|
def write(name, n):
|
|
rows = render(n)
|
|
with open(os.path.join(HERE, name), "wb") as f:
|
|
f.write(_png(n, n, rows))
|
|
print(f"wrote {name} ({n}x{n})")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
write("icon-512.png", 512)
|
|
write("icon-192.png", 192)
|
|
write("apple-touch-icon.png", 180)
|
|
write("icon-maskable-512.png", 512)
|