(msg tea.KeyMsg)
| 17 | const queueSlashHint = "a slash command can't join a queued prompt - send it when the turn ends" |
| 18 | |
| 19 | func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { |
| 20 | // Any key that isn't Ctrl+C clears a pending quit arm: no stray quits. |
| 21 | if msg.Type != tea.KeyCtrlC && !m.quitArmedAt.IsZero() { |
| 22 | m.quitArmedAt = time.Time{} |
| 23 | if m.status == quitArmText { |
| 24 | m.status = "" |
| 25 | } |
| 26 | } |
| 27 | switch msg.Type { |
| 28 | case tea.KeyCtrlC: |
| 29 | return m.handleCtrlC() |
| 30 | case tea.KeyCtrlL: |
| 31 | // Clear the typed prompt and force a full redraw. Scrollback and |
| 32 | // history stay; Ctrl+L tidies input, /clear starts over. Close the |
| 33 | // popover with the text it filtered on, like every other clearing |
| 34 | // path; left open, its stale selection would hijack the next Enter. |
| 35 | m.ta.Reset() |
| 36 | m.closePopover() |
| 37 | return m, tea.ClearScreen |
| 38 | case tea.KeyBackspace: |
| 39 | // Mid-turn Backspace on an empty prompt pulls a queued prompt back into |
| 40 | // the textarea for editing; any other Backspace is an ordinary character |
| 41 | // delete and falls through to the textarea. |
| 42 | if m.phase.active() && m.queued != nil && m.ta.Value() == "" { |
| 43 | return m.unqueuePrompt() |
| 44 | } |
| 45 | case tea.KeyCtrlD: |
| 46 | // Ctrl+D on empty input = EOF = quit; no-op on non-empty so a |
| 47 | // reflexive press never destroys a draft, and no-op mid-turn (the |
| 48 | // textarea is empty then, since submit resets it) so a reflexive press |
| 49 | // can't quit without cancelling turnCtx and orphan a running tool's |
| 50 | // process group. Ctrl+C is the mid-turn escape. |
| 51 | if m.ta.Value() == "" && !m.phase.active() { |
| 52 | return m, tea.Quit |
| 53 | } |
| 54 | return m, nil |
| 55 | case tea.KeyUp: |
| 56 | if m.popoverOpen() { |
| 57 | return m.popoverMoveSelection(-1) |
| 58 | } |
| 59 | // ↑ is prompt-only: cursor up if a row is above, else walk |
| 60 | // history. The terminal owns scrollback (PgUp / wheel native). |
| 61 | if !m.cursorOnFirstLine() { |
| 62 | break |
| 63 | } |
| 64 | return m.historyUp(), nil |
| 65 | case tea.KeyDown: |
| 66 | if m.popoverOpen() { |
| 67 | return m.popoverMoveSelection(1) |
| 68 | } |
| 69 | if !m.cursorOnLastLine() { |
| 70 | break |
| 71 | } |
| 72 | return m.historyDown(), nil |
| 73 | case tea.KeyTab: |
| 74 | return m.handleTab(msg) |
| 75 | case tea.KeyShiftTab: |
| 76 | if !m.popoverOpen() { |
no test coverage detected