1 Commits

Author SHA1 Message Date
bd66da130e feat: focus search bar on startup by default
The search input is now focused when the TUI opens, so filtering works
immediately without an extra keystroke. This is especially helpful with
many hosts.

Removes the now-redundant -s/--search flag and updates the docs.
2026-07-09 12:24:10 +02:00
3 changed files with 9 additions and 14 deletions

View File

@@ -108,7 +108,7 @@ sshm
- `f` - Port forwarding setup - `f` - Port forwarding setup
- `H` - Toggle hidden hosts visibility - `H` - Toggle hidden hosts visibility
- `q` - Quit - `q` - Quit
- `/` - Search/filter hosts - `/` - Focus the search bar (already focused at startup; press `Enter` to jump back to the host list)
**Real-time Status Indicators:** **Real-time Status Indicators:**
- 🟢 **Online** - Host is reachable via SSH - 🟢 **Online** - Host is reachable via SSH

View File

@@ -24,9 +24,6 @@ var configFile string
// forceTTY forces pseudo-TTY allocation for remote commands // forceTTY forces pseudo-TTY allocation for remote commands
var forceTTY bool var forceTTY bool
// searchMode enables the focus on search mode at startup
var searchMode bool
// noUpdateCheck disables the async update check in the TUI // noUpdateCheck disables the async update check in the TUI
var noUpdateCheck bool var noUpdateCheck bool
@@ -145,7 +142,7 @@ func runInteractiveMode() {
} }
// Run the interactive TUI // Run the interactive TUI
if err := ui.RunInteractiveMode(hosts, configFile, searchMode, AppVersion, noUpdateCheck); err != nil { if err := ui.RunInteractiveMode(hosts, configFile, AppVersion, noUpdateCheck); err != nil {
log.Fatalf("Error running interactive mode: %v", err) log.Fatalf("Error running interactive mode: %v", err)
} }
} }
@@ -244,7 +241,6 @@ func Execute() {
func init() { func init() {
RootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "SSH config file to use (default: ~/.ssh/config)") RootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "SSH config file to use (default: ~/.ssh/config)")
RootCmd.Flags().BoolVarP(&forceTTY, "tty", "t", false, "Force pseudo-TTY allocation (useful for interactive remote commands)") RootCmd.Flags().BoolVarP(&forceTTY, "tty", "t", false, "Force pseudo-TTY allocation (useful for interactive remote commands)")
RootCmd.PersistentFlags().BoolVarP(&searchMode, "search", "s", false, "Focus on search input at startup")
RootCmd.PersistentFlags().BoolVar(&noUpdateCheck, "no-update-check", false, "Disable automatic update check") RootCmd.PersistentFlags().BoolVar(&noUpdateCheck, "no-update-check", false, "Disable automatic update check")
RootCmd.SetVersionTemplate("{{.Name}} version {{.Version}}\n") RootCmd.SetVersionTemplate("{{.Name}} version {{.Version}}\n")

View File

@@ -15,8 +15,9 @@ import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
) )
// NewModel creates a new TUI model with the given SSH hosts // NewModel creates a new TUI model with the given SSH hosts.
func NewModel(hosts []config.SSHHost, configFile string, searchMode bool, currentVersion string, noUpdateCheck bool) Model { // The search input is focused at startup so users can filter hosts right away.
func NewModel(hosts []config.SSHHost, configFile string, currentVersion string, noUpdateCheck bool) Model {
// Load application configuration // Load application configuration
appConfig, err := config.LoadAppConfig() appConfig, err := config.LoadAppConfig()
if err != nil { if err != nil {
@@ -60,7 +61,7 @@ func NewModel(hosts []config.SSHHost, configFile string, searchMode bool, curren
height: 24, height: 24,
ready: false, ready: false,
viewMode: ViewList, viewMode: ViewList,
searchMode: searchMode, searchMode: true,
} }
// Apply visibility filter (showHidden is false by default) // Apply visibility filter (showHidden is false by default)
@@ -75,9 +76,7 @@ func NewModel(hosts []config.SSHHost, configFile string, searchMode bool, curren
ti.Placeholder = "Search hosts or tags..." ti.Placeholder = "Search hosts or tags..."
ti.CharLimit = 50 ti.CharLimit = 50
ti.Width = 25 ti.Width = 25
if searchMode {
ti.Focus() ti.Focus()
}
// Use dynamic column width calculation (will fallback to static if width not available) // Use dynamic column width calculation (will fallback to static if width not available)
nameWidth, hostnameWidth, tagsWidth, lastLoginWidth := m.calculateDynamicColumnWidths(sortedHosts) nameWidth, hostnameWidth, tagsWidth, lastLoginWidth := m.calculateDynamicColumnWidths(sortedHosts)
@@ -161,8 +160,8 @@ func NewModel(hosts []config.SSHHost, configFile string, searchMode bool, curren
} }
// RunInteractiveMode starts the interactive TUI interface // RunInteractiveMode starts the interactive TUI interface
func RunInteractiveMode(hosts []config.SSHHost, configFile string, searchMode bool, currentVersion string, noUpdateCheck bool) error { func RunInteractiveMode(hosts []config.SSHHost, configFile string, currentVersion string, noUpdateCheck bool) error {
m := NewModel(hosts, configFile, searchMode, currentVersion, noUpdateCheck) m := NewModel(hosts, configFile, currentVersion, noUpdateCheck)
// Start the application in alt screen mode for clean output // Start the application in alt screen mode for clean output
p := tea.NewProgram(m, tea.WithAltScreen()) p := tea.NewProgram(m, tea.WithAltScreen())