summaryrefslogtreecommitdiff
path: root/internal/tui/eventstream
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-25 08:55:58 +0200
committerPaul Buetow <paul@buetow.org>2026-02-25 08:55:58 +0200
commit22f1589e62aeafed805b8dd07d4610b7662f205e (patch)
tree1661bd680ccd0d969359c3f3cf3cbd3d8ec4a5a3 /internal/tui/eventstream
parentd423225771a10ebae87d22c69fe88e5b65a3d378 (diff)
Polish stream filter UX and expand TUI viewport
Diffstat (limited to 'internal/tui/eventstream')
-rw-r--r--internal/tui/eventstream/model.go24
-rw-r--r--internal/tui/eventstream/model_test.go48
2 files changed, 71 insertions, 1 deletions
diff --git a/internal/tui/eventstream/model.go b/internal/tui/eventstream/model.go
index fb4b88f..f51b7b5 100644
--- a/internal/tui/eventstream/model.go
+++ b/internal/tui/eventstream/model.go
@@ -17,6 +17,9 @@ type Model struct {
filterModal FilterModal
paused bool
+ // pauseBeforeFilter keeps the pre-modal pause state so opening the filter
+ // can pause refresh temporarily without losing the user's prior state.
+ pauseBeforeFilter bool
scrollOffset int
autoScroll bool
@@ -33,13 +36,28 @@ func NewModel(source *RingBuffer) Model {
}
}
+// SetSource updates the backing ring buffer and refreshes visible rows.
+func (m *Model) SetSource(source *RingBuffer) {
+ m.source = source
+ m.Refresh()
+}
+
+// FilterModalVisible reports whether the filter modal is currently open.
+func (m Model) FilterModalVisible() bool {
+ return m.filterModal.Visible()
+}
+
func (m *Model) HandleKey(keyStr string) bool {
if m.filterModal.Visible() {
wasVisible := m.filterModal.Visible()
m.filterModal = m.filterModal.Update(keyMsgFromString(keyStr))
if wasVisible && !m.filterModal.Visible() {
m.filter = m.filterModal.Filter()
+ m.paused = m.pauseBeforeFilter
m.applyFilter()
+ if !m.paused {
+ m.Refresh()
+ }
}
return true
}
@@ -52,6 +70,8 @@ func (m *Model) HandleKey(keyStr string) bool {
}
return true
case "f":
+ m.pauseBeforeFilter = m.paused
+ m.paused = true
m.filterModal = m.filterModal.Open(m.filter)
return true
case "G":
@@ -113,7 +133,9 @@ func (m *Model) View(width, height int) string {
out := base + "\n" + status
if m.filterModal.Visible() {
- return m.filterModal.View(width, height) + "\n" + out
+ // While editing filters, show a dedicated modal screen to avoid
+ // visual mixing with the live stream table underneath.
+ return m.filterModal.View(width, height)
}
return out
}
diff --git a/internal/tui/eventstream/model_test.go b/internal/tui/eventstream/model_test.go
index 69369d8..937bb33 100644
--- a/internal/tui/eventstream/model_test.go
+++ b/internal/tui/eventstream/model_test.go
@@ -122,3 +122,51 @@ func TestModelHandleKeyRouting(t *testing.T) {
t.Fatalf("modal should close on esc")
}
}
+
+func TestFilterModalTemporarilyPausesAndRestoresState(t *testing.T) {
+ rb := NewRingBuffer()
+ m := NewModel(rb)
+ m.height = 20
+ pushEvents(rb, 4)
+ m.Refresh()
+
+ if m.paused {
+ t.Fatalf("expected model to start unpaused")
+ }
+ if !m.HandleKey("f") {
+ t.Fatalf("f should be handled")
+ }
+ if !m.paused {
+ t.Fatalf("expected model paused while filter modal is open")
+ }
+ if !m.filterModal.Visible() {
+ t.Fatalf("expected filter modal visible after f")
+ }
+ if !m.HandleKey("esc") {
+ t.Fatalf("esc should be routed to filter modal")
+ }
+ if m.filterModal.Visible() {
+ t.Fatalf("expected filter modal closed after esc")
+ }
+ if m.paused {
+ t.Fatalf("expected pause state restored to unpaused after modal close")
+ }
+
+ // If the user was already paused before opening the filter modal,
+ // that pause state should remain after closing.
+ if !m.HandleKey("space") {
+ t.Fatalf("space should toggle pause")
+ }
+ if !m.paused {
+ t.Fatalf("expected paused=true after space")
+ }
+ if !m.HandleKey("f") {
+ t.Fatalf("f should be handled while paused")
+ }
+ if !m.HandleKey("esc") {
+ t.Fatalf("esc should close modal")
+ }
+ if !m.paused {
+ t.Fatalf("expected paused state preserved after modal close")
+ }
+}