Skip to content

Commit 2efd1bb

Browse files
authored
Merge pull request #2146 from dgageot/better-tmux
Better rendering in tmux and ghostty
2 parents 6db2705 + 8fb16e8 commit 2efd1bb

4 files changed

Lines changed: 3 additions & 87 deletions

File tree

cmd/root/new.go

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,7 @@ func runTUI(ctx context.Context, rt runtime.Runtime, sess *session.Session, spaw
108108
wd, _ := os.Getwd()
109109
model := tui.New(ctx, spawner, a, wd, cleanup)
110110

111-
programOpts := []tea.ProgramOption{tea.WithContext(ctx), tea.WithFilter(filter)}
112-
113-
// Terminal multiplexers can drop frames at the default 60 FPS, making
114-
// spinners appear static. Cap the renderer at 30 FPS when detected.
115-
if inMultiplexer() {
116-
programOpts = append(programOpts, tea.WithFPS(30))
117-
}
118-
119-
p := tea.NewProgram(model, programOpts...)
111+
p := tea.NewProgram(model, tea.WithContext(ctx), tea.WithFilter(filter))
120112
coalescer.SetSender(p.Send)
121113

122114
if m, ok := model.(interface{ SetProgram(p *tea.Program) }); ok {
@@ -126,16 +118,3 @@ func runTUI(ctx context.Context, rt runtime.Runtime, sess *session.Session, spaw
126118
_, err := p.Run()
127119
return err
128120
}
129-
130-
// inMultiplexer reports whether the process is running inside a terminal
131-
// multiplexer (tmux, screen).
132-
func inMultiplexer() bool {
133-
if os.Getenv("TMUX") != "" {
134-
return true
135-
}
136-
if os.Getenv("STY") != "" {
137-
return true
138-
}
139-
term := os.Getenv("TERM")
140-
return strings.HasPrefix(term, "tmux") || strings.HasPrefix(term, "screen")
141-
}

pkg/tui/components/scrollbar/scrollbar.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func New() *Model {
3535
return &Model{
3636
width: Width,
3737
trackChar: "⎪",
38-
thumbChar: "",
38+
thumbChar: "",
3939
}
4040
}
4141

pkg/tui/components/spinner/spinner.go

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package spinner
22

33
import (
44
"math/rand/v2"
5-
"os"
65
"strings"
76

87
tea "charm.land/bubbletea/v2"
@@ -138,37 +137,7 @@ func (s *spinner) Stop() {
138137
}
139138

140139
// spinnerFrames holds the animation frames for the current terminal.
141-
// Braille characters are used by default; inside tmux they don't render
142-
// correctly, so we fall back to ASCII.
143-
var spinnerFrames = selectFrames(inMultiplexer())
144-
145-
// inMultiplexer reports whether the process is running inside a terminal
146-
// multiplexer (tmux, screen). Detection checks multiple env vars because
147-
// some of them may be stripped in containers or sudo sessions.
148-
func inMultiplexer() bool {
149-
if os.Getenv("TMUX") != "" {
150-
return true
151-
}
152-
if os.Getenv("STY") != "" { // GNU screen
153-
return true
154-
}
155-
term := os.Getenv("TERM")
156-
return strings.HasPrefix(term, "tmux") || strings.HasPrefix(term, "screen")
157-
}
158-
159-
var (
160-
brailleFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
161-
asciiFrames = []string{"|", "/", "-", "\\"}
162-
)
163-
164-
// selectFrames returns ASCII spinner frames when inTmux is true,
165-
// braille frames otherwise.
166-
func selectFrames(inTmux bool) []string {
167-
if inTmux {
168-
return asciiFrames
169-
}
170-
return brailleFrames
171-
}
140+
var spinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
172141

173142
// Frame returns the spinner character for the given animation frame.
174143
func Frame(index int) string {

pkg/tui/components/spinner/spinner_test.go

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,38 +28,6 @@ func TestFrameWrapsAround(t *testing.T) {
2828
require.Equal(t, spinnerFrames[0], Frame(n), "Frame should wrap around")
2929
}
3030

31-
func TestSelectFrames(t *testing.T) {
32-
require.Equal(t, brailleFrames, selectFrames(false))
33-
require.Equal(t, asciiFrames, selectFrames(true))
34-
}
35-
36-
func TestInMultiplexer(t *testing.T) {
37-
tests := []struct {
38-
name string
39-
env map[string]string
40-
want bool
41-
}{
42-
{"plain terminal", map[string]string{"TERM": "xterm-256color"}, false},
43-
{"TMUX set", map[string]string{"TMUX": "/tmp/tmux-1000/default,123,0"}, true},
44-
{"STY set", map[string]string{"STY": "12345.pts-0"}, true},
45-
{"TERM=tmux-256color", map[string]string{"TERM": "tmux-256color"}, true},
46-
{"TERM=screen-256color", map[string]string{"TERM": "screen-256color"}, true},
47-
{"TERM=screen", map[string]string{"TERM": "screen"}, true},
48-
}
49-
for _, tt := range tests {
50-
t.Run(tt.name, func(t *testing.T) {
51-
// Clear all multiplexer env vars
52-
t.Setenv("TMUX", "")
53-
t.Setenv("STY", "")
54-
t.Setenv("TERM", "")
55-
for k, v := range tt.env {
56-
t.Setenv(k, v)
57-
}
58-
require.Equal(t, tt.want, inMultiplexer())
59-
})
60-
}
61-
}
62-
6331
func BenchmarkSpinner_ModeSpinnerOnly(b *testing.B) {
6432
s := New(ModeSpinnerOnly, lipgloss.NewStyle())
6533
for b.Loop() {

0 commit comments

Comments
 (0)