From 47e3dddff5d096b7d18b8c4e81a09e34a9fb203f Mon Sep 17 00:00:00 2001 From: Harshit Singh Bhandari Date: Tue, 23 Jun 2026 17:49:04 +0530 Subject: [PATCH] fix(desktop): stop console-window flashing on Windows (#399) On Windows the daemon shells out to console-subsystem processes without suppressing the console window. The reaper polls session liveness on a timer (and the attach loop retries), each call runs a zellij command, and every invocation pops a console window that instantly closes, so the user sees rapid blinking. - Add a platform-split hideWindow(cmd) helper to the zellij package (CREATE_NO_WINDOW + HideWindow on Windows, no-op elsewhere) and call it in execRunner.Run so list-sessions and friends stop flashing. - startBackgroundProcess now uses CREATE_NO_WINDOW instead of CREATE_NEW_CONSOLE (the latter creates a console then hides it, flashing). - Pass windowsHide: true to the daemon spawn in main.ts so the daemon's own console stays hidden on a Windows GUI launch. Co-authored-by: Claude Opus 4.8 --- .../adapters/runtime/zellij/process_other.go | 8 +++++++- .../adapters/runtime/zellij/process_windows.go | 13 ++++++++++++- backend/internal/adapters/runtime/zellij/zellij.go | 1 + frontend/src/main.ts | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/backend/internal/adapters/runtime/zellij/process_other.go b/backend/internal/adapters/runtime/zellij/process_other.go index fa963c4c2..69e955f36 100644 --- a/backend/internal/adapters/runtime/zellij/process_other.go +++ b/backend/internal/adapters/runtime/zellij/process_other.go @@ -2,7 +2,13 @@ package zellij -import "errors" +import ( + "errors" + "os/exec" +) + +// hideWindow is a no-op off Windows: only Windows pops a console window. +func hideWindow(*exec.Cmd) {} // startBackgroundProcess is a stub: the fire-and-forget path is only used by // the Windows zellij codepath. Non-Windows builds create sessions diff --git a/backend/internal/adapters/runtime/zellij/process_windows.go b/backend/internal/adapters/runtime/zellij/process_windows.go index f58ea45e1..513019630 100644 --- a/backend/internal/adapters/runtime/zellij/process_windows.go +++ b/backend/internal/adapters/runtime/zellij/process_windows.go @@ -10,12 +10,23 @@ import ( "golang.org/x/sys/windows" ) +// hideWindow suppresses the console window for a console-subsystem child so +// frequent zellij calls (e.g. the reaper's list-sessions) don't flash on Windows. +func hideWindow(cmd *exec.Cmd) { + cmd.SysProcAttr = &syscall.SysProcAttr{ + HideWindow: true, + CreationFlags: windows.CREATE_NO_WINDOW, + } +} + func startBackgroundProcess(env []string, name string, args ...string) error { script := "Start-Process -FilePath " + psQuote(name) + " -ArgumentList " + psQuote(windowsCommandLine(args)) + " -WindowStyle Hidden" cmd := exec.Command("powershell.exe", "-NoLogo", "-NoProfile", "-EncodedCommand", powerShellEncodedCommand(script)) cmd.Env = env cmd.SysProcAttr = &syscall.SysProcAttr{ - CreationFlags: windows.CREATE_NEW_CONSOLE, + // CREATE_NO_WINDOW, not CREATE_NEW_CONSOLE: the latter creates a console + // then hides it, which flashed a window. + CreationFlags: windows.CREATE_NO_WINDOW, HideWindow: true, } if err := cmd.Start(); err != nil { diff --git a/backend/internal/adapters/runtime/zellij/zellij.go b/backend/internal/adapters/runtime/zellij/zellij.go index f73261380..03a1f9397 100644 --- a/backend/internal/adapters/runtime/zellij/zellij.go +++ b/backend/internal/adapters/runtime/zellij/zellij.go @@ -93,6 +93,7 @@ type execRunner struct{} func (execRunner) Run(ctx context.Context, env []string, name string, args ...string) ([]byte, error) { cmd := exec.CommandContext(ctx, name, args...) cmd.Env = zellijCommandEnv(os.Environ(), env) + hideWindow(cmd) return cmd.CombinedOutput() } diff --git a/frontend/src/main.ts b/frontend/src/main.ts index b77e61e81..274a140ca 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -506,6 +506,8 @@ async function startDaemonInner(startEpoch: number): Promise { env: daemonEnv(), shell: launch.shell, detached: true, + // Hide the daemon's console on a Windows GUI launch (no flashing terminal). + windowsHide: true, }); daemonProcess = child;