Files
arcrun-collector/cmd/arcrun-app/tray_darwin.go
T
Leo 5860ef72df t194 四修:托盤改用原生 NSStatusItem——energye/systray 在 Wails 之下根本不建托盤
leo:右鍵仍然沒用。這次先做**最小重現**才動手,結果推翻了我前兩次的判斷。

## 真兇(實測,非推論)
systray.go:83 的 setInternalLoop(true) **只在 systray.Run() 裡呼叫**;
RunWithExternalLoop 沒有 ⇒ registerSystray() 第一行就
`if (!internalLoop) return;` ⇒ **delegate 從沒建立**
⇒ onReady 不會被呼叫、enable_on_click 不會執行、左右鍵事件根本不存在。

最小重現(scratchpad/traytest):RunWithExternalLoop + onReady 印 "READY"
⇒ **READY 從未印出**。托盤自始至終沒被建立過。
(左鍵之所以「第一次能開」是 Wails 自己開的窗,與托盤無關。)

## 正解:原生 NSStatusItem(tray_darwin.m + tray_darwin.go)
左鍵=開視窗;右鍵=暫時掛選單、performClick、再拿掉(不常駐 setMenu:,
否則按鈕 action 不會被呼叫、左鍵也會變成彈選單——與 systray 那個坑同源)。

## 過程中踩到、也寫進閘的三個坑
① dispatch_async(main_queue) **無效**:Wails 佔住主執行緒後不跑標準 run loop,
   排進去的 block 永遠不執行(log 只印到 "dispatching…")⇒ 改 performSelectorOnMainThread
② NSStatusBar 需要 NSApp 已初始化:在 wails.Run() 之前呼叫 ⇒ 靜默失敗、icon 不出現
③ 🔴 **我一度對著 `go build` 產的 stub 除錯**——沒帶 Wails build tags 的執行檔
   一跑就印 "Wails applications will not build without the correct build tags" 並退出,
   我卻以為是「托盤沒建起來」,白繞一圈。**驗 Wails App 一定要用 wails build 的產物。**

## 實測證據(AppleScript 從 UI 層查,最貼近使用者看到的)
  menu bar 數=2,status item 數=1   ← 選單列 icon 真的存在
  collector 同步啟動、正常結束無孤兒
三支機械閘全過(閘也改成驗原生實作:不可 dispatch_async/必須
performSelectorOnMainThread/不可常駐 setMenu/不可再依賴 energye/systray)。

⚠️ 仍未驗:左右鍵的**實際點擊行為**要 leo 手動點。
2026-08-05 02:56:41 +08:00

41 lines
951 B
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build darwin
package main
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Cocoa
#include <stdlib.h>
void createTray(const char *iconBytes, int iconLen, const char *tooltip, const char *quitTitle);
*/
import "C"
import "unsafe"
// trayApp 讓 C 回呼找得到 App(cgo 匯出函式不能帶 Go 參數)。
var trayApp *App
//export trayOnLeftClick
func trayOnLeftClick() {
if trayApp != nil {
trayApp.ShowWindow()
}
}
//export trayOnQuit
func trayOnQuit() {
if trayApp != nil {
trayApp.Quit()
}
}
// setupTray 建立選單列 icon。
// ⚠️ **必須在主執行緒呼叫**NSStatusItem 內部會 new NSWindow)。
func setupTray(app *App) {
trayApp = app
tip := C.CString("Arcrun — 你的知識庫同步小幫手")
quit := C.CString("結束 Arcrun")
defer C.free(unsafe.Pointer(tip))
defer C.free(unsafe.Pointer(quit))
C.createTray((*C.char)(unsafe.Pointer(&trayIcon[0])), C.int(len(trayIcon)), tip, quit)
}