From f2b75267a6085f28a415bc8464cf2a70b650e65f Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 7 Apr 2026 09:07:05 +0300 Subject: ui: fix priority black spot and q/esc search-clear in ultra mode Priority field: the no-priority dash (-) was rendered with terminal- default (black) background because the priority badge path unconditionally passed bg="" to suppress the badge colour. Now only H/M/L pass bg="" to preserve their coloured pill; the dash case inherits the card bg. q/esc with active search: first press clears the ultra search filter (regex + filtered index + cursor reset) and stays in ultra mode, same as esc behaves in normal table mode. A second press with no active search exits ultra / quits the app as before. Tests updated accordingly. Co-Authored-By: Claude Sonnet 4.6 --- internal/ui/keyhandlers.go | 11 +++++++++-- internal/ui/table_test.go | 44 ++++++++++++++++++++++++++++++-------------- internal/ui/ultra.go | 7 ++++++- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/internal/ui/keyhandlers.go b/internal/ui/keyhandlers.go index d68a7f5..ffd52b1 100644 --- a/internal/ui/keyhandlers.go +++ b/internal/ui/keyhandlers.go @@ -157,6 +157,15 @@ func (m *Model) handleQuitOrEscape() (tea.Model, tea.Cmd) { return m, nil } if m.showUltra { + // Active search: q/esc clears the search filter first, same as in + // normal table mode. Only proceed to exit/quit when no search is active. + if m.ultraSearchRegex != nil { + m.ultraSearchRegex = nil + m.ultraFiltered = nil + m.ultraCursor = 0 + m.ultraOffset = 0 + return m, nil + } // When started via --ultra flag there is no table view to return to, // so q/esc exits the application directly. if m.ultraStartup { @@ -164,8 +173,6 @@ func (m *Model) handleQuitOrEscape() (tea.Model, tea.Cmd) { } m.ultraClearFocusedID() m.showUltra = false - m.ultraSearchRegex = nil - m.ultraFiltered = nil m.ultraSearchInput.SetValue("") return m, nil } diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index 0acbfab..a065ca7 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -898,22 +898,30 @@ func TestUltraExitHotkeysClearUltraState(t *testing.T) { m.ultraSearchInput.SetValue("ultra needle") m.ultraFocusedID = 17 + // First q clears the active search but stays in ultra mode. mv, cmd = (&m).Update(tea.KeyPressMsg{Code: 'q', Text: "q"}) if cmd != nil { - t.Fatalf("q in ultra mode unexpectedly returned a command") + t.Fatalf("q with active search unexpectedly returned a command") } m = *mv.(*Model) - if m.showUltra { - t.Fatalf("q did not exit ultra mode") + if !m.showUltra { + t.Fatalf("q with active search should stay in ultra mode") } if m.ultraSearchRegex != nil { - t.Fatalf("q did not clear ultraSearchRegex") + t.Fatalf("first q did not clear ultraSearchRegex") } if m.ultraFiltered != nil { - t.Fatalf("q did not clear ultraFiltered") + t.Fatalf("first q did not clear ultraFiltered") } - if got := m.ultraSearchInput.Value(); got != "" { - t.Fatalf("q did not clear ultraSearchInput, got %q", got) + + // Second q (no active search) exits ultra mode. + mv, cmd = (&m).Update(tea.KeyPressMsg{Code: 'q', Text: "q"}) + if cmd != nil { + t.Fatalf("q in ultra mode (no search) unexpectedly returned a command") + } + m = *mv.(*Model) + if m.showUltra { + t.Fatalf("second q did not exit ultra mode") } if m.ultraFocusedID != 0 { t.Fatalf("q did not clear ultraFocusedID, got %d", m.ultraFocusedID) @@ -929,22 +937,30 @@ func TestUltraExitHotkeysClearUltraState(t *testing.T) { m.ultraFiltered = []int{2} m.ultraSearchInput.SetValue("second needle") + // First esc clears the active search, stays in ultra. mv, cmd = (&m).Update(tea.KeyPressMsg{Code: tea.KeyEsc}) if cmd != nil { - t.Fatalf("esc in ultra mode unexpectedly returned a command") + t.Fatalf("esc with active search unexpectedly returned a command") } m = *mv.(*Model) - if m.showUltra { - t.Fatalf("esc did not exit ultra mode") + if !m.showUltra { + t.Fatalf("esc with active search should stay in ultra mode") } if m.ultraSearchRegex != nil { - t.Fatalf("esc did not clear ultraSearchRegex") + t.Fatalf("first esc did not clear ultraSearchRegex") } if m.ultraFiltered != nil { - t.Fatalf("esc did not clear ultraFiltered") + t.Fatalf("first esc did not clear ultraFiltered") + } + + // Second esc (no active search) exits ultra mode. + mv, cmd = (&m).Update(tea.KeyPressMsg{Code: tea.KeyEsc}) + if cmd != nil { + t.Fatalf("esc in ultra mode (no search) unexpectedly returned a command") } - if got := m.ultraSearchInput.Value(); got != "" { - t.Fatalf("esc did not clear ultraSearchInput, got %q", got) + m = *mv.(*Model) + if m.showUltra { + t.Fatalf("second esc did not exit ultra mode") } if m.ultraFocusedID != 0 { t.Fatalf("esc did not clear ultraFocusedID, got %d", m.ultraFocusedID) diff --git a/internal/ui/ultra.go b/internal/ui/ultra.go index 9fccc0c..e2ad3e9 100644 --- a/internal/ui/ultra.go +++ b/internal/ui/ultra.go @@ -654,7 +654,12 @@ func (m *Model) renderUltraHeaderWithRegex(t task.Task, width int, re *regexp.Re sep := ultraFieldSep(bg) id := m.ultraStyledText(re, lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("253")), idText, bg) - priority := m.ultraStyledText(re, ultraPriorityStyle(m.theme, t.Priority), priorityText, "") // priority badge keeps its own bg + // H/M/L badges keep their own coloured background; plain "-" uses the card bg. + priorityBG := bg + if t.Priority == "H" || t.Priority == "M" || t.Priority == "L" { + priorityBG = "" + } + priority := m.ultraStyledText(re, ultraPriorityStyle(m.theme, t.Priority), priorityText, priorityBG) status := m.ultraStyledText(re, lipgloss.NewStyle().Foreground(lipgloss.Color("246")), statusText, bg) urgency := m.ultraStyledText(re, lipgloss.NewStyle().Foreground(lipgloss.Color("214")), urgencyText, bg) age := m.ultraStyledText(re, lipgloss.NewStyle().Foreground(lipgloss.Color("240")), ageText, bg) -- cgit v1.2.3