mirror of
https://github.com/Gu1llaum-3/sshm.git
synced 2025-10-21 18:37:23 +02:00
Compare commits
No commits in common. "049998c2355108ebaf2dce8e0a56e8e82e86432a" and "3d746ec49ae01c9c94a8df9f5ab3417ac2a0e10a" have entirely different histories.
049998c235
...
3d746ec49a
30
README.md
30
README.md
@ -553,34 +553,6 @@ This will be automatically converted to:
|
|||||||
StrictHostKeyChecking no
|
StrictHostKeyChecking no
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Key Bindings
|
|
||||||
|
|
||||||
SSHM supports customizable key bindings through a configuration file. This is particularly useful for users who want to modify the default quit behavior.
|
|
||||||
|
|
||||||
**Configuration File Location:**
|
|
||||||
- **Linux/macOS**: `~/.config/sshm/config.json`
|
|
||||||
- **Windows**: `%APPDATA%\sshm\config.json`
|
|
||||||
|
|
||||||
**Example Configuration:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"key_bindings": {
|
|
||||||
"quit_keys": ["q", "ctrl+c"],
|
|
||||||
"disable_esc_quit": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Available Options:**
|
|
||||||
- **quit_keys**: Array of keys that will quit the application. Default: `["q", "ctrl+c"]`
|
|
||||||
- **disable_esc_quit**: Boolean flag to disable ESC key from quitting the application. Default: `false`
|
|
||||||
|
|
||||||
**For Vim Users:**
|
|
||||||
If you frequently press ESC accidentally causing the application to quit, set `disable_esc_quit` to `true`. This will disable ESC as a quit key while preserving all other functionality.
|
|
||||||
|
|
||||||
**Default Configuration:**
|
|
||||||
If no configuration file exists, SSHM will automatically create one with default settings that maintain backward compatibility.
|
|
||||||
|
|
||||||
## 🛠️ Development
|
## 🛠️ Development
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
@ -697,8 +669,6 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|||||||
- [Charm](https://charm.sh/) for the amazing TUI libraries
|
- [Charm](https://charm.sh/) for the amazing TUI libraries
|
||||||
- [Cobra](https://cobra.dev/) for the excellent CLI framework
|
- [Cobra](https://cobra.dev/) for the excellent CLI framework
|
||||||
- [@yimeng](https://github.com/yimeng) for contributing SSH Include directive support
|
- [@yimeng](https://github.com/yimeng) for contributing SSH Include directive support
|
||||||
- [@ldreux](https://github.com/ldreux) for contributing multi-word search functionality
|
|
||||||
- [@qingfengzxr](https://github.com/qingfengzxr) for contributing custom key bindings support
|
|
||||||
- The Go community for building such fantastic tools
|
- The Go community for building such fantastic tools
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
// KeyBindings represents configurable key bindings for the application
|
|
||||||
type KeyBindings struct {
|
|
||||||
// Quit keys - keys that will quit the application
|
|
||||||
QuitKeys []string `json:"quit_keys"`
|
|
||||||
|
|
||||||
// DisableEscQuit - if true, ESC key won't quit the application (useful for vim users)
|
|
||||||
DisableEscQuit bool `json:"disable_esc_quit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppConfig represents the main application configuration
|
|
||||||
type AppConfig struct {
|
|
||||||
KeyBindings KeyBindings `json:"key_bindings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDefaultKeyBindings returns the default key bindings configuration
|
|
||||||
func GetDefaultKeyBindings() KeyBindings {
|
|
||||||
return KeyBindings{
|
|
||||||
QuitKeys: []string{"q", "ctrl+c"}, // Default keeps current behavior minus ESC
|
|
||||||
DisableEscQuit: false, // Default to false for backward compatibility
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDefaultAppConfig returns the default application configuration
|
|
||||||
func GetDefaultAppConfig() AppConfig {
|
|
||||||
return AppConfig{
|
|
||||||
KeyBindings: GetDefaultKeyBindings(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAppConfigPath returns the path to the application config file
|
|
||||||
func GetAppConfigPath() (string, error) {
|
|
||||||
configDir, err := GetSSHMConfigDir()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(configDir, "config.json"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadAppConfig loads the application configuration from file
|
|
||||||
// If the file doesn't exist, it returns the default configuration
|
|
||||||
func LoadAppConfig() (*AppConfig, error) {
|
|
||||||
configPath, err := GetAppConfigPath()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If config file doesn't exist, return default config and create the file
|
|
||||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
||||||
defaultConfig := GetDefaultAppConfig()
|
|
||||||
|
|
||||||
// Create config directory if it doesn't exist
|
|
||||||
configDir := filepath.Dir(configPath)
|
|
||||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save default config to file
|
|
||||||
if err := SaveAppConfig(&defaultConfig); err != nil {
|
|
||||||
// If we can't save, just return the default config without erroring
|
|
||||||
// This allows the app to work even if config file can't be created
|
|
||||||
return &defaultConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &defaultConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read existing config file
|
|
||||||
data, err := os.ReadFile(configPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var config AppConfig
|
|
||||||
if err := json.Unmarshal(data, &config); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate and fill in missing fields with defaults
|
|
||||||
config = mergeWithDefaults(config)
|
|
||||||
|
|
||||||
return &config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveAppConfig saves the application configuration to file
|
|
||||||
func SaveAppConfig(config *AppConfig) error {
|
|
||||||
if config == nil {
|
|
||||||
return errors.New("config cannot be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath, err := GetAppConfigPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create config directory if it doesn't exist
|
|
||||||
configDir := filepath.Dir(configPath)
|
|
||||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.MarshalIndent(config, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.WriteFile(configPath, data, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mergeWithDefaults ensures all required fields are set with defaults if missing
|
|
||||||
func mergeWithDefaults(config AppConfig) AppConfig {
|
|
||||||
defaults := GetDefaultAppConfig()
|
|
||||||
|
|
||||||
// If QuitKeys is empty, use defaults
|
|
||||||
if len(config.KeyBindings.QuitKeys) == 0 {
|
|
||||||
config.KeyBindings.QuitKeys = defaults.KeyBindings.QuitKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldQuitOnKey checks if the given key should trigger quit based on configuration
|
|
||||||
func (kb *KeyBindings) ShouldQuitOnKey(key string) bool {
|
|
||||||
// Special handling for ESC key
|
|
||||||
if key == "esc" {
|
|
||||||
return !kb.DisableEscQuit
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if key is in the quit keys list
|
|
||||||
for _, quitKey := range kb.QuitKeys {
|
|
||||||
if quitKey == key {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,181 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDefaultKeyBindings(t *testing.T) {
|
|
||||||
kb := GetDefaultKeyBindings()
|
|
||||||
|
|
||||||
// Test default configuration
|
|
||||||
if kb.DisableEscQuit {
|
|
||||||
t.Error("Default configuration should allow ESC to quit (backward compatibility)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test default quit keys
|
|
||||||
expectedQuitKeys := []string{"q", "ctrl+c"}
|
|
||||||
if len(kb.QuitKeys) != len(expectedQuitKeys) {
|
|
||||||
t.Errorf("Expected %d quit keys, got %d", len(expectedQuitKeys), len(kb.QuitKeys))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, expected := range expectedQuitKeys {
|
|
||||||
if i >= len(kb.QuitKeys) || kb.QuitKeys[i] != expected {
|
|
||||||
t.Errorf("Expected quit key %s, got %s", expected, kb.QuitKeys[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShouldQuitOnKey(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
keyBindings KeyBindings
|
|
||||||
key string
|
|
||||||
expectedResult bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Default config - ESC should quit",
|
|
||||||
keyBindings: KeyBindings{
|
|
||||||
QuitKeys: []string{"q", "ctrl+c"},
|
|
||||||
DisableEscQuit: false,
|
|
||||||
},
|
|
||||||
key: "esc",
|
|
||||||
expectedResult: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Disabled ESC quit - ESC should not quit",
|
|
||||||
keyBindings: KeyBindings{
|
|
||||||
QuitKeys: []string{"q", "ctrl+c"},
|
|
||||||
DisableEscQuit: true,
|
|
||||||
},
|
|
||||||
key: "esc",
|
|
||||||
expectedResult: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Q key should quit",
|
|
||||||
keyBindings: KeyBindings{
|
|
||||||
QuitKeys: []string{"q", "ctrl+c"},
|
|
||||||
DisableEscQuit: true,
|
|
||||||
},
|
|
||||||
key: "q",
|
|
||||||
expectedResult: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Ctrl+C should quit",
|
|
||||||
keyBindings: KeyBindings{
|
|
||||||
QuitKeys: []string{"q", "ctrl+c"},
|
|
||||||
DisableEscQuit: true,
|
|
||||||
},
|
|
||||||
key: "ctrl+c",
|
|
||||||
expectedResult: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Other keys should not quit",
|
|
||||||
keyBindings: KeyBindings{
|
|
||||||
QuitKeys: []string{"q", "ctrl+c"},
|
|
||||||
DisableEscQuit: true,
|
|
||||||
},
|
|
||||||
key: "enter",
|
|
||||||
expectedResult: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result := tt.keyBindings.ShouldQuitOnKey(tt.key)
|
|
||||||
if result != tt.expectedResult {
|
|
||||||
t.Errorf("ShouldQuitOnKey(%q) = %v, expected %v", tt.key, result, tt.expectedResult)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppConfigBasics(t *testing.T) {
|
|
||||||
// Test default config creation
|
|
||||||
defaultConfig := GetDefaultAppConfig()
|
|
||||||
|
|
||||||
if defaultConfig.KeyBindings.DisableEscQuit {
|
|
||||||
t.Error("Default configuration should allow ESC to quit")
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedQuitKeys := []string{"q", "ctrl+c"}
|
|
||||||
if len(defaultConfig.KeyBindings.QuitKeys) != len(expectedQuitKeys) {
|
|
||||||
t.Errorf("Expected %d quit keys, got %d", len(expectedQuitKeys), len(defaultConfig.KeyBindings.QuitKeys))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeWithDefaults(t *testing.T) {
|
|
||||||
// Test config with missing QuitKeys
|
|
||||||
incompleteConfig := AppConfig{
|
|
||||||
KeyBindings: KeyBindings{
|
|
||||||
DisableEscQuit: true,
|
|
||||||
// QuitKeys is missing
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mergedConfig := mergeWithDefaults(incompleteConfig)
|
|
||||||
|
|
||||||
// Should preserve DisableEscQuit
|
|
||||||
if !mergedConfig.KeyBindings.DisableEscQuit {
|
|
||||||
t.Error("Should preserve DisableEscQuit as true")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should fill in default QuitKeys
|
|
||||||
expectedQuitKeys := []string{"q", "ctrl+c"}
|
|
||||||
if len(mergedConfig.KeyBindings.QuitKeys) != len(expectedQuitKeys) {
|
|
||||||
t.Errorf("Expected %d quit keys, got %d", len(expectedQuitKeys), len(mergedConfig.KeyBindings.QuitKeys))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSaveAndLoadAppConfigIntegration(t *testing.T) {
|
|
||||||
// Create a temporary directory for testing
|
|
||||||
tempDir, err := os.MkdirTemp("", "sshm_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create temp directory: %v", err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tempDir)
|
|
||||||
|
|
||||||
// Create a custom config file directly in temp directory
|
|
||||||
configPath := filepath.Join(tempDir, "config.json")
|
|
||||||
|
|
||||||
customConfig := AppConfig{
|
|
||||||
KeyBindings: KeyBindings{
|
|
||||||
QuitKeys: []string{"q"},
|
|
||||||
DisableEscQuit: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save config directly to file
|
|
||||||
data, err := json.MarshalIndent(customConfig, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to marshal config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.WriteFile(configPath, data, 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to write config file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read and unmarshal config
|
|
||||||
readData, err := os.ReadFile(configPath)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to read config file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var loadedConfig AppConfig
|
|
||||||
err = json.Unmarshal(readData, &loadedConfig)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to unmarshal config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the loaded config matches what we saved
|
|
||||||
if !loadedConfig.KeyBindings.DisableEscQuit {
|
|
||||||
t.Error("DisableEscQuit should be true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(loadedConfig.KeyBindings.QuitKeys) != 1 || loadedConfig.KeyBindings.QuitKeys[0] != "q" {
|
|
||||||
t.Errorf("Expected quit keys to be ['q'], got %v", loadedConfig.KeyBindings.QuitKeys)
|
|
||||||
}
|
|
||||||
}
|
|
@ -80,9 +80,6 @@ type Model struct {
|
|||||||
sortMode SortMode
|
sortMode SortMode
|
||||||
configFile string // Path to the SSH config file
|
configFile string // Path to the SSH config file
|
||||||
|
|
||||||
// Application configuration
|
|
||||||
appConfig *config.AppConfig
|
|
||||||
|
|
||||||
// Version update information
|
// Version update information
|
||||||
updateInfo *version.UpdateInfo
|
updateInfo *version.UpdateInfo
|
||||||
currentVersion string
|
currentVersion string
|
||||||
|
@ -37,64 +37,35 @@ func sortHostsByName(hosts []config.SSHHost) []config.SSHHost {
|
|||||||
|
|
||||||
// filterHosts filters hosts according to the search query (name or tags)
|
// filterHosts filters hosts according to the search query (name or tags)
|
||||||
func (m Model) filterHosts(query string) []config.SSHHost {
|
func (m Model) filterHosts(query string) []config.SSHHost {
|
||||||
subqueries := strings.Split(query, " ")
|
|
||||||
subqueriesLength := len(subqueries)
|
|
||||||
subfilteredHosts := make([][]config.SSHHost, subqueriesLength)
|
|
||||||
for i, subquery := range subqueries {
|
|
||||||
subfilteredHosts[i] = m.filterHostsByWord(subquery)
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the intersection of search results
|
|
||||||
result := make([]config.SSHHost, 0)
|
|
||||||
tempMap := map[string]int{}
|
|
||||||
for _, hosts := range subfilteredHosts {
|
|
||||||
for _, host := range hosts {
|
|
||||||
if _, ok := tempMap[host.Name]; !ok {
|
|
||||||
tempMap[host.Name] = 1
|
|
||||||
} else {
|
|
||||||
tempMap[host.Name] = tempMap[host.Name] + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if tempMap[host.Name] == subqueriesLength {
|
|
||||||
result = append(result, host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// filterHostsByWord filters hosts according to a single word
|
|
||||||
func (m Model) filterHostsByWord(word string) []config.SSHHost {
|
|
||||||
var filtered []config.SSHHost
|
var filtered []config.SSHHost
|
||||||
|
|
||||||
if word == "" {
|
if query == "" {
|
||||||
filtered = m.hosts
|
filtered = m.hosts
|
||||||
} else {
|
} else {
|
||||||
word = strings.ToLower(word)
|
query = strings.ToLower(query)
|
||||||
|
|
||||||
for _, host := range m.hosts {
|
for _, host := range m.hosts {
|
||||||
// Check the hostname
|
// Check the hostname
|
||||||
if strings.Contains(strings.ToLower(host.Name), word) {
|
if strings.Contains(strings.ToLower(host.Name), query) {
|
||||||
filtered = append(filtered, host)
|
filtered = append(filtered, host)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the hostname
|
// Check the hostname
|
||||||
if strings.Contains(strings.ToLower(host.Hostname), word) {
|
if strings.Contains(strings.ToLower(host.Hostname), query) {
|
||||||
filtered = append(filtered, host)
|
filtered = append(filtered, host)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the user
|
// Check the user
|
||||||
if strings.Contains(strings.ToLower(host.User), word) {
|
if strings.Contains(strings.ToLower(host.User), query) {
|
||||||
filtered = append(filtered, host)
|
filtered = append(filtered, host)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the tags
|
// Check the tags
|
||||||
for _, tag := range host.Tags {
|
for _, tag := range host.Tags {
|
||||||
if strings.Contains(strings.ToLower(tag), word) {
|
if strings.Contains(strings.ToLower(tag), query) {
|
||||||
filtered = append(filtered, host)
|
filtered = append(filtered, host)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,6 @@ import (
|
|||||||
|
|
||||||
// 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, currentVersion string) Model {
|
func NewModel(hosts []config.SSHHost, configFile, currentVersion string) Model {
|
||||||
// Load application configuration
|
|
||||||
appConfig, err := config.LoadAppConfig()
|
|
||||||
if err != nil {
|
|
||||||
// Log the error but continue with default configuration
|
|
||||||
fmt.Printf("Warning: Could not load application config: %v, using defaults\n", err)
|
|
||||||
defaultConfig := config.GetDefaultAppConfig()
|
|
||||||
appConfig = &defaultConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the history manager
|
// Initialize the history manager
|
||||||
historyManager, err := history.NewHistoryManager()
|
historyManager, err := history.NewHistoryManager()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -48,7 +39,6 @@ func NewModel(hosts []config.SSHHost, configFile, currentVersion string) Model {
|
|||||||
sortMode: SortByName,
|
sortMode: SortByName,
|
||||||
configFile: configFile,
|
configFile: configFile,
|
||||||
currentVersion: currentVersion,
|
currentVersion: currentVersion,
|
||||||
appConfig: appConfig,
|
|
||||||
styles: styles,
|
styles: styles,
|
||||||
width: 80,
|
width: 80,
|
||||||
height: 24,
|
height: 24,
|
||||||
|
@ -445,9 +445,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
func (m Model) handleListViewKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
func (m Model) handleListViewKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
key := msg.String()
|
|
||||||
|
|
||||||
switch key {
|
switch msg.String() {
|
||||||
case "esc", "ctrl+c":
|
case "esc", "ctrl+c":
|
||||||
if m.deleteMode {
|
if m.deleteMode {
|
||||||
// Exit delete mode
|
// Exit delete mode
|
||||||
@ -456,17 +455,11 @@ func (m Model) handleListViewKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
m.table.Focus()
|
m.table.Focus()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
// Use configurable key bindings for quit
|
|
||||||
if m.appConfig != nil && m.appConfig.KeyBindings.ShouldQuitOnKey(key) {
|
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
|
||||||
case "q":
|
case "q":
|
||||||
if !m.searchMode && !m.deleteMode {
|
if !m.searchMode && !m.deleteMode {
|
||||||
// Use configurable key bindings for quit
|
|
||||||
if m.appConfig != nil && m.appConfig.KeyBindings.ShouldQuitOnKey(key) {
|
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case "/", "ctrl+f":
|
case "/", "ctrl+f":
|
||||||
if !m.searchMode && !m.deleteMode {
|
if !m.searchMode && !m.deleteMode {
|
||||||
// Enter search mode
|
// Enter search mode
|
||||||
|
Loading…
x
Reference in New Issue
Block a user