summaryrefslogtreecommitdiff
path: root/internal/tui/eventstream/model.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-25 22:58:40 +0200
committerPaul Buetow <paul@buetow.org>2026-02-25 22:58:40 +0200
commit4c34b9efcd539c819648c927d7e3f53220df8ad2 (patch)
treef9de9fd650a2d16316ba2c159990d891c9de5189 /internal/tui/eventstream/model.go
parent67e10f34c92e93343adbd690b3b21e455e863bd3 (diff)
Fix stream paused scrolling and apply pending TUI/probe updates
Diffstat (limited to 'internal/tui/eventstream/model.go')
-rw-r--r--internal/tui/eventstream/model.go88
1 files changed, 77 insertions, 11 deletions
diff --git a/internal/tui/eventstream/model.go b/internal/tui/eventstream/model.go
index f51b7b5..0c50d0c 100644
--- a/internal/tui/eventstream/model.go
+++ b/internal/tui/eventstream/model.go
@@ -36,6 +36,17 @@ func NewModel(source *RingBuffer) Model {
}
}
+// SetViewport updates the render/scroll viewport dimensions used for
+// max-scroll and page-step calculations during key handling.
+func (m *Model) SetViewport(width, height int) {
+ if width > 0 {
+ m.width = width
+ }
+ if height > 0 {
+ m.height = height
+ }
+}
+
// SetSource updates the backing ring buffer and refreshes visible rows.
func (m *Model) SetSource(source *RingBuffer) {
m.source = source
@@ -47,6 +58,11 @@ func (m Model) FilterModalVisible() bool {
return m.filterModal.Visible()
}
+// Paused reports whether stream refresh is currently paused.
+func (m Model) Paused() bool {
+ return m.paused
+}
+
func (m *Model) HandleKey(keyStr string) bool {
if m.filterModal.Visible() {
wasVisible := m.filterModal.Visible()
@@ -66,10 +82,12 @@ func (m *Model) HandleKey(keyStr string) bool {
case " ", "space":
m.paused = !m.paused
if !m.paused {
+ // Resuming should return to live-tail behavior immediately.
+ m.autoScroll = true
m.Refresh()
}
return true
- case "f":
+ case "f", "F":
m.pauseBeforeFilter = m.paused
m.paused = true
m.filterModal = m.filterModal.Open(m.filter)
@@ -87,24 +105,44 @@ func (m *Model) HandleKey(keyStr string) bool {
m.applyFilter()
return true
case "j", "down":
- if m.scrollOffset < m.maxScrollOffset() {
- m.scrollOffset++
- }
- if m.scrollOffset < m.maxScrollOffset() {
- m.autoScroll = false
- }
+ m.scrollByLines(1)
return true
case "k", "up":
- if m.scrollOffset > 0 {
- m.scrollOffset--
- }
- m.autoScroll = false
+ m.scrollByLines(-1)
+ return true
+ case "pgdown", "pgdn", "pagedown":
+ m.scrollByLines(m.pageStep())
+ return true
+ case "pgup", "pageup":
+ m.scrollByLines(-m.pageStep())
return true
default:
return false
}
}
+// HandleTeaKey handles stream keys based on Bubble Tea key message types first,
+// then falls back to string matching for rune-driven shortcuts.
+func (m *Model) HandleTeaKey(msg tea.KeyMsg) bool {
+ switch msg.Type {
+ case tea.KeyUp:
+ return m.HandleKey("up")
+ case tea.KeyDown:
+ return m.HandleKey("down")
+ case tea.KeyPgUp:
+ return m.HandleKey("pgup")
+ case tea.KeyPgDown:
+ return m.HandleKey("pgdown")
+ case tea.KeySpace:
+ return m.HandleKey("space")
+ case tea.KeyRunes:
+ if len(msg.Runes) == 1 {
+ return m.HandleKey(string(msg.Runes[0]))
+ }
+ }
+ return m.HandleKey(msg.String())
+}
+
func (m *Model) View(width, height int) string {
if width <= 0 {
width = 100
@@ -198,6 +236,34 @@ func (m *Model) visibleRows() int {
return rows
}
+func (m *Model) pageStep() int {
+ rows := m.visibleRows()
+ if rows <= 1 {
+ return 1
+ }
+ return rows - 1
+}
+
+func (m *Model) scrollByLines(delta int) {
+ if delta == 0 {
+ return
+ }
+ max := m.maxScrollOffset()
+ next := m.scrollOffset + delta
+ if next < 0 {
+ next = 0
+ }
+ if next > max {
+ next = max
+ }
+ if next != m.scrollOffset {
+ m.scrollOffset = next
+ }
+ if m.scrollOffset < max {
+ m.autoScroll = false
+ }
+}
+
func keyMsgFromString(keyStr string) tea.KeyMsg {
switch keyStr {
case "esc":