package tui import ( "strings" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) // SSHConfigModel handles SSH connection input type SSHConfigModel struct { inputs []textinput.Model focusIndex int submitted bool cancelled bool // Parsed values Host string Port string User string Password string } const ( inputHost = iota inputPort inputUser inputPassword ) // NewSSHConfigModel creates a new SSH config input model func NewSSHConfigModel() SSHConfigModel { inputs := make([]textinput.Model, 4) // Host input inputs[inputHost] = textinput.New() inputs[inputHost].Placeholder = "192.168.1.100" inputs[inputHost].Focus() inputs[inputHost].CharLimit = 256 inputs[inputHost].Width = 30 inputs[inputHost].Prompt = "Host: " // Port input inputs[inputPort] = textinput.New() inputs[inputPort].Placeholder = "22" inputs[inputPort].CharLimit = 5 inputs[inputPort].Width = 10 inputs[inputPort].Prompt = "Port: " inputs[inputPort].SetValue("22") // User input inputs[inputUser] = textinput.New() inputs[inputUser].Placeholder = "root" inputs[inputUser].CharLimit = 64 inputs[inputUser].Width = 20 inputs[inputUser].Prompt = "User: " // Password input inputs[inputPassword] = textinput.New() inputs[inputPassword].Placeholder = "password" inputs[inputPassword].CharLimit = 128 inputs[inputPassword].Width = 30 inputs[inputPassword].Prompt = "Password: " inputs[inputPassword].EchoMode = textinput.EchoPassword inputs[inputPassword].EchoCharacter = '•' return SSHConfigModel{ inputs: inputs, focusIndex: 0, } } // Init initializes the model func (m SSHConfigModel) Init() tea.Cmd { return textinput.Blink } // Update handles messages func (m SSHConfigModel) Update(msg tea.Msg) (SSHConfigModel, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c", "esc": m.cancelled = true return m, nil case "tab", "down": m.focusIndex++ if m.focusIndex >= len(m.inputs) { m.focusIndex = 0 } return m, m.updateFocus() case "shift+tab", "up": m.focusIndex-- if m.focusIndex < 0 { m.focusIndex = len(m.inputs) - 1 } return m, m.updateFocus() case "enter": if m.focusIndex == len(m.inputs)-1 { // Submit on last field m.submitted = true m.Host = m.inputs[inputHost].Value() m.Port = m.inputs[inputPort].Value() m.User = m.inputs[inputUser].Value() m.Password = m.inputs[inputPassword].Value() return m, nil } // Move to next field m.focusIndex++ return m, m.updateFocus() } } // Update focused input cmd := m.updateInputs(msg) return m, cmd } func (m *SSHConfigModel) updateFocus() tea.Cmd { cmds := make([]tea.Cmd, len(m.inputs)) for i := range m.inputs { if i == m.focusIndex { cmds[i] = m.inputs[i].Focus() } else { m.inputs[i].Blur() } } return tea.Batch(cmds...) } func (m *SSHConfigModel) updateInputs(msg tea.Msg) tea.Cmd { cmds := make([]tea.Cmd, len(m.inputs)) for i := range m.inputs { m.inputs[i], cmds[i] = m.inputs[i].Update(msg) } return tea.Batch(cmds...) } // View renders the SSH config form func (m SSHConfigModel) View() string { var b strings.Builder titleStyle := lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#7D56F4")). MarginBottom(1) b.WriteString(titleStyle.Render("🔌 SSH Connection")) b.WriteString("\n\n") for i := range m.inputs { b.WriteString(m.inputs[i].View()) b.WriteString("\n") } helpStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")) b.WriteString("\n") b.WriteString(helpStyle.Render("Tab/↑↓ navigate • Enter submit • Esc cancel")) return b.String() } // IsSubmitted returns true if the form was submitted func (m SSHConfigModel) IsSubmitted() bool { return m.submitted } // IsCancelled returns true if the form was cancelled func (m SSHConfigModel) IsCancelled() bool { return m.cancelled } // GetConfig returns the SSH config values func (m SSHConfigModel) GetConfig() (host, port, user, password string) { return m.Host, m.Port, m.User, m.Password }