summaryrefslogtreecommitdiff
path: root/internal/ui
diff options
context:
space:
mode:
authorPaul Bütow <1224732+snonux@users.noreply.github.com>2025-06-20 22:43:13 +0300
committerPaul Bütow <1224732+snonux@users.noreply.github.com>2025-06-20 22:43:13 +0300
commit8b69b521f4d68f6f8a55561bce1f28271892a9be (patch)
tree88ee851b006e7b7ad7a34a16afc2117af30be18f /internal/ui
parent07e53d017555eafa2ae2174297cc61e9960bf870 (diff)
Add ESC hotkey for help and search
Diffstat (limited to 'internal/ui')
-rw-r--r--internal/ui/table.go14
-rw-r--r--internal/ui/table_test.go101
2 files changed, 113 insertions, 2 deletions
diff --git a/internal/ui/table.go b/internal/ui/table.go
index 7cde4c0..79a538e 100644
--- a/internal/ui/table.go
+++ b/internal/ui/table.go
@@ -272,12 +272,22 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "?":
m.showHelp = true
return m, nil
- case "q":
+ case "q", "esc":
if m.showHelp {
m.showHelp = false
return m, nil
}
- return m, tea.Quit
+ if m.searchRegex != nil {
+ m.searchRegex = nil
+ m.searchMatches = nil
+ m.searchIndex = 0
+ m.reload()
+ return m, nil
+ }
+ if msg.String() == "q" {
+ return m, tea.Quit
+ }
+ return m, nil
case "e", "E":
if row := m.tbl.SelectedRow(); row != nil {
idStr := ansi.Strip(row[0])
diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go
index 7b3dd72..d120337 100644
--- a/internal/ui/table_test.go
+++ b/internal/ui/table_test.go
@@ -431,3 +431,104 @@ func TestNavigationHotkeys(t *testing.T) {
t.Fatalf("G hotkey: expected 1 got %d", m.tbl.Cursor())
}
}
+
+func setupBasicTask(t *testing.T, tmp string) string {
+ taskPath := filepath.Join(tmp, "task")
+ script := "#!/bin/sh\n" +
+ "if echo \"$@\" | grep -q export; then\n" +
+ " echo '{\"id\":1,\"uuid\":\"x\",\"description\":\"alpha\",\"status\":\"pending\",\"entry\":\"\",\"priority\":\"\",\"urgency\":0}'\n" +
+ " echo '{\"id\":2,\"uuid\":\"y\",\"description\":\"beta\",\"status\":\"pending\",\"entry\":\"\",\"priority\":\"\",\"urgency\":0}'\n" +
+ " exit 0\n" +
+ "fi\n"
+ if err := os.WriteFile(taskPath, []byte(script), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ return taskPath
+}
+
+func setupEnv(t *testing.T, taskPath string) {
+ origPath := os.Getenv("PATH")
+ os.Setenv("PATH", filepath.Dir(taskPath)+":"+origPath)
+ t.Cleanup(func() { os.Setenv("PATH", origPath) })
+
+ tmp := filepath.Dir(taskPath)
+ os.Setenv("TASKDATA", tmp)
+ os.Setenv("TASKRC", "/dev/null")
+ t.Cleanup(func() {
+ os.Unsetenv("TASKDATA")
+ os.Unsetenv("TASKRC")
+ })
+}
+
+func TestEscClosesHelp(t *testing.T) {
+ tmp := t.TempDir()
+ taskPath := setupBasicTask(t, tmp)
+ setupEnv(t, taskPath)
+
+ m, err := New(nil)
+ if err != nil {
+ t.Fatalf("New: %v", err)
+ }
+
+ mv, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'?'}})
+ m = mv.(Model)
+ if !m.showHelp {
+ t.Fatalf("help not shown")
+ }
+
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyEsc})
+ m = mv.(Model)
+ if m.showHelp {
+ t.Fatalf("esc did not close help")
+ }
+}
+
+func TestSearchExitHotkeys(t *testing.T) {
+ tmp := t.TempDir()
+ taskPath := setupBasicTask(t, tmp)
+ setupEnv(t, taskPath)
+
+ m, err := New(nil)
+ if err != nil {
+ t.Fatalf("New: %v", err)
+ }
+
+ // enter search mode
+ mv, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'/'}})
+ m = mv.(Model)
+ for _, r := range "alpha" {
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{r}})
+ m = mv.(Model)
+ }
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
+ m = mv.(Model)
+ if m.searchRegex == nil {
+ t.Fatalf("search regex not set")
+ }
+
+ // escape search results with ESC
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyEsc})
+ m = mv.(Model)
+ if m.searchRegex != nil {
+ t.Fatalf("esc did not clear search")
+ }
+
+ // search again and exit with q
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'/'}})
+ m = mv.(Model)
+ for _, r := range "beta" {
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{r}})
+ m = mv.(Model)
+ }
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
+ m = mv.(Model)
+ if m.searchRegex == nil {
+ t.Fatalf("search regex not set for q")
+ }
+
+ mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'q'}})
+ m = mv.(Model)
+ if m.searchRegex != nil {
+ t.Fatalf("q did not clear search")
+ }
+}