051c865085
leo 08-06 封測回報:托盤與視窗 icon 是「W」,不是 CIS 的 chevron。
真兇:build/windows/icon.ico 是 wails init 生成後從沒被動過的 Wails 預設圖。
換 CIS logo 那次只換了 build/appicon.png,Windows 這顆沒人碰;
tray_windows.go 又 embed 同一顆 ⇒ 視窗與托盤兩處都是 W。
macOS 走 trayicon.png(正確)⇒ 這個病在 Mac 上 100% 驗不出來。
改法不是「這次手動換一顆」,是讓它不可能再漂移:
- 新增 gen-icons.py:兩顆 .ico 一律由 appicon.png 現場產生;
--check 模式比對「committed == 重新產生的」,可當機械閘。
- tray_windows.go 改 embed build/trayicon.ico(小尺寸階梯,系統匣只吃 16/32)。
已驗:重新產生後把 256/32 都渲成 PNG 目視確認為 CIS chevron;--check 通過。
未驗:Windows 真機(需重打包後由封測者確認)⇒ 狀態 ◐,不是 ✅。
同時落帳(wiki status/mistakes):
- manifest 宣告 v0.18.5 但產物是 v0.18.4 的完整證據鏈
- 五個 Windows 真兇(檔:行)
- leo 兩問的設計定案:trigger=內容指紋(不另立、不用 tag/Actions)、
changelog 單一真相源=CHANGELOG.md 其餘全投影
- 這次穿過機械閘的那道縫:缺「宣告版本 == 產物內嵌版本」的驗證
72 lines
2.7 KiB
Python
72 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
||
"""gen-icons.py — 從 build/appicon.png 產生 Windows 用的 .ico(2026-08-06)
|
||
|
||
🔴 為什麼要有這支:
|
||
leo 08-06 封測回報「托盤及視窗 icon 是『W』,不是 CIS 的 chevron 標誌(a>>)」。
|
||
真兇=`build/windows/icon.ico` 是 `wails init` 生成後**從沒被換過的 Wails 預設圖**;
|
||
換 CIS logo 那次只換了 `build/appicon.png`,Windows 這顆沒人動。
|
||
而 `tray_windows.go` 又 embed 同一顆 ⇒ 視窗與托盤兩處都是 W。
|
||
|
||
根治不是「這次手動換一顆」——是**讓它不可能再漂移**:
|
||
icon.ico/trayicon.ico 一律由 appicon.png 現場產生,
|
||
build-win.sh 每次都跑這支,check-cis.sh 驗「committed == 重新產生的」。
|
||
|
||
用法:
|
||
python3 gen-icons.py # 產生(覆寫)
|
||
python3 gen-icons.py --check # 只驗證,不寫檔;不一致回傳 1
|
||
"""
|
||
import hashlib
|
||
import io
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
from PIL import Image
|
||
|
||
HERE = Path(__file__).resolve().parent
|
||
SRC = HERE / "build" / "appicon.png"
|
||
APP_ICO = HERE / "build" / "windows" / "icon.ico"
|
||
TRAY_ICO = HERE / "build" / "trayicon.ico"
|
||
|
||
# 視窗/工作列/檔案總管會用到的完整尺寸階梯。
|
||
APP_SIZES = [16, 24, 32, 48, 64, 128, 256]
|
||
# 系統匣只吃小尺寸(Windows 依 DPI 取 16 或 32)。
|
||
TRAY_SIZES = [16, 24, 32, 48]
|
||
|
||
|
||
def render(sizes):
|
||
"""把 appicon.png 縮成多尺寸,包成一顆 .ico。"""
|
||
if not SRC.exists():
|
||
sys.exit("❌ 找不到來源圖:%s" % SRC)
|
||
src = Image.open(SRC).convert("RGBA")
|
||
buf = io.BytesIO()
|
||
# Pillow 的 ICO 儲存會自己依 sizes 產生各層。
|
||
src.save(buf, format="ICO", sizes=[(s, s) for s in sizes])
|
||
return buf.getvalue()
|
||
|
||
|
||
def main():
|
||
check_only = "--check" in sys.argv
|
||
fail = 0
|
||
targets = ((APP_ICO, APP_SIZES, "視窗/工作列 icon"),
|
||
(TRAY_ICO, TRAY_SIZES, "系統匣 icon"))
|
||
for path, sizes, label in targets:
|
||
want = render(sizes)
|
||
have = path.read_bytes() if path.exists() else b""
|
||
if check_only:
|
||
if hashlib.sha256(want).digest() == hashlib.sha256(have).digest():
|
||
print(" ✅ %s(%s)與 appicon.png 一致" % (label, path.name))
|
||
else:
|
||
print(" ❌ %s(%s)與 appicon.png 不一致 "
|
||
"⇒ 跑 `python3 gen-icons.py` 重產" % (label, path.name))
|
||
fail = 1
|
||
else:
|
||
path.parent.mkdir(parents=True, exist_ok=True)
|
||
path.write_bytes(want)
|
||
print(" ✅ 產生 %s(%d bytes,%d 種尺寸)"
|
||
% (path.relative_to(HERE), len(want), len(sizes)))
|
||
return fail
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|