Skip to content

Commit 47e3ddd

Browse files
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 <noreply@anthropic.com>
1 parent afe0817 commit 47e3ddd

4 files changed

Lines changed: 22 additions & 2 deletions

File tree

backend/internal/adapters/runtime/zellij/process_other.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
package zellij
44

5-
import "errors"
5+
import (
6+
"errors"
7+
"os/exec"
8+
)
9+
10+
// hideWindow is a no-op off Windows: only Windows pops a console window.
11+
func hideWindow(*exec.Cmd) {}
612

713
// startBackgroundProcess is a stub: the fire-and-forget path is only used by
814
// the Windows zellij codepath. Non-Windows builds create sessions

backend/internal/adapters/runtime/zellij/process_windows.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,23 @@ import (
1010
"golang.org/x/sys/windows"
1111
)
1212

13+
// hideWindow suppresses the console window for a console-subsystem child so
14+
// frequent zellij calls (e.g. the reaper's list-sessions) don't flash on Windows.
15+
func hideWindow(cmd *exec.Cmd) {
16+
cmd.SysProcAttr = &syscall.SysProcAttr{
17+
HideWindow: true,
18+
CreationFlags: windows.CREATE_NO_WINDOW,
19+
}
20+
}
21+
1322
func startBackgroundProcess(env []string, name string, args ...string) error {
1423
script := "Start-Process -FilePath " + psQuote(name) + " -ArgumentList " + psQuote(windowsCommandLine(args)) + " -WindowStyle Hidden"
1524
cmd := exec.Command("powershell.exe", "-NoLogo", "-NoProfile", "-EncodedCommand", powerShellEncodedCommand(script))
1625
cmd.Env = env
1726
cmd.SysProcAttr = &syscall.SysProcAttr{
18-
CreationFlags: windows.CREATE_NEW_CONSOLE,
27+
// CREATE_NO_WINDOW, not CREATE_NEW_CONSOLE: the latter creates a console
28+
// then hides it, which flashed a window.
29+
CreationFlags: windows.CREATE_NO_WINDOW,
1930
HideWindow: true,
2031
}
2132
if err := cmd.Start(); err != nil {

backend/internal/adapters/runtime/zellij/zellij.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ type execRunner struct{}
9393
func (execRunner) Run(ctx context.Context, env []string, name string, args ...string) ([]byte, error) {
9494
cmd := exec.CommandContext(ctx, name, args...)
9595
cmd.Env = zellijCommandEnv(os.Environ(), env)
96+
hideWindow(cmd)
9697
return cmd.CombinedOutput()
9798
}
9899

frontend/src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ async function startDaemonInner(startEpoch: number): Promise<DaemonStatus> {
506506
env: daemonEnv(),
507507
shell: launch.shell,
508508
detached: true,
509+
// Hide the daemon's console on a Windows GUI launch (no flashing terminal).
510+
windowsHide: true,
509511
});
510512
daemonProcess = child;
511513

0 commit comments

Comments
 (0)