From fa56f47f960b3afc07c4dfcc8ddbd810b5c8b32b Mon Sep 17 00:00:00 2001 From: Tyler Koenig Date: Thu, 11 Jun 2026 19:10:22 -0400 Subject: [PATCH] fix(tui): track selection by site ID + q means back everywhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cursor tracked by site ID instead of positional index. When the list re-sorts every tick (sites change status), the selection stays on the same monitor instead of silently jumping to whatever now occupies that index position. q now means "back" in detail, history, SLA, and alert-detail views — consistent with muscle memory from navigating deeper views. Only the dashboard q quits the app. ctrl+c always quits from anywhere. --- internal/tui/data.go | 15 +++++++++++++++ internal/tui/tab_alerts.go | 2 +- internal/tui/tui.go | 1 + internal/tui/update.go | 9 +++++---- internal/tui/view_detail.go | 2 +- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/internal/tui/data.go b/internal/tui/data.go index 857fb07..23e48d5 100644 --- a/internal/tui/data.go +++ b/internal/tui/data.go @@ -105,9 +105,24 @@ func (m *Model) refreshLive() { } m.sites = ordered m.logViewport.SetContent(strings.Join(m.engine.GetLogs(), "\n")) + + if m.currentTab == 0 && m.selectedID != 0 { + for i, s := range m.sites { + if s.ID == m.selectedID { + m.cursor = i + break + } + } + } m.clampCursor() } +func (m *Model) syncSelectedID() { + if m.currentTab == 0 && m.cursor < len(m.sites) { + m.selectedID = m.sites[m.cursor].ID + } +} + // clampCursor keeps the cursor and scroll offset within the current tab's list. func (m *Model) clampCursor() { listLen := m.currentListLen() diff --git a/internal/tui/tab_alerts.go b/internal/tui/tab_alerts.go index 3d96c63..2edc900 100644 --- a/internal/tui/tab_alerts.go +++ b/internal/tui/tab_alerts.go @@ -271,7 +271,7 @@ func (m Model) viewAlertDetailPanel() string { } b.WriteString(m.divider() + "\n") - b.WriteString(m.st.subtleStyle.Render(" [i/Esc] Back [e] Edit [t] Test [q] Quit")) + b.WriteString(m.st.subtleStyle.Render(" [q/Esc] Back [e] Edit [t] Test")) return lipgloss.NewStyle().Padding(1, 2).Render(b.String()) } diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 7fab229..225c38d 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -103,6 +103,7 @@ type Model struct { state sessionState currentTab int cursor int + selectedID int tableOffset int maxTableRows int termWidth int diff --git a/internal/tui/update.go b/internal/tui/update.go index 12c0ea2..d808fed 100644 --- a/internal/tui/update.go +++ b/internal/tui/update.go @@ -286,6 +286,7 @@ func (m *Model) handleMouse(msg tea.MouseMsg) (tea.Model, tea.Cmd) { } } } + m.syncSelectedID() return m, nil } @@ -379,7 +380,7 @@ func (m *Model) handleDetailKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { return m, m.openSLAView(m.sites[m.cursor]) } case "q": - return m, tea.Quit + m.state = stateDashboard } return m, nil } @@ -499,10 +500,8 @@ func (m *Model) handleHistoryKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { func (m *Model) handleAlertDetailKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { switch msg.String() { - case "i", "esc": + case "q", "i", "esc": m.state = stateDashboard - case "q": - return m, tea.Quit } return m, nil } @@ -537,6 +536,7 @@ func (m *Model) handleDashboardKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { if m.cursor < m.tableOffset { m.tableOffset = m.cursor } + m.syncSelectedID() } case "down", "j": if m.state == stateLogs { @@ -548,6 +548,7 @@ func (m *Model) handleDashboardKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { if m.cursor >= m.tableOffset+m.maxTableRows { m.tableOffset++ } + m.syncSelectedID() } } case "n": diff --git a/internal/tui/view_detail.go b/internal/tui/view_detail.go index 081fd40..1274a91 100644 --- a/internal/tui/view_detail.go +++ b/internal/tui/view_detail.go @@ -254,7 +254,7 @@ func (m Model) viewDetailPanel() string { b.WriteString("\n") b.WriteString(m.divider() + "\n") - b.WriteString(m.st.subtleStyle.Render(" [i/Esc] Back [e] Edit [h] History [s] SLA [click] Inspect [q] Quit")) + b.WriteString(m.st.subtleStyle.Render(" [q/Esc] Back [e] Edit [h] History [s] SLA [click] Inspect")) return lipgloss.NewStyle().Padding(1, 2).Render(b.String()) }