Added Quick commands

+Added Quick command Functionality
This commit is contained in:
2026-05-03 02:03:57 +02:00
parent d2b61046c6
commit b886c3e9e8
3 changed files with 209 additions and 3 deletions

193
main.go
View File

@@ -26,6 +26,8 @@ const (
ViewAddServer viewMode = "add_server"
ViewEditServer viewMode = "edit_server"
ViewDeleteConfirm viewMode = "delete_confirm"
ViewCommands viewMode = "commands"
ViewCommandOutput viewMode = "command_output"
)
type model struct {
@@ -40,6 +42,10 @@ type model struct {
addInputs []textinput.Model
addFocus int
editIndex int
commandSelected int
commandOutput string
commandTitle string
commandError string
}
var (
@@ -190,6 +196,12 @@ func (m *model) saveNewServer() error {
return nil
}
type quickCommandResultMsg struct {
Title string
Output string
Error string
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.err != nil {
return m, nil
@@ -200,8 +212,28 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.width = msg.Width
m.height = msg.Height
case quickCommandResultMsg:
m.commandTitle = msg.Title
m.commandOutput = msg.Output
m.commandError = msg.Error
m.view = ViewCommandOutput
return m, nil
case tea.KeyMsg:
// Command Output offen lassen, bis du ihn wegdrückst
if m.view == ViewCommandOutput {
switch msg.String() {
case "q", "esc", "enter":
m.view = ViewCommands
return m, nil
case "ctrl+c":
return m, tea.Quit
}
}
// Add/Edit Formular
if m.view == ViewAddServer || m.view == ViewEditServer {
switch msg.String() {
case "ctrl+c":
@@ -252,6 +284,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd
}
// Delete Confirm
if m.view == ViewDeleteConfirm {
switch msg.String() {
case "y", "Y", "j":
@@ -267,6 +300,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
// Normale Navigation
switch msg.String() {
case "ctrl+c":
return m, tea.Quit
@@ -301,27 +335,48 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
return m, nil
case "c":
if len(m.servers) > 0 {
m.view = ViewCommands
m.commandSelected = 0
}
return m, nil
case "s":
m.view = ViewSettings
return m, nil
case "h":
m.view = ViewHelp
return m, nil
case "up", "k":
if m.view == ViewServers && m.selected > 0 {
m.selected--
}
if m.view == ViewCommands && m.commandSelected > 0 {
m.commandSelected--
}
case "down", "j":
if m.view == ViewServers && m.selected < len(m.servers)-1 {
m.selected++
}
if m.view == ViewCommands && m.commandSelected < len(m.cfg.QuickCommands)-1 {
m.commandSelected++
}
case "enter":
if m.view == ViewServers && len(m.servers) > 0 {
server := m.servers[m.selected]
return m, connectSSH(server, m.cfg.Settings)
}
if m.view == ViewCommands && len(m.servers) > 0 && len(m.cfg.QuickCommands) > 0 {
server := m.servers[m.selected]
quick := m.cfg.QuickCommands[m.commandSelected]
return m, runQuickCommand(server, m.cfg.Settings, quick)
}
}
}
@@ -367,6 +422,10 @@ func (m model) View() string {
right = m.renderEditServerContent()
case ViewDeleteConfirm:
right = m.renderDeleteConfirmContent()
case ViewCommands:
right = m.renderCommandsContent()
case ViewCommandOutput:
right = m.renderCommandOutputContent()
default:
right = m.renderServerList()
}
@@ -378,7 +437,7 @@ func (m model) View() string {
right,
)
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden a Hinzufügen e Editieren d Löschen Tab Ansicht q Zurück/Beenden")
footer := helpStyle.Render("↑/↓ Auswahl Enter Verbinden/Ausführen a Hinzufügen e Editieren d Löschen c Commands Tab Ansicht q Zurück/Beenden")
content := lipgloss.JoinVertical(
lipgloss.Left,
@@ -421,6 +480,7 @@ func (m model) renderNavigation() string {
{"󰐕", "Add Server", ViewAddServer},
{"󰢹", "Settings", ViewSettings},
{"󰋖", "Help", ViewHelp},
{"󰘳", "Commands", ViewCommands},
}
var lines []string
@@ -775,6 +835,137 @@ func (m model) renderServerFormContent(badge string, title string) string {
Render(strings.Join(lines, "\n"))
}
func (m model) renderCommandOutputContent() string {
lines := []string{
badgeStyle.Render(" COMMAND OUTPUT "),
"",
selectedStyle.Render(m.commandTitle),
"",
}
if m.commandError != "" {
lines = append(lines, warnStyle().Render("Fehler: "+m.commandError))
lines = append(lines, "")
}
output := strings.TrimSpace(m.commandOutput)
if output == "" {
output = "Keine Ausgabe."
}
lines = append(lines, output)
lines = append(lines, "")
lines = append(lines, mutedStyle.Render("q / Esc / Enter: zurück zu Quick Commands"))
return panelStyle.
Width(max(m.width-36, 60)).
Height(max(m.height-9, 14)).
Render(strings.Join(lines, "\n"))
}
func (m model) renderCommandsContent() string {
lines := []string{
badgeStyle.Render(" QUICK COMMANDS "),
"",
}
if len(m.servers) == 0 {
lines = append(lines, mutedStyle.Render("Kein Server ausgewählt."))
} else {
server := m.servers[m.selected]
lines = append(lines, fmt.Sprintf("Server: %s %s@%s:%d", server.Name, server.User, server.Host, server.Port))
lines = append(lines, "")
}
if len(m.cfg.QuickCommands) == 0 {
lines = append(lines, mutedStyle.Render("Keine quick_commands in config.yaml gefunden."))
} else {
for i, cmd := range m.cfg.QuickCommands {
line := fmt.Sprintf("%s → %s", cmd.Name, cmd.Command)
if i == m.commandSelected {
lines = append(lines, selectedStyle.Render("> "+line))
} else {
lines = append(lines, normalStyle.Render(" "+line))
}
}
}
lines = append(lines, "")
lines = append(lines, mutedStyle.Render("Enter: Command ausführen ↑/↓ Auswahl q: zurück"))
return panelStyle.
Width(max(m.width-36, 60)).
Height(max(m.height-9, 14)).
Render(strings.Join(lines, "\n"))
}
func runQuickCommand(server models.Server, settings models.Settings, quick models.QuickCommand) tea.Cmd {
return func() tea.Msg {
title := fmt.Sprintf("%s auf %s", quick.Name, server.Name)
args := []string{
"-p", strconv.Itoa(server.Port),
}
if settings.Terminal.EnableKittyFix && server.KittyFix {
args = append(args, "-t")
}
var cmd *exec.Cmd
if server.Auth == "key" {
if server.Key != "" {
args = append(args, "-i", expandHome(server.Key))
}
args = append(args, server.User+"@"+server.Host, quick.Command)
cmd = exec.Command("ssh", args...)
cmd.Env = buildSSHEnv(server, settings)
} else if server.Auth == "password" {
password, err := secret.GetPassword(server.PasswordID)
if err != nil {
return quickCommandResultMsg{
Title: title,
Output: "",
Error: fmt.Sprintf("kein passwort gespeichert für %s", server.PasswordID),
}
}
sshArgs := []string{
"-e",
"ssh",
"-p", strconv.Itoa(server.Port),
server.User + "@" + server.Host,
quick.Command,
}
cmd = exec.Command("sshpass", sshArgs...)
cmd.Env = append(buildSSHEnv(server, settings), "SSHPASS="+password)
} else {
return quickCommandResultMsg{
Title: title,
Output: "",
Error: fmt.Sprintf("unbekannte auth methode: %s", server.Auth),
}
}
output, err := cmd.CombinedOutput()
errorText := ""
if err != nil {
errorText = err.Error()
}
return quickCommandResultMsg{
Title: title,
Output: string(output),
Error: errorText,
}
}
}
func (m model) renderDeleteConfirmContent() string {
if len(m.servers) == 0 {
return panelStyle.Render("Kein Server zum Löschen vorhanden.")