diff --git a/internal/tui/commands_test.go b/internal/tui/commands_test.go index 385bd24..c03b381 100644 --- a/internal/tui/commands_test.go +++ b/internal/tui/commands_test.go @@ -3,6 +3,8 @@ package tui import ( "strings" "testing" + + tea "github.com/charmbracelet/bubbletea" ) func TestCommandPrefix(t *testing.T) { @@ -128,6 +130,36 @@ func TestCommandCompletion(t *testing.T) { } } +// While the command popup is open, plain keys must keep flowing into the +// input (narrowing the filter) instead of being swallowed. +func TestCommandPopupKeepsTyping(t *testing.T) { + m := wired(t) + m.Update(key("/")) // opens the command popup + if !m.ac.open || m.ac.mode != acCmd { + t.Fatalf("command popup not open: %+v", m.ac) + } + m.Update(key("h")) // must reach the input, not be swallowed + if got := m.ta.Value(); got != "/h" { + t.Fatalf("input after typing = %q, want %q", got, "/h") + } + if !m.ac.open || m.ac.mode != acCmd { + t.Fatal("popup should stay open while the name is typed") + } + if len(m.ac.items) != 1 || m.ac.items[0].ID != "/help" { + t.Errorf("popup should re-filter to /help: %+v", m.ac.items) + } + // Backspace edits the input too. + m.Update(tea.KeyMsg{Type: tea.KeyBackspace}) + if got := m.ta.Value(); got != "/" { + t.Errorf("input after backspace = %q, want %q", got, "/") + } + // ctrl+c still quits while the popup has capture. + m.Update(key("ctrl+c")) + if !m.quitting { + t.Error("ctrl+c should quit while the popup is open") + } +} + func TestCommandPopupEnterExecutes(t *testing.T) { m := wired(t) m.msgs = append(m.msgs, message{role: roleUser, content: "x"}) diff --git a/internal/tui/input.go b/internal/tui/input.go index b50ffec..6988c71 100644 --- a/internal/tui/input.go +++ b/internal/tui/input.go @@ -39,8 +39,16 @@ func (m *Model) handleACKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { case "esc": m.closeAC() return m, nil - } - return m, nil + case "ctrl+c": + m.quitting = true + return m, tea.Quit + } + // Any other key is plain input: forward it to the textarea, then + // re-evaluate the popup against the new value — typing narrows the + // filter, backspace widens it, a space ends command completion. + var cmd tea.Cmd + m.ta, cmd = m.ta.Update(msg) + return m, tea.Batch(cmd, m.syncAC()) } // acMode selects what the completion popup is completing.