From f82d40e06b5346218819fb22fea34f598dd4157d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 28 Mar 2026 09:14:22 +0200 Subject: ui: add ultra mode entry and exit hotkeys (vl vm) --- internal/ui/keyhandlers.go | 12 ++++++++ internal/ui/table_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++ internal/ui/ultra.go | 4 +++ 3 files changed, 86 insertions(+) diff --git a/internal/ui/keyhandlers.go b/internal/ui/keyhandlers.go index 528a8c8..b645a9c 100644 --- a/internal/ui/keyhandlers.go +++ b/internal/ui/keyhandlers.go @@ -110,6 +110,11 @@ func (m *Model) handleNormalMode(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { return m.handleShowTaskDetail() case "i": return m.handleEnterOrEdit() + case "u": + m.showUltra = true + m.ultraCursor = m.tbl.Cursor() + m.ultraOffset = 0 + return m, nil case "1": return m.handleJumpToRandomTask() case "2": @@ -139,6 +144,13 @@ func (m *Model) handleToggleHelp() (tea.Model, tea.Cmd) { } func (m *Model) handleQuitOrEscape() (tea.Model, tea.Cmd) { + if m.showUltra { + m.showUltra = false + m.ultraSearchRegex = nil + m.ultraFiltered = nil + m.ultraSearchInput.SetValue("") + return m, nil + } if m.showTaskDetail { m.showTaskDetail = false m.currentTaskDetail = nil diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index 25eea72..ec2b9bc 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "strings" "testing" "time" @@ -659,6 +660,75 @@ func TestEscClosesHelp(t *testing.T) { } } +func TestUltraExitHotkeysClearUltraState(t *testing.T) { + tmp := t.TempDir() + taskPath := setupBasicTask(t, tmp) + setupEnv(t, taskPath) + + m, err := New(nil, "firefox") + if err != nil { + t.Fatalf("New: %v", err) + } + + mv, cmd := (&m).Update(tea.KeyPressMsg{Code: 'u', Text: "u"}) + if cmd != nil { + t.Fatalf("u unexpectedly returned a command") + } + m = *mv.(*Model) + if !m.showUltra { + t.Fatalf("u did not enter ultra mode") + } + + m.ultraSearchRegex = regexp.MustCompile("alpha") + m.ultraFiltered = []int{0, 1} + m.ultraSearchInput.SetValue("ultra needle") + + mv, cmd = (&m).Update(tea.KeyPressMsg{Code: 'q', Text: "q"}) + if cmd != nil { + t.Fatalf("q in ultra mode unexpectedly returned a command") + } + m = *mv.(*Model) + if m.showUltra { + t.Fatalf("q did not exit ultra mode") + } + if m.ultraSearchRegex != nil { + t.Fatalf("q did not clear ultraSearchRegex") + } + if m.ultraFiltered != nil { + t.Fatalf("q did not clear ultraFiltered") + } + if got := m.ultraSearchInput.Value(); got != "" { + t.Fatalf("q did not clear ultraSearchInput, got %q", got) + } + + mv, cmd = (&m).Update(tea.KeyPressMsg{Code: 'u', Text: "u"}) + if cmd != nil { + t.Fatalf("u unexpectedly returned a command on re-entry") + } + m = *mv.(*Model) + m.ultraSearchRegex = regexp.MustCompile("beta") + m.ultraFiltered = []int{2} + m.ultraSearchInput.SetValue("second needle") + + mv, cmd = (&m).Update(tea.KeyPressMsg{Code: tea.KeyEsc}) + if cmd != nil { + t.Fatalf("esc in ultra mode unexpectedly returned a command") + } + m = *mv.(*Model) + if m.showUltra { + t.Fatalf("esc did not exit ultra mode") + } + if m.ultraSearchRegex != nil { + t.Fatalf("esc did not clear ultraSearchRegex") + } + if m.ultraFiltered != nil { + t.Fatalf("esc did not clear ultraFiltered") + } + if got := m.ultraSearchInput.Value(); got != "" { + t.Fatalf("esc did not clear ultraSearchInput, got %q", got) + } +} + // TestExpandedCellViewNoDoubleRender is a regression test for a bug where // expandedCellView() was appended to the layout unconditionally AND again // inside the cellExpanded guard, producing a duplicate line when expanded. diff --git a/internal/ui/ultra.go b/internal/ui/ultra.go index 551ece8..f4b7cf4 100644 --- a/internal/ui/ultra.go +++ b/internal/ui/ultra.go @@ -10,5 +10,9 @@ func (m *Model) renderUltraModus() string { // handleUltraMode handles keyboard input in ultra mode. func (m *Model) handleUltraMode(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { + switch msg.String() { + case "q", "esc": + return m.handleQuitOrEscape() + } return m, nil } -- cgit v1.2.3