From 549834f343e3a2f594769d3b145f1f7a15c58473 Mon Sep 17 00:00:00 2001 From: richblack Date: Wed, 5 Aug 2026 00:31:52 +0800 Subject: [PATCH] =?UTF-8?q?t194=EF=BC=9A=E6=89=98=E7=9B=A4=E6=94=B6?= =?UTF-8?q?=E6=88=90=20Google=20Drive=20=E5=BC=8F=EF=BC=88=E9=BB=9E=20icon?= =?UTF-8?q?=20=E9=96=8B=E8=A6=96=E7=AA=97=EF=BC=8F=E5=8F=B3=E9=8D=B5?= =?UTF-8?q?=E5=8F=AA=E6=9C=89=E7=B5=90=E6=9D=9F=EF=BC=89=EF=BC=8B=E5=90=8C?= =?UTF-8?q?=E7=B6=91=20collector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit leo 2026-08-05:「依照 google drive,托盤裡只剩下按右鍵會結束, 其他設定都在這個頁面,點擊托盤的 icon 就立刻展開界面」 ## 托盤 Wails v2 沒有內建托盤 ⇒ 用 energye/systray(Wails 相容分支,支援左鍵事件)。 - 左鍵:直接展開主視窗,**不彈選單** - 右鍵:只有一項「結束 Arcrun」 - 設定全部留在視窗裡,托盤不再是第二套 UI (fyne 版把設定塞下拉選單,leo 早說過「不可能統統塞在下拉選單」) - 關窗=隱藏不結束(daemon 常駐;按 × 就停止同步不符預期) - Dock 不出現 icon:Wails v2.13 的 mac.ActivationPolicy 還沒開放(原始碼是註解掉的) ⇒ 改走 Info.plist 的 LSUIElement,與 fyne 版同一個做法 ## 🔴 順手抓到自己的大漏:這個 App 根本沒有啟動 collector daemon 的**本體**是 `collector direct`(監看/萃卡/上傳),Wails 版我一路沒接 ⇒ 照這樣出貨會是「裝了也不會同步」的空殼。 修:新增 supervise.go,**複用既有 supervisor 套件**(重起退避/狀態機/ t191 的 phase:start/done ⇒「同步中…」),不複製一份(複製=會漂移)。 go.mod 用 replace 指回 collector 主模組。 加/刪資料夾、改 AI 設定後都會 restartWatch(),設定立刻生效。 ## 打包 新增 build-mac.sh:編 collector → wails build → **同綑進 .app** → ad-hoc 重簽。 沒同綑就等於沒有 daemon。 實測(v0.18.0):collector 同綑 15M ✅/LSUIElement=true ✅/版本注入 ✅/已簽 ✅ 兩支機械閘全過。 ⚠️ 未驗:托盤左右鍵的實際行為要真機點(我開不了視窗)。 --- cmd/arcrun-app/app.go | 31 +++++++++++-- cmd/arcrun-app/build-mac.sh | 31 +++++++++++++ cmd/arcrun-app/build/darwin/Info.plist | 4 ++ cmd/arcrun-app/build/trayicon.ico | Bin 0 -> 2739 bytes cmd/arcrun-app/build/trayicon.png | Bin 0 -> 33422 bytes cmd/arcrun-app/go.mod | 19 +++++--- cmd/arcrun-app/go.sum | 18 ++++---- cmd/arcrun-app/main.go | 60 ++++++++++++++++++++----- cmd/arcrun-app/supervise.go | 56 +++++++++++++++++++++++ 9 files changed, 191 insertions(+), 28 deletions(-) create mode 100755 cmd/arcrun-app/build-mac.sh create mode 100644 cmd/arcrun-app/build/trayicon.ico create mode 100644 cmd/arcrun-app/build/trayicon.png create mode 100644 cmd/arcrun-app/supervise.go diff --git a/cmd/arcrun-app/app.go b/cmd/arcrun-app/app.go index f25c657..9ffcfb0 100644 --- a/cmd/arcrun-app/app.go +++ b/cmd/arcrun-app/app.go @@ -299,7 +299,11 @@ func (a *App) AddFolder(accIdx int, path string) error { } cfg.Accounts[accIdx].WatchFolders = append(cfg.Accounts[accIdx].WatchFolders, path) sort.Strings(cfg.Accounts[accIdx].WatchFolders) - return saveCfg(cfg) + if err := saveCfg(cfg); err != nil { + return err + } + restartWatch() // 立刻生效,不必等下一輪 + return nil } func (a *App) RemoveFolder(accIdx int, path string) error { @@ -317,7 +321,11 @@ func (a *App) RemoveFolder(accIdx int, path string) error { } } cfg.Accounts[accIdx].WatchFolders = keep - return saveCfg(cfg) + if err := saveCfg(cfg); err != nil { + return err + } + restartWatch() + return nil } // SetAI 存 AI 設定。 @@ -341,8 +349,25 @@ func (a *App) SetAI(useGemini bool, key string) error { cfg.Accounts[i].Extractor = engine cfg.Accounts[i].GeminiAPIKey = key } - return saveCfg(cfg) + if err := saveCfg(cfg); err != nil { + return err + } + restartWatch() + return nil } // OpenURL 用系統瀏覽器開網址(下載頁/說明文件)。 func (a *App) OpenURL(u string) { runtime.BrowserOpenURL(a.ctx, u) } + +// ── 托盤會呼叫的兩個動作(t194)── + +// ShowWindow 把主視窗叫出來並帶到前景。 +// 🔴 leo 2026-08-05:「**點擊托盤的 icon 就立刻展開界面**」 +// ⇒ 左鍵不彈選單、直接開窗(Google Drive 的行為)。 +func (a *App) ShowWindow() { + runtime.WindowShow(a.ctx) + runtime.WindowUnminimise(a.ctx) +} + +// Quit 真的結束程式(=停止看守)。只有托盤右鍵那一項會呼叫。 +func (a *App) Quit() { runtime.Quit(a.ctx) } diff --git a/cmd/arcrun-app/build-mac.sh b/cmd/arcrun-app/build-mac.sh new file mode 100755 index 0000000..0ae242e --- /dev/null +++ b/cmd/arcrun-app/build-mac.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# build-mac.sh — 打包 Arcrun.app(Wails 版,t194) +# +# 與 fyne 版最重要的差別:**要把 collector 同綑進 .app**。 +# daemon 的本體是 `collector direct`(監看/萃卡/上傳),App 只是它的門面; +# 沒同綑 ⇒ 裝了也不會同步。 +set -euo pipefail +cd "$(dirname "$0")" +VERSION="${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}" +export PATH="$PATH:$(go env GOPATH)/bin" +echo "🏷 版本:${VERSION}" + +echo "① 編 collector(同綑執行檔,純 stdlib、CGO_ENABLED=0)" +( cd ../.. && CGO_ENABLED=0 go build -ldflags "-s -w" -o "cmd/arcrun-app/arcrun-collector" . ) + +echo "② wails build" +wails build -clean -ldflags "-X main.version=${VERSION}" + +APP="build/bin/Arcrun.app" +[ -d "build/bin/arcrun-app.app" ] && { rm -rf "$APP"; mv "build/bin/arcrun-app.app" "$APP"; } + +echo "③ 同綑 collector 進 .app(App 用 os.Executable() 找同層)" +cp arcrun-collector "$APP/Contents/MacOS/arcrun-collector" + +echo "④ ad-hoc 重簽(改過 bundle 內容必重簽,否則 launchd 拒開)" +codesign --force --deep --sign - "$APP" 2>/dev/null || true + +rm -f arcrun-collector +echo "✅ 完成:$APP" +echo " 驗法:open '$APP' → 選單列出現 icon、Dock **不出現** icon" +echo " 點 icon → 立刻展開視窗;右鍵 → 只有「結束 Arcrun」" diff --git a/cmd/arcrun-app/build/darwin/Info.plist b/cmd/arcrun-app/build/darwin/Info.plist index 4de2887..3d3d717 100644 --- a/cmd/arcrun-app/build/darwin/Info.plist +++ b/cmd/arcrun-app/build/darwin/Info.plist @@ -1,6 +1,10 @@ + + LSUIElement + CFBundlePackageType APPL CFBundleName diff --git a/cmd/arcrun-app/build/trayicon.ico b/cmd/arcrun-app/build/trayicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..88fd04f8679fc85e246aebab872c17f0a8ad6438 GIT binary patch literal 2739 zcmai#X*|?z7svmW7-SzrjM7lHF^o0a&5X%tl(ndaED2+8tl7qreJ6>jk;dS*uVahM zgOYF)DvF_uLP(YfkNfV;^WynD&v|j3>zwoXoHy6+_q_ms1z-aN1c0MB1&Ff(fac$~ zQ@?9Jb^y41)Puu+*FV?*po;?lk}NJ=;N_O!KI-yfOi@-x`R@$^praWey>aLS04%c@ zl#xwHQB%&akB}v}Z&>Oe-iXYW)@@VrFRhUifmFFb7cnyW!^FckV&t^;3R=)}^qCqm zRLO3l4JgNDmnl-m(`M1jSdcv8u?}ZH)+7M?J)L3kaou2XnbUuW*|6) znyH^s5GdSlNM9Lke%FW0F?uiM=EPXFn+rK_BtP_^>*l=amoXXaXteK?uUd+!T z>7PFam(SC*D6j0^b${OM?d1~`YT&m!Xn&j|qI+Y+UA@-Zp0#q^(-b;p9jy~A877l7 zdGe%C(ejGZ?Wvyc-}R4o@iN+k8?PycOC5(gR2iVdd(B*506nfw6K`=H9Wm|s6Eh? zQq1`?isP5LA@GDn#Po$(#?PZC2QVg=P}RmaWB!XUP92R;{UeM!VPQW2fL-giFjlVM zu#&dm1Gg>T`?$6sA}rp@G1pG|sYWITGH(YJeX{PiL7B#MLz73@{`L1T7XzWP-!;Xmz$ zgA(Y=N+SI6*&)}~KQCBTEL0Cs^FDgLUn8hCwLE_Myin_lSl8pQurT+#pZNv`985k| zN`^J~NQ+yN;@DY{HVy}R@0Pj8C%eq=3Em1n2S*JXT)hO_<{o&ZLK_wv)fpaIw{&6% ztLy0vqS3yb9$w2=AK@#^L<+SwAYDNZCY1LRz%J@`I%jMA&rQCjL&bUsi5 zKPlSH+;9pA$X*}BYApxp`edZf_2O#CEiGc~T$VVOj;*Fn5J&9d(h^%1`>3+QJ(#eu{_0& zQoKU13&$`&F|Y7=pzmDd4kmW9a!>&*l7rYw&|K8GV{nVqo-dQiNUDOXDiX&8W7iU%r=JK+jr?2 zI9|~FvYf|sqSlNJDKW?VxfhsBda*WqeTrSWZXLxco?PbKQ&}BW;+l!SOPi#%hn5Ghv#A-s$h-*9gwNGHNsH;e(RLE|!Pl zK!=VjN^<4TmG4oLQ)NX%bpRzQa8WUeP0u+CwO?2w_kVcv_g4k~hc{PQejVsAKwODxLlFtm~D2mVl~BJ`m*R@(=;FSn^bWa9*tE0Xjs2pcl zsp)KQKlwHHb*-kxUy%eGu)=GRwe@xX5nevNIg3&VbJ&q!hH#Q^#54KYHzFb;+#9bs zdHbNFq70=rHaB|`EnV0ipmnkq#iC1y%^DAt zw+Kn8x%7Yr39Itlt(-UIv^&&$&CKD#6l1#1&fea}Q0VH%2=IyI%$x`-ixTtUKYV>_ z?kaXuUhHn3%*=fn^kOqTy`}bA6ovOf$d3Bmz4aVXCC57Ml@;F*<_#Jg!pZ;b!b44* zh0C)m#M)Z;HtX!{X#A8b1Ofr^@|vLHQVnGOl1}?nBYU{UICG`?63QDVwOXW#D9L)9 zAB&B+PcCg6NY!b$-741oUIT9t_#ri2t7h$Kt6&;0{};Ng&}kZ3%g3sOM$4UqfWdK3 zdhXB&t#%gH4p+FxM=5;sLbpjXUBUcnYNHGVB9Um(Cx4+SYWIiu5iwmn9a_31qJYTA z$Pv%6Sq1;PI=HB~fRNB2-q$@OZh2bo(`Q#6Pj!!LZDRdW(Z(<;<+aFtS?Gwu{L0E} zd{+R4J#cdqE-DK0+>+9cCK8_uYEnCPC$?XEBz*pEdd|gpuIVI`1tiRqI~}d>au^^!F6D5U-3wON(C=$t4zC2o%LrNd%7=w{hire-$JKqUFx!*gd(tHG z@@4s(=QA&gMzD53bvN!TmeSjpoEjwiU^UKdlyx@IR$;xqD$U0%SUpFyyDRR0y9C?J T_U*fHI4&K7)f;B~r|16-m_FiC literal 0 HcmV?d00001 diff --git a/cmd/arcrun-app/build/trayicon.png b/cmd/arcrun-app/build/trayicon.png new file mode 100644 index 0000000000000000000000000000000000000000..0c7ad4f749ef61e65eefbf0c69310a5ce6c43f7e GIT binary patch literal 33422 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 /Users/youlinhsieh/go/pkg/mod + +// supervisor 套件在 collector 主模組裡(重起退避/狀態機/round 解析都在那)—— +// 用 replace 指過去,**不複製一份**(複製=兩份會漂移,今天已被 leo 點破過同類問題)。 +require arcrun-rag/collector v0.0.0 + +replace arcrun-rag/collector => ../.. diff --git a/cmd/arcrun-app/go.sum b/cmd/arcrun-app/go.sum index b664a33..955bc0e 100644 --- a/cmd/arcrun-app/go.sum +++ b/cmd/arcrun-app/go.sum @@ -4,6 +4,8 @@ github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/energye/systray v1.0.3 h1:XnyjJCeRU5z00bpNOic2fGTKz/7yHZMZjWiGIVXDS+4= +github.com/energye/systray v1.0.3/go.mod h1:HelKhC3PXwv3ryDxbuQqV+7kAxAYNzE5cfdrerGOZTc= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -61,23 +63,23 @@ github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhw github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/wails/v2 v2.13.0 h1:S7OgXWpj72V91unF8iDWJKbcS9ZpwCT3R0QVru4v2Mg= github.com/wailsapp/wails/v2 v2.13.0/go.mod h1:nVr/wSIEZ7xxKPkzK65mjpKpaOPQI2k4pvLwGR/i4kc= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/arcrun-app/main.go b/cmd/arcrun-app/main.go index 0fa252d..a7660a7 100644 --- a/cmd/arcrun-app/main.go +++ b/cmd/arcrun-app/main.go @@ -1,39 +1,75 @@ package main import ( + "context" "embed" + "github.com/energye/systray" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/assetserver" + wruntime "github.com/wailsapp/wails/v2/pkg/runtime" ) //go:embed all:frontend/dist var assets embed.FS +//go:embed build/trayicon.png +var trayIcon []byte + +type wailsCtx = context.Context + // 版本號由 build 時 -ldflags 注入(同 arcrun-tray 的規矩)。 var version = "dev" func main() { - // Create an instance of the app structure app := NewApp() - // Create application with options err := wails.Run(&options.App{ - Title: "arcrun-app", - Width: 1024, - Height: 768, - AssetServer: &assetserver.Options{ - Assets: assets, + Title: "Arcrun", + Width: 1000, Height: 700, + MinWidth: 820, MinHeight: 560, + AssetServer: &assetserver.Options{Assets: assets}, + OnStartup: func(ctx wailsCtx) { + app.startup(ctx) + startSupervisor() // 🔴 daemon 本體:不啟動就不會同步 + setupTray(app) }, - BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, - OnStartup: app.startup, - Bind: []interface{}{ - app, + // 🔴 daemon 是常駐程式:關窗只是收起來,**不能真的結束** + // (結束=停止看守資料夾,使用者不會預期按 × 就不再同步)。 + OnBeforeClose: func(ctx wailsCtx) bool { + wruntime.WindowHide(ctx) + return true // true=攔下關閉 }, + HideWindowOnClose: true, + // Dock 不出現 icon:Wails v2.13 的 mac.ActivationPolicy 還沒開放(原始碼裡是註解掉的), + // 改走 Info.plist 的 LSUIElement(與 fyne 版同一個做法,見 build/darwin/Info.plist)。 + Bind: []interface{}{app}, }) - if err != nil { println("Error:", err.Error()) } } + +// setupTray 建立選單列 icon。 +// +// 🔴 leo 2026-08-05:「依照 google drive,托盤裡**只剩下按右鍵會結束**, +// 其他設定都在這個頁面,**點擊托盤的 icon 就立刻展開界面**」 +// +// ⇒ 左鍵=直接開視窗(不彈選單);右鍵=只有一項「結束 Arcrun」。 +// 所有設定都在視窗裡,托盤不再是第二套 UI(fyne 版把設定塞在下拉選單, +// leo:「daemon 設定項越來越多,不可能統統塞在下拉選單」)。 +func setupTray(app *App) { + go systray.Run(func() { + systray.SetTemplateIcon(trayIcon, trayIcon) + systray.SetTooltip("Arcrun — 你的知識庫同步小幫手") + + // 左鍵:立刻展開主視窗 + systray.SetOnClick(func(menu systray.IMenu) { app.ShowWindow() }) + + // 右鍵:只有「結束」一項 + quit := systray.AddMenuItem("結束 Arcrun", "停止同步並關閉") + quit.Click(func() { app.Quit() }) + systray.SetOnRClick(func(menu systray.IMenu) { _ = menu.ShowMenu() }) + }, nil) +} diff --git a/cmd/arcrun-app/supervise.go b/cmd/arcrun-app/supervise.go new file mode 100644 index 0000000..0f7e02e --- /dev/null +++ b/cmd/arcrun-app/supervise.go @@ -0,0 +1,56 @@ +package main + +// supervise.go — 看守 collector 子行程(t194) +// +// 🔴 沒有這一段,這個 App 就只是個「會顯示設定的空殼」—— +// daemon 的本體是 `collector direct`,它才在監看資料夾、萃卡、上傳。 +// (寫 Wails 版時我一度漏了它,等於做出一個裝了也不會同步的東西。) +// +// 邊界:**複用既有的 supervisor 套件**(重起退避、狀態機、round 解析都在裡面, +// 含 t191 的 phase:start/done ⇒ 「同步中…」)。這裡只做「找到執行檔、拉起來」。 +import ( + "os" + "path/filepath" + "runtime" + + "arcrun-rag/collector/supervisor" +) + +var sup *supervisor.Supervisor + +// collectorBinPath 找同綑的 collector 執行檔。 +// 規則與 arcrun-tray 一致:跟 App 執行檔同層(build 時會複製進 .app)。 +func collectorBinPath() string { + name := "arcrun-collector" + if runtime.GOOS == "windows" { + name += ".exe" + } + exe, err := os.Executable() + if err != nil { + return name + } + if p, err := filepath.EvalSymlinks(exe); err == nil { + exe = p + } + return filepath.Join(filepath.Dir(exe), name) +} + +// startSupervisor 在 App 啟動時拉起 collector;沒有 config 就先不啟動 +// (使用者還沒連知識庫,啟動只會一直失敗刷 log)。 +func startSupervisor() { + sup = supervisor.New(collectorBinPath(), configPath()) + if _, err := os.Stat(configPath()); err == nil { + sup.Start() + } +} + +// restartWatch 在設定變更後重起看守,讓新設定立刻生效。 +func restartWatch() { + if sup == nil { + return + } + sup.Stop() + if _, err := os.Stat(configPath()); err == nil { + sup.Start() + } +}