Version 1 OneShot

This commit is contained in:
2026-10-09 18:58:50 +00:00
parent 514c13d184
commit bf0d661eed
27 changed files with 3039 additions and 0 deletions

69
scripts/make_icon.py Normal file
View File

@@ -0,0 +1,69 @@
"""Render assets/icon.png and a multi-size assets/icon.ico. Run once; outputs are committed."""
import os
import struct
import sys
from pathlib import Path
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from PySide6.QtCore import QBuffer, QPointF, QRectF, Qt
from PySide6.QtGui import QColor, QGuiApplication, QImage, QLinearGradient, QPainter, QPainterPath, QPolygonF
ASSETS = Path(__file__).resolve().parent.parent / "assets"
def render(size: int) -> QImage:
img = QImage(size, size, QImage.Format_ARGB32)
img.fill(Qt.transparent)
p = QPainter(img)
p.setRenderHint(QPainter.Antialiasing)
s = size / 256
grad = QLinearGradient(0, 0, 0, size)
grad.setColorAt(0, QColor("#2f80ed"))
grad.setColorAt(1, QColor("#1b4f9c"))
path = QPainterPath()
path.addRoundedRect(QRectF(8 * s, 8 * s, 240 * s, 240 * s), 52 * s, 52 * s)
p.fillPath(path, grad)
# Film strip holes along the top and bottom edges.
p.setBrush(QColor(255, 255, 255, 70))
p.setPen(Qt.NoPen)
for i in range(6):
x = (38 + i * 34) * s
p.drawRoundedRect(QRectF(x, 22 * s, 18 * s, 14 * s), 3 * s, 3 * s)
p.drawRoundedRect(QRectF(x, 220 * s, 18 * s, 14 * s), 3 * s, 3 * s)
# Two chevrons: "convert from -> to".
p.setBrush(QColor("white"))
for dx in (0, 62):
p.drawPolygon(QPolygonF([QPointF((70 + dx) * s, 72 * s), QPointF((136 + dx) * s, 128 * s),
QPointF((70 + dx) * s, 184 * s), QPointF((70 + dx) * s, 152 * s),
QPointF((100 + dx) * s, 128 * s), QPointF((70 + dx) * s, 104 * s)]))
p.end()
return img
def png_bytes(img: QImage) -> bytes:
buf = QBuffer()
buf.open(QBuffer.WriteOnly)
img.save(buf, "PNG")
return bytes(buf.data())
def write_ico(path: Path, sizes=(16, 24, 32, 48, 64, 128, 256)) -> None:
images = [png_bytes(render(n)) for n in sizes]
header = struct.pack("<HHH", 0, 1, len(images))
offset = 6 + 16 * len(images)
entries, blobs = b"", b""
for n, data in zip(sizes, images):
entries += struct.pack("<BBBBHHII", n % 256, n % 256, 0, 0, 1, 32, len(data), offset)
offset += len(data)
blobs += data
path.write_bytes(header + entries + blobs)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
ASSETS.mkdir(exist_ok=True)
render(256).save(str(ASSETS / "icon.png"))
write_ico(ASSETS / "icon.ico")
print("wrote", ASSETS / "icon.png", ASSETS / "icon.ico")