CI / backend (push) Canceled after 0s
- primary 换成 Indigo 阶(600 即 #4F46E5),中性色转 Slate 轴、纸面更冷更白 - 实心主色面(主按钮、工作区选中)用 600,链接/浅底文字仍用 700 保对比度 - 信息色 sky 调深到 #16697C,与靛蓝同屏能分清 - Logo / favicon / PWA 图标 / 安卓自适应图标统一为靛蓝底 + 白线 + 淡紫点,tools/make-icons.py 重新生成 - 文档与 e2e 断言同步(主题色 #4f46e5)
77 lines
2.6 KiB
Python
77 lines
2.6 KiB
Python
"""按 Web Logo(icons.tsx 的 <Logo/>)重绘 PWA 图标:靛蓝底 + 三条白线 + 一点淡紫。
|
||
|
||
用法:python tools/make-icons.py
|
||
输出覆盖 web/public/icons/*.png(与 SVG 同一套比例:64 画布,rx=18,线宽 4,点半径 6)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
|
||
from PIL import Image, ImageDraw
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
OUT = ROOT / "web" / "public" / "icons"
|
||
|
||
BG = "#4F46E5"
|
||
LINE = "#FFFFFF"
|
||
DOT = "#C7D2FE"
|
||
|
||
SS = 8 # 超采样倍数,边缘才不毛
|
||
|
||
|
||
def draw(size: int, *, rounded: bool, inset: float = 0.0) -> Image.Image:
|
||
"""在 size×size 上画 logo。inset 是安全区留白比例(maskable 图标要留)。"""
|
||
big = size * SS
|
||
img = Image.new("RGBA", (big, big), (0, 0, 0, 0))
|
||
d = ImageDraw.Draw(img)
|
||
|
||
if rounded:
|
||
d.rounded_rectangle([0, 0, big - 1, big - 1], radius=big * 18 / 64, fill=BG)
|
||
else:
|
||
d.rectangle([0, 0, big, big], fill=BG)
|
||
|
||
# 64 画布坐标 → 像素;inset 让图形整体缩小居中(给 maskable 留安全区)
|
||
scale = big / 64 * (1 - 2 * inset)
|
||
off = big * inset
|
||
|
||
def box(x: float, y: float) -> tuple[float, float]:
|
||
return off + x * scale, off + y * scale
|
||
|
||
stroke = max(1, round(4 * scale))
|
||
for y, x2 in ((23, 46), (32, 37), (41, 30)):
|
||
x1, yy = box(18, y)
|
||
d.line([x1, yy, *box(x2, y)], fill=LINE, width=stroke)
|
||
# 圆头:PIL 的 line 没有 cap,用小圆补两端
|
||
r = stroke / 2
|
||
for cx, cy in ((x1, yy), box(x2, y)):
|
||
d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=LINE)
|
||
|
||
cx, cy = box(46, 41)
|
||
r = 6 * scale
|
||
d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=DOT)
|
||
|
||
return img.resize((size, size), Image.LANCZOS)
|
||
|
||
|
||
def main() -> None:
|
||
OUT.mkdir(parents=True, exist_ok=True)
|
||
# 圆角版:favicon 之外的常规 PWA 图标
|
||
draw(192, rounded=True).save(OUT / "icon-192.png")
|
||
draw(512, rounded=True).save(OUT / "icon-512.png")
|
||
# iOS 会自己裁圆角,底图必须是方的
|
||
draw(180, rounded=False).save(OUT / "apple-touch-icon.png")
|
||
# maskable:图形缩进 12% 安全区
|
||
draw(512, rounded=False, inset=0.12).save(OUT / "maskable-512.png")
|
||
# 文档里引用的安卓图标预览
|
||
preview = ROOT / "2.Design" / "screens" / "android-icon-512.png"
|
||
preview.parent.mkdir(parents=True, exist_ok=True)
|
||
draw(512, rounded=True).save(preview)
|
||
for p in sorted(OUT.glob("*.png")):
|
||
print(p.relative_to(ROOT), Image.open(p).size)
|
||
print(preview.relative_to(ROOT), Image.open(preview).size)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|