Skip to content

Commit 6239d68

Browse files
committed
[FIX] TUI.
1 parent 21452dd commit 6239d68

1 file changed

Lines changed: 56 additions & 8 deletions

File tree

internal/ui/run.go

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package ui
33
import (
44
"context"
55
"os"
6+
"runtime"
7+
"strconv"
68

79
tea "github.com/charmbracelet/bubbletea"
810
"github.com/charmbracelet/x/term"
@@ -17,21 +19,67 @@ func Run(ctx context.Context, mode Mode, events <-chan domain.Event, meta Meta)
1719
func RunWithCancel(ctx context.Context, mode Mode, events <-chan domain.Event, actions chan<- domain.Action, meta Meta, cancel func()) error {
1820
m := NewModel(ctx, mode, events, actions, meta, cancel)
1921

20-
// Bubble Tea relies on terminal size messages to render the UI. In some environments
21-
// (e.g., wrapped CLIs, certain PTYs, or misconfigured terminals) the size cannot be
22-
// detected, leaving the UI stuck at the "Loading…" placeholder. Seed a sensible
23-
// initial size so the UI can render and progress even if WindowSizeMsg never arrives.
24-
if w, h, err := term.GetSize(os.Stdout.Fd()); err == nil && w > 0 && h > 0 {
22+
in := os.Stdin
23+
out := os.Stdout
24+
var tty *os.File
25+
26+
// If we're launched through a wrapper that doesn't preserve TTY stdio (e.g., a PHP
27+
// bootstrapper), Bubble Tea might not be able to detect the real window size. When
28+
// possible, attach directly to /dev/tty.
29+
if runtime.GOOS != "windows" && (!term.IsTerminal(in.Fd()) || !term.IsTerminal(out.Fd())) {
30+
f, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
31+
if err == nil {
32+
tty = f
33+
in = f
34+
out = f
35+
}
36+
}
37+
if tty != nil {
38+
defer tty.Close()
39+
}
40+
41+
// Seed a sensible initial size so the UI can render even if WindowSizeMsg never arrives.
42+
if w, h, ok := detectTerminalSize(in, out, tty); ok {
2543
m.width = w
2644
m.height = h
27-
m.reflow()
2845
} else {
2946
m.width = 80
3047
m.height = 24
31-
m.reflow()
3248
}
49+
m.reflow()
3350

34-
p := tea.NewProgram(m, tea.WithAltScreen())
51+
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithInput(in), tea.WithOutput(out))
3552
_, err := p.Run()
3653
return err
3754
}
55+
56+
func detectTerminalSize(in *os.File, out *os.File, tty *os.File) (w int, h int, ok bool) {
57+
fds := []uintptr{}
58+
if out != nil {
59+
fds = append(fds, out.Fd())
60+
}
61+
if in != nil {
62+
fds = append(fds, in.Fd())
63+
}
64+
if tty != nil {
65+
fds = append(fds, tty.Fd())
66+
}
67+
fds = append(fds, os.Stderr.Fd())
68+
69+
for _, fd := range fds {
70+
if fd == 0 || !term.IsTerminal(fd) {
71+
continue
72+
}
73+
if tw, th, err := term.GetSize(fd); err == nil && tw > 0 && th > 0 {
74+
return tw, th, true
75+
}
76+
}
77+
78+
if cols, err := strconv.Atoi(os.Getenv("COLUMNS")); err == nil && cols > 0 {
79+
if lines, err := strconv.Atoi(os.Getenv("LINES")); err == nil && lines > 0 {
80+
return cols, lines, true
81+
}
82+
}
83+
84+
return 0, 0, false
85+
}

0 commit comments

Comments
 (0)