diff --git a/internal/tui/keybinds.go b/internal/tui/keybinds.go index a2f69c7..ed2b8d4 100644 --- a/internal/tui/keybinds.go +++ b/internal/tui/keybinds.go @@ -1,6 +1,7 @@ package tui import ( + "strings" "tusshi/internal/constants" "tusshi/internal/ssh" "tusshi/internal/tui/commands" @@ -113,13 +114,7 @@ func (m *Model) handleNormalKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { return m, m.ActiveComponent.Init() case utils.MatchesMultipleStringOptions(msg.String(), constants.KeyEnter): - if len(m.Filtered) > 0 { - selected := m.Filtered[m.SelectedIndex] - sshCmd := ssh.NewSSHCommand(selected.Alias) - return m, tea.ExecProcess(sshCmd, func(err error) tea.Msg { - return SSHFinishedMsg{Err: err} - }) - } + return m.connectSelectedHost() } return m, nil @@ -128,9 +123,32 @@ func (m *Model) handleNormalKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { // handleSearchKey processes keyboard input when performing a text search. func (m *Model) handleSearchKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { switch msg.String() { - case constants.KeyEsc, constants.KeyEnter: + case constants.KeyEsc: + m.Mode = ModeNormal + m.SearchInput.Blur() + return m, nil + + case constants.KeyEnter: m.Mode = ModeNormal m.SearchInput.Blur() + return m.connectSelectedHost() + + case "up": // TODO: maybe refactor keybinds constants? but this would likely never change... + if m.SelectedIndex > 0 { + m.SelectedIndex-- + } + return m, nil + + case "down": + if m.SelectedIndex < len(m.Filtered)-1 { + m.SelectedIndex++ + } + return m, nil + + // TODO: test on multiple terminal emulators + case "ctrl+backspace", "ctrl+h", "ctrl+w": // why: kitty and xterm-compatible terminals transmit ctrl+backspace as ctrl+h + deleteWordBackwards(&m.SearchInput) + m.FilterHosts() return m, nil } @@ -159,3 +177,36 @@ func (m *Model) handleCommandKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { m.CommandInput, cmdCmd = m.CommandInput.Update(msg) return m, cmdCmd } + +func (m *Model) connectSelectedHost() (tea.Model, tea.Cmd) { + if len(m.Filtered) == 0 { + return m, nil + } + selected := m.Filtered[m.SelectedIndex] + sshCmd := ssh.NewSSHCommand(selected.Alias) + return m, tea.ExecProcess(sshCmd, func(err error) tea.Msg { + return SSHFinishedMsg{Err: err} + }) + +} + +func deleteWordBackwards(ti *textinput.Model) { + val := ti.Value() + pos := ti.Position() + if pos == 0 || len(val) == 0 { + return + } + + left := strings.TrimRight(val[:pos], " ") + lastSpace := strings.LastIndex(left, " ") + + var newVal string + if lastSpace == -1 { + newVal = val[pos:] + ti.SetCursor(0) + } else { + newVal = left[:lastSpace+1] + val[pos:] + ti.SetCursor(lastSpace + 1) + } + ti.SetValue(newVal) +} diff --git a/internal/tui/model.go b/internal/tui/model.go index 8776076..3551d85 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -171,6 +171,7 @@ func (m *Model) Reload() { } // FilterHosts filters and fuzzy-matches the host list using active tab and search query. +// Handles index under & overflow for selectedIndex func (m *Model) FilterHosts() { var filtered []*config.Host searchQ := strings.ToLower(m.SearchInput.Value())