Skip to content

Commit 3af0c0c

Browse files
committed
feat: add paste SSH command feature with 'v' keybinding
- Add 'v' keybinding to paste and parse SSH commands from clipboard - Parse SSH command using the new SSH parser - Auto-generate unique alias if duplicate detected - Open server form in Add mode with parsed data - Update UI components to show 'v' keybinding: - Hint bar: Add 'v' to keybinding hints - Status bar: Add 'v' Paste SSH to navigation help - Server details: Add 'v' to commands list - Support for clipboard integration via github.com/atotto/clipboard - Add getExistingAliases helper to retrieve all current aliases
1 parent d20c552 commit 3af0c0c

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ With lazyssh, you can quickly navigate, connect, manage, and transfer files betw
2323
### Quick Server Navigation
2424
- 🔍 Fuzzy search by alias, IP, or tags.
2525
- 🖥 One‑keypress SSH into the selected server (Enter).
26+
- 📋 Copy SSH command to clipboard & paste SSH commands from clipboard to quickly add servers.
2627
- 🏷 Tag servers (e.g., prod, dev, test) for quick filtering.
2728
- ↕️ Sort by alias or last SSH (toggle + reverse).
2829

@@ -168,6 +169,7 @@ make run
168169
| ↑↓/jk | Navigate servers |
169170
| Enter | SSH into selected server |
170171
| c | Copy SSH command to clipboard |
172+
| v | Paste SSH command from clipboard |
171173
| g | Ping selected server |
172174
| r | Refresh background data |
173175
| a | Add server |

internal/adapters/ui/handlers.go

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ func (t *tui) handleGlobalKeys(event *tcell.EventKey) *tcell.EventKey {
6363
case 'c':
6464
t.handleCopyCommand()
6565
return nil
66+
case 'v':
67+
t.handlePasteCommand()
68+
return nil
6669
case 'g':
6770
t.handlePingSelected()
6871
return nil
@@ -125,6 +128,37 @@ func (t *tui) handleCopyCommand() {
125128
}
126129
}
127130

131+
func (t *tui) handlePasteCommand() {
132+
// Read from clipboard
133+
clipContent, err := clipboard.ReadAll()
134+
if err != nil {
135+
t.showStatusTemp("Failed to read from clipboard")
136+
return
137+
}
138+
139+
// Try to parse as SSH command
140+
server, err := ParseSSHCommand(clipContent)
141+
if err != nil {
142+
t.showStatusTemp("Invalid SSH command in clipboard: " + err.Error())
143+
return
144+
}
145+
146+
// Check for duplicate alias and auto-adjust if necessary
147+
existingAliases := t.getExistingAliases()
148+
server.Alias = GenerateUniqueAlias(server.Alias, existingAliases)
149+
150+
// Show the server form with parsed data
151+
// Note: For Add mode, original should be nil. We'll set initial data separately.
152+
form := NewServerForm(ServerFormAdd, nil).
153+
SetInitialData(server).
154+
SetApp(t.app).
155+
SetVersionInfo(t.version, t.commit).
156+
OnSave(t.handleServerSave).
157+
OnCancel(t.handleFormCancel).
158+
SetExistingAliases(t.getExistingAliases())
159+
t.app.SetRoot(form, true)
160+
}
161+
128162
func (t *tui) handleTagsEdit() {
129163
if server, ok := t.serverList.GetSelectedServer(); ok {
130164
t.showEditTagsForm(server)
@@ -186,7 +220,8 @@ func (t *tui) handleServerAdd() {
186220
SetApp(t.app).
187221
SetVersionInfo(t.version, t.commit).
188222
OnSave(t.handleServerSave).
189-
OnCancel(t.handleFormCancel)
223+
OnCancel(t.handleFormCancel).
224+
SetExistingAliases(t.getExistingAliases())
190225
t.app.SetRoot(form, true)
191226
}
192227

@@ -196,7 +231,8 @@ func (t *tui) handleServerEdit() {
196231
SetApp(t.app).
197232
SetVersionInfo(t.version, t.commit).
198233
OnSave(t.handleServerSave).
199-
OnCancel(t.handleFormCancel)
234+
OnCancel(t.handleFormCancel).
235+
SetExistingAliases(t.getExistingAliases())
200236
t.app.SetRoot(form, true)
201237
}
202238
}
@@ -234,6 +270,20 @@ func (t *tui) handleFormCancel() {
234270
t.returnToMain()
235271
}
236272

273+
// getExistingAliases returns all existing server aliases
274+
func (t *tui) getExistingAliases() []string {
275+
servers, err := t.serverService.ListServers("")
276+
if err != nil {
277+
return []string{}
278+
}
279+
280+
aliases := make([]string, len(servers))
281+
for i, s := range servers {
282+
aliases[i] = s.Alias
283+
}
284+
return aliases
285+
}
286+
237287
func (t *tui) handlePingSelected() {
238288
if server, ok := t.serverList.GetSelectedServer(); ok {
239289
alias := server.Alias

internal/adapters/ui/hint_bar.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ import (
2222
func NewHintBar() *tview.TextView {
2323
hint := tview.NewTextView().SetDynamicColors(true)
2424
hint.SetBackgroundColor(tcell.Color233)
25-
hint.SetText("[#BBBBBB]Press [::b]/[-:-:b] to search… • ↑↓ Navigate • Enter SSH • c Copy SSH • g Ping • r Refresh • a Add • e Edit • t Tags • d Delete • p Pin/Unpin • s Sort[-]")
25+
hint.SetText("[#BBBBBB]Press [::b]/[-:-:b] to search… • ↑↓ Navigate • Enter SSH • c Copy SSH • v Paste SSH • g Ping • r Refresh • a Add • e Edit • t Tags • d Delete • p Pin/Unpin • s Sort[-]")
2626
return hint
2727
}

internal/adapters/ui/server_details.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ func (sd *ServerDetails) UpdateServer(server domain.Server) {
213213
}
214214

215215
// Commands list
216-
text += "\n[::b]Commands:[-]\n Enter: SSH connect\n c: Copy SSH command\n g: Ping server\n r: Refresh list\n a: Add new server\n e: Edit entry\n t: Edit tags\n d: Delete entry\n p: Pin/Unpin"
216+
text += "\n[::b]Commands:[-]\n Enter: SSH connect\n c: Copy SSH command\n v: Paste SSH command\n g: Ping server\n r: Refresh list\n a: Add new server\n e: Edit entry\n t: Edit tags\n d: Delete entry\n p: Pin/Unpin"
217217

218218
sd.TextView.SetText(text)
219219
}

internal/adapters/ui/status_bar.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
)
2121

2222
func DefaultStatusText() string {
23-
return "[white]↑↓[-] Navigate • [white]Enter[-] SSH • [white]c[-] Copy SSH • [white]a[-] Add • [white]e[-] Edit • [white]g[-] Ping • [white]d[-] Delete • [white]p[-] Pin/Unpin • [white]/[-] Search • [white]q[-] Quit"
23+
return "[white]↑↓[-] Navigate • [white]Enter[-] SSH • [white]c[-] Copy SSH • [white]v[-] Paste SSH • [white]a[-] Add • [white]e[-] Edit • [white]g[-] Ping • [white]d[-] Delete • [white]p[-] Pin/Unpin • [white]/[-] Search • [white]q[-] Quit"
2424
}
2525

2626
func NewStatusBar() *tview.TextView {

0 commit comments

Comments
 (0)