diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-25 08:55:58 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-25 08:55:58 +0200 |
| commit | 22f1589e62aeafed805b8dd07d4610b7662f205e (patch) | |
| tree | 1661bd680ccd0d969359c3f3cf3cbd3d8ec4a5a3 /internal/tui/eventstream | |
| parent | d423225771a10ebae87d22c69fe88e5b65a3d378 (diff) | |
Polish stream filter UX and expand TUI viewport
Diffstat (limited to 'internal/tui/eventstream')
| -rw-r--r-- | internal/tui/eventstream/model.go | 24 | ||||
| -rw-r--r-- | internal/tui/eventstream/model_test.go | 48 |
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") + } +} |
