diff --git a/local-tool/Makefile b/local-tool/Makefile index 06ca92a..87f2ac2 100644 --- a/local-tool/Makefile +++ b/local-tool/Makefile @@ -520,15 +520,48 @@ wails-linux: payload-linux ## ⚠️ 必須在 Linux runner 上執行:wails bu @du -sh visiona-local/build/bin/visiona-local # ── 安裝檔打包 ───────────────────────────────────────────────────── -dmg: wails-macos ## hdiutil UDZO → dist/visiona-local.dmg - mkdir -p $(DIST) +dmg: wails-macos ## 美化 DMG(create-dmg 有裝)或 plain DMG(fallback)→ dist/visiona-local.dmg + @mkdir -p $(DIST) + @rm -f $(DIST)/visiona-local.dmg + @if command -v create-dmg > /dev/null 2>&1; then \ + $(MAKE) --no-print-directory dmg-fancy; \ + else \ + echo "⚠️ create-dmg 未安裝,使用 plain DMG(hdiutil UDZO)"; \ + echo " 想要美化版本請執行:brew install create-dmg"; \ + $(MAKE) --no-print-directory dmg-plain; \ + fi + @du -sh $(DIST)/visiona-local.dmg + @file $(DIST)/visiona-local.dmg + +dmg-plain: ## hdiutil UDZO → dist/visiona-local.dmg(無背景圖,CI / fallback 用) + @mkdir -p $(DIST) rm -f $(DIST)/visiona-local.dmg hdiutil create -volname "visionA-local" \ -srcfolder visiona-local/build/bin/visiona-local.app \ -ov -format UDZO \ $(DIST)/visiona-local.dmg - @du -sh $(DIST)/visiona-local.dmg - @file $(DIST)/visiona-local.dmg + +dmg-fancy: ## create-dmg 美化版 → dist/visiona-local.dmg(需 brew install create-dmg) + @if [ ! -d visiona-local/build/bin/visiona-local.app ]; then \ + echo "❌ visiona-local/build/bin/visiona-local.app 不存在,請先跑 make wails-macos"; exit 1; \ + fi + @if ! command -v create-dmg > /dev/null 2>&1; then \ + echo "❌ create-dmg 未安裝,請執行:brew install create-dmg"; exit 1; \ + fi + @mkdir -p $(DIST) + rm -f $(DIST)/visiona-local.dmg + create-dmg \ + --volname "visionA-local" \ + --background installer/macos/background.png \ + --window-pos 200 120 \ + --window-size 640 400 \ + --icon-size 128 \ + --icon "visiona-local.app" 180 200 \ + --app-drop-link 460 200 \ + --hide-extension "visiona-local.app" \ + --no-internet-enable \ + $(DIST)/visiona-local.dmg \ + visiona-local/build/bin/visiona-local.app exe-only: ## 只跑 iscc 打包 installer(前置產物必須已存在),不重 build wails/payload @if [ ! -f visiona-local/build/bin/visiona-local.exe ]; then \ diff --git a/local-tool/installer/macos/README.md b/local-tool/installer/macos/README.md new file mode 100644 index 0000000..95f53b8 --- /dev/null +++ b/local-tool/installer/macos/README.md @@ -0,0 +1,42 @@ +# macOS DMG 美化資源 + +## 檔案 + +| 檔案 | 用途 | +|------|------| +| `make-dmg-background.py` | 生成背景圖的 Python script(需 Pillow) | +| `background.png` | 640×400 DMG 背景圖(1x) | +| `background@2x.png` | 1280×800 DMG 背景圖(Retina,create-dmg 自動挑用) | + +## 使用 + +```bash +# 一次性安裝 +brew install create-dmg + +# Build 美化 DMG +make dmg +``` + +`make dmg` 會自動偵測 `create-dmg`: +- 有裝 → 產出美化版(深色背景 + 拖曳示意) +- 沒裝 → fallback 到 `make dmg-plain`(原本的 hdiutil UDZO,CI 友善) + +可直接指定 target: +- `make dmg-fancy` — 強制美化版(`create-dmg` 未裝會報錯) +- `make dmg-plain` — 強制 plain 版 + +## 重新生成背景圖 + +改配色或文案時: + +```bash +python3 installer/macos/make-dmg-background.py +``` + +會同時輸出 `background.png` 與 `background@2x.png`。 + +## 設計對齊 + +背景圖配色對齊 Wails 控制台深色 splash(`#111827` → `#0B0F19` 漸層 + `#38BDF8` accent)。 +左側 app icon 位於 (180, 200),右側 Applications 捷徑位於 (460, 200),箭頭在中間。 diff --git a/local-tool/installer/macos/background.png b/local-tool/installer/macos/background.png new file mode 100644 index 0000000..f9b3711 Binary files /dev/null and b/local-tool/installer/macos/background.png differ diff --git a/local-tool/installer/macos/background@2x.png b/local-tool/installer/macos/background@2x.png new file mode 100644 index 0000000..46c3b45 Binary files /dev/null and b/local-tool/installer/macos/background@2x.png differ diff --git a/local-tool/installer/macos/make-dmg-background.py b/local-tool/installer/macos/make-dmg-background.py new file mode 100644 index 0000000..37f0026 --- /dev/null +++ b/local-tool/installer/macos/make-dmg-background.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +""" +生成 DMG 美化背景圖(640×400),對齊 Wails 控制台深色 splash 風格。 + +用法: + python3 installer/macos/make-dmg-background.py + +輸出: + installer/macos/background.png (1x, 640×400) + installer/macos/background@2x.png (2x, 1280×800 Retina) + +create-dmg 會自動挑 @2x 版本用於 Retina 螢幕。 +""" +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFont + +OUT_DIR = Path(__file__).resolve().parent +W, H = 640, 400 + +BG_TOP = (17, 24, 39) +BG_BOTTOM = (11, 15, 25) +TEXT = (229, 231, 235) +MUTED = (148, 163, 184) +ACCENT = (56, 189, 248) + + +def make(scale: int, out_path: Path) -> None: + w, h = W * scale, H * scale + img = Image.new("RGB", (w, h), BG_TOP) + px = img.load() + for y in range(h): + t = y / (h - 1) + r = int(BG_TOP[0] + (BG_BOTTOM[0] - BG_TOP[0]) * t) + g = int(BG_TOP[1] + (BG_BOTTOM[1] - BG_TOP[1]) * t) + b = int(BG_TOP[2] + (BG_BOTTOM[2] - BG_TOP[2]) * t) + for x in range(w): + px[x, y] = (r, g, b) + + draw = ImageDraw.Draw(img) + + try: + font_title = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 22 * scale) + font_hint = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 14 * scale) + except OSError: + font_title = ImageFont.load_default() + font_hint = ImageFont.load_default() + + title = "Drag visionA-local to Applications" + tw = draw.textlength(title, font=font_title) + draw.text(((w - tw) / 2, 28 * scale), title, fill=TEXT, font=font_title) + + hint = "拖曳圖示到右邊的 Applications 即可安裝" + hw = draw.textlength(hint, font=font_hint) + draw.text(((w - hw) / 2, 60 * scale), hint, fill=MUTED, font=font_hint) + + # 箭頭位置:左右兩個 icon 約在 y=230,中心。icon 本身 128×128,由 create-dmg 擺。 + # create-dmg 預設 app icon x=180, Applications x=460(y=200)。箭頭畫在中間 240-400 區段。 + arrow_y = 200 * scale + arrow_x1 = 260 * scale + arrow_x2 = 380 * scale + line_w = 3 * scale + draw.line([(arrow_x1, arrow_y), (arrow_x2, arrow_y)], fill=ACCENT, width=line_w) + # 箭頭頭 + head = 12 * scale + draw.polygon( + [ + (arrow_x2, arrow_y), + (arrow_x2 - head, arrow_y - head // 2 - 2 * scale), + (arrow_x2 - head, arrow_y + head // 2 + 2 * scale), + ], + fill=ACCENT, + ) + + img.save(out_path, "PNG", optimize=True) + print(f"wrote {out_path} ({out_path.stat().st_size // 1024} KB)") + + +def main() -> None: + make(1, OUT_DIR / "background.png") + make(2, OUT_DIR / "background@2x.png") + + +if __name__ == "__main__": + main()