on main help sign in
Merge pull request #111 from charmbracelet/perf-filter

Improve TUI performance with Bubbletea v2 event filtering

commit55a3756410241d3edb7b0bfc889e3db986b8358d authorRaphael Amorim <rapha850@gmail.com> dateWed 9 Jul 2025, 10:43 (1 year ago) parenteeeb9a0ff4ca8e
4 files changed+32−3
cmd/root.go+2 internal/tui/components/chat/editor/editor.go+1 internal/tui/page/chat/chat.go+6 internal/tui/tui.go+23−3
cmd/root.go+2
@@ -120,6 +120,8 @@ to assist developers in writing, debugging, and understanding code directly from
120120 tea.WithAltScreen(),
121121 tea.WithKeyReleases(),
122122 tea.WithUniformKeyLayout(),
123+ tea.WithMouseCellMotion(), // Use cell motion instead of all motion to reduce event flooding
124+ tea.WithFilter(tui.MouseEventFilter), // Filter mouse events based on focus state
123125 )
124126
125127 // Setup the subscriptions, this will send services events to the TUI
internal/tui/components/chat/editor/editor.go+1
@@ -258,6 +258,7 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
258258 }
259259 }
260260 }
261+
261262 m.textarea, cmd = m.textarea.Update(msg)
262263 cmds = append(cmds, cmd)
263264 return m, tea.Batch(cmds...)
internal/tui/page/chat/chat.go+6
@@ -39,6 +39,7 @@ type (
3939type ChatPage interface {
4040 util.Model
4141 layout.Help
42+ IsChatFocused() bool
4243}
4344
4445type chatPage struct {
@@ -415,3 +416,8 @@ func NewChatPage(app *app.App) ChatPage {
415416 header: header.New(app.LSPClients),
416417 }
417418}
419+
420+// IsChatFocused returns whether the chat messages are focused (true) or editor is focused (false)
421+func (p *chatPage) IsChatFocused() bool {
422+ return p.chatFocused
423+}
internal/tui/tui.go+23−3
@@ -31,6 +31,29 @@ import (
3131 "github.com/charmbracelet/lipgloss/v2"
3232)
3333
34+// MouseEventFilter filters mouse events based on the current focus state
35+// This is used with tea.WithFilter to prevent mouse scroll events from
36+// interfering with typing performance in the editor
37+func MouseEventFilter(m tea.Model, msg tea.Msg) tea.Msg {
38+ // Only filter mouse events
39+ switch msg.(type) {
40+ case tea.MouseWheelMsg, tea.MouseMotionMsg:
41+ // Check if we have an appModel and if editor is focused
42+ if appModel, ok := m.(*appModel); ok {
43+ if appModel.currentPage == chat.ChatPageID {
44+ if chatPage, ok := appModel.pages[appModel.currentPage].(chat.ChatPage); ok {
45+ // If editor is focused (not chatFocused), filter out mouse wheel/motion events
46+ if !chatPage.IsChatFocused() {
47+ return nil // Filter out the event
48+ }
49+ }
50+ }
51+ }
52+ }
53+ // Allow all other events to pass through
54+ return msg
55+}
56+
3457// appModel represents the main application model that manages pages, dialogs, and UI state.
3558type appModel struct {
3659 wWidth, wHeight int // Window dimensions
@@ -82,9 +105,6 @@ func (a appModel) Init() tea.Cmd {
82105 return nil
83106 })
84107
85− // Enable mouse support.
86− cmds = append(cmds, tea.EnableMouseAllMotion)
87−
88108 return tea.Batch(cmds...)
89109}
90110