feat: Add pcap import, file browser, logging, local capture, and stable call ordering
This commit is contained in:
180
internal/tui/ssh_config.go
Normal file
180
internal/tui/ssh_config.go
Normal file
@@ -0,0 +1,180 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user