## Context Nex 桌面应用是一个将后端服务(Go/Gin)与前端静态资源(embed.FS)打包为单一可执行文件的跨平台应用。当前 Windows 构建存在三类问题: 1. **通用 `desktop` target 无平台感知**:输出 `build/nex` 无 `.exe` 后缀,且缺少 `-H=windowsgui` linker flag 导致控制台窗口闪现 2. **系统托盘图标加载失败**:`getlantern/systray` 在 Windows 上期望 ICO 格式,代码传入了 64x64 的 PNG 3. **`showError`/`showAbout` 使用 `msg *`**:Windows Home 版本可能不可用,配合 `-H=windowsgui` 后行为不可预测,且不支持自定义标题栏 项目已有 `assets/icon.ico`(256x256)但代码未使用。 ## Goals / Non-Goals **Goals:** - Windows 构建产物可直接双击运行(.exe 后缀、无控制台窗口) - 系统托盘图标在所有平台上正确加载 - Windows 上使用原生 `MessageBoxW` 对话框替代 `msg *` - Makefile target 命名简洁统一 **Non-Goals:** - 不引入新的第三方依赖 - 不改变 macOS/Linux 上的现有行为 - 不涉及应用签名或代码公证(属于发布流程) - 不重构整体打包架构 ## Decisions ### 1. 删除通用 `desktop` target,重命名平台 target **决策**:删除 `desktop` target,将 `desktop-darwin`/`desktop-windows`/`desktop-linux` 重命名为 `desktop-mac`/`desktop-win`/`desktop-linux`。 **理由**:通用 target 在跨平台构建时必然需要条件判断,增加复杂度。按平台分离更明确,且项目已有先例。短命名 `win`/`mac`/`linux` 更简洁。 **产物命名统一**:`nex-{os}-{arch}[.exe]` - `nex-mac-arm64`、`nex-mac-amd64` - `nex-win-amd64.exe` - `nex-linux-amd64` ### 2. 托盘图标运行时按平台选择格式 **决策**:在 `setupSystray` 中根据 `runtime.GOOS` 选择图标文件: - Windows:加载 `assets/icon.ico`(256x256 ICO) - 其他:加载 `assets/icon.png`(PNG) **备选方案**: - ~~Build tags + 编译时选择~~:增加文件数,维护成本高 - ~~所有平台统一用 ICO~~:Linux/macOS 的 systray 实现对 ICO 支持不一致 **理由**:运行时判断最简单,两个文件都已通过 `embedfs.Assets`(`assets/*`)嵌入,零额外成本。 ### 3. Windows 原生对话框使用 `user32.MessageBoxW` **决策**:通过 `syscall` 调用 `user32.dll` 的 `MessageBoxW`,替换 `msg *`。 **实现方式**: ```go var ( user32 = syscall.NewLazyDLL("user32.dll") procMessageBoxW = user32.NewProc("MessageBoxW") ) func messageBox(title, message string) { procMessageBoxW.Call(0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(message))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))), 0x10) } ``` **备选方案**: - ~~继续用 `msg *`~~:不解决 Home 版不可用、标题栏不支持的问题 - ~~`rundll32` 调用~~:同样不可靠 - ~~引入 `lxn/walk` 等 GUI 库~~:引入重依赖,过度 **理由**:`MessageBoxW` 是 Windows 原生 API,所有版本都有,与 `-H=windowsgui` 完美兼容,支持标题栏和图标类型,零依赖。使用 `syscall`(非 `unsafe` 外部依赖)即可。 ### 4. `showError`/`showAbout` 统一用平台 switch **决策**:保持现有的 `switch runtime.GOOS` 结构,仅替换 Windows 分支实现。macOS(osascript)和 Linux(zenity)不变。 ## Risks / Trade-offs - **[syscall 跨架构]** `MessageBoxW` 的 `syscall.NewLazyDLL` 仅在 Windows 上有效 → 使用 `runtime.GOOS` 守卫,非 Windows 不会执行该路径,编译时通过 build 文件或运行时判断确保不触发 - **[ICO 嵌入体积]** `icon.ico` 270KB,已在 `embedfs` 中,不增加新体积 → 无风险 - **[Makefile 兼容性]** 删除 `desktop` target 后,CI/本地脚本如果引用它需更新 → 需检查是否有外部引用