From 59cf9825d1ca0045f61bb1121f49bf5562c892c8 Mon Sep 17 00:00:00 2001 From: richblack Date: Tue, 28 Jul 2026 00:42:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(t75=20=E2=91=A3=E2=91=A4):=20=E9=80=A3?= =?UTF-8?q?=E7=B7=9A=E7=B2=BE=E9=9D=88=E4=B8=8D=E5=86=8D=E6=B8=85=E7=A9=BA?= =?UTF-8?q?=E8=BC=B8=E5=85=A5=EF=BC=8BWindows=20=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E6=AF=8F=E5=B9=BE=E7=A7=92=E9=96=83=20console=20=E8=A6=96?= =?UTF-8?q?=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit leo 同事真機實測撞到的兩個最嚴重問題。 ④ 連線失敗會清空使用者輸入(leo:「很糟糕」) 真兇:失敗後呼叫 showConnectWizard(),註解寫著「不要把他丟回空白畫面」 **但行為與註解相反**——它重建整個對話框,urlEntry 是全新的, 而 cfg.CypherURL 因連線失敗根本沒寫入 ⇒ 欄位就是空的 ⇒ 使用者整段重打。 修:改 showConnectWizardWith(prevURL, prevEmail) 帶回上次輸入, 讓他只改錯的那個字。密碼不帶回(打錯時重打較安全,也少留記憶體)。 ⑤ daemon 開著時每幾秒閃一個方框(約半螢幕) 真兇:arcrun-collector.exe 是 console 型執行檔,supervisor 每輪 exec 起它, **Windows 預設會為 console 程式配可見視窗** ⇒ 每輪閃一次。 Mac/Linux 無此概念故開發期完全沒想到。 修:新增 hidewindow_windows.go(HideWindow + CREATE_NO_WINDOW) +hidewindow_other.go(no-op),編譯期分平台不寫 runtime.GOOS 判斷。 註:-H windowsgui 是連結期選項(管自己有沒有視窗),與此(管起別人時給不給視窗) 是兩件事,兩者都要做才乾淨。 驗證:Mac 與 Windows 交叉編譯皆 exit=0 且產物生成;collector 全測試綠。 ⚠️ 未真機驗——⑤ 的效果必須在真 Windows 上確認方框消失。 --- cmd/arcrun-tray/main.go | 23 +++++++++++++++++++---- supervisor/hidewindow_other.go | 10 ++++++++++ supervisor/hidewindow_windows.go | 31 +++++++++++++++++++++++++++++++ supervisor/supervisor.go | 3 +++ 4 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 supervisor/hidewindow_other.go create mode 100644 supervisor/hidewindow_windows.go diff --git a/cmd/arcrun-tray/main.go b/cmd/arcrun-tray/main.go index 76181bc..405da3a 100644 --- a/cmd/arcrun-tray/main.go +++ b/cmd/arcrun-tray/main.go @@ -422,16 +422,30 @@ func main() { // t54 連線精靈:輸入知識庫網址+帳密 → 換設定 → 寫檔 → 開始看守。 // 沒連線過的機器一開 app 就自動跳;之後也能從選單「重新連線…」再開。 + // t75(2026-07-28,leo 同事真機實測):**連線失敗重試時必須把使用者剛打的內容帶回來**。 + // + // 原本的寫法是失敗後直接呼叫 `showConnectWizard()`,註解寫著「不要把他丟回空白畫面」—— + // **但行為與註解相反**:它重建整個對話框、`urlEntry` 是全新的,而 `cfg.CypherURL` + // 因為連線失敗根本沒被寫入 ⇒ 欄位就是空的 ⇒ **使用者得整段重打**。 + // leo 原話:「貼上網址後說這不是個網址然後清空內容,**很糟糕**」。 + // ⇒ 改成帶入上次輸入(prefill)。密碼不帶回(打錯密碼時重打比較安全,也避免把密碼留在記憶體更久)。 var showConnectWizard func() - showConnectWizard = func() { + var showConnectWizardWith func(prevURL, prevEmail string) + showConnectWizard = func() { showConnectWizardWith("", "") } + showConnectWizardWith = func(prevURL, prevEmail string) { urlEntry := widget.NewEntry() urlEntry.SetPlaceHolder("https://…workers.dev/portal/") - if cfg.CypherURL != "" { + // 優先用「使用者剛才打的」,其次才是已存的設定——重試時他要看到的是自己的輸入。 + if prevURL != "" { + urlEntry.SetText(prevURL) + } else if cfg.CypherURL != "" { urlEntry.SetText(cfg.CypherURL) } emailEntry := widget.NewEntry() emailEntry.SetPlaceHolder("你的 Email") - if cfg.Email != "" { + if prevEmail != "" { + emailEntry.SetText(prevEmail) + } else if cfg.Email != "" { emailEntry.SetText(cfg.Email) } pwEntry := widget.NewPasswordEntry() @@ -454,7 +468,8 @@ func main() { if err != nil { // fyne 的 UI 更新須回主執行緒;用 dialog 顯示即可(它內部處理) dialog.ShowError(err, win) - showConnectWizard() // 讓用戶改完再試,不要把他丟回空白畫面 + // t75:把剛才打的網址與 email 帶回去,讓他只改錯的那個字,不必整段重打。 + showConnectWizardWith(urlEntry.Text, emailEntry.Text) return } applyRemoteConfig(cfg, r) diff --git a/supervisor/hidewindow_other.go b/supervisor/hidewindow_other.go new file mode 100644 index 0000000..1d0ddee --- /dev/null +++ b/supervisor/hidewindow_other.go @@ -0,0 +1,10 @@ +//go:build !windows + +package supervisor + +import "os/exec" + +// hideChildWindow 在非 Windows 平台是 no-op: +// Unix 系沒有「起子行程會配一個視窗」這回事(那是 Windows console subsystem 的行為)。 +// 保留同名函式是為了讓 supervisor.go 不必寫 runtime.GOOS 判斷(編譯期就分好)。 +func hideChildWindow(_ *exec.Cmd) {} diff --git a/supervisor/hidewindow_windows.go b/supervisor/hidewindow_windows.go new file mode 100644 index 0000000..a952eaa --- /dev/null +++ b/supervisor/hidewindow_windows.go @@ -0,0 +1,31 @@ +//go:build windows + +package supervisor + +import ( + "os/exec" + "syscall" +) + +// CREATE_NO_WINDOW:起子行程時不要配一個新的 console 視窗。 +// +// 為什麼需要(t75,2026-07-28 leo 同事在真 Windows 上實測撞到): +// 使用者回報「**daemon 開啟時每幾秒螢幕會閃一個方框,大概有螢幕一半大,關閉它就消失**」。 +// 根因=`arcrun-collector.exe` 是 **console 型執行檔**(`PE32+ (console)`), +// supervisor 每輪起它一次,**Windows 預設會為 console 程式配一個可見的視窗** ⇒ 每輪閃一下。 +// +// **Mac/Linux 沒有這個概念**,所以這個跨平台差異在開發期完全沒被想到—— +// 這也是為什麼「真機驗證」不能只驗自己改過的東西(見 t75 教訓)。 +// +// 注意:`-H windowsgui` 是**連結期**選項,只影響「這支程式自己有沒有視窗」; +// 這裡處理的是「**我起別人時要不要給它視窗**」,是不同一件事,兩者都要做才乾淨。 +const createNoWindow = 0x08000000 + +// hideChildWindow 讓子行程不要彈出 console 視窗(僅 Windows 有效)。 +func hideChildWindow(cmd *exec.Cmd) { + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + cmd.SysProcAttr.HideWindow = true + cmd.SysProcAttr.CreationFlags |= createNoWindow +} diff --git a/supervisor/supervisor.go b/supervisor/supervisor.go index 57ccea4..7712ab8 100644 --- a/supervisor/supervisor.go +++ b/supervisor/supervisor.go @@ -251,6 +251,9 @@ func (s *Supervisor) loop(ctx context.Context) { // runOnce 跑一次 collector direct 行程,串流其 stdout JSON 更新狀態,回傳退出原因。 func (s *Supervisor) runOnce(ctx context.Context) error { cmd := exec.CommandContext(ctx, s.BinPath, "direct", "--config", s.ConfigPath) + // t75:Windows 上不要讓 collector 彈出 console 視窗(每輪閃一次,使用者會以為中毒)。 + // 非 Windows 平台為 no-op,見 hidewindow_*.go。 + hideChildWindow(cmd) stdout, err := cmd.StdoutPipe() if err != nil { return err