sshkeeper/internal/tui/template_form.go

152 lines
3.5 KiB
Go

package tui
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/bubbletea"
"github.com/mirivlad/sshkeeper/internal/model"
)
// --- Template form model ---
type templateFormModel struct {
edit bool
oldName string
inputs []textinput.Model
labels []string
focusIdx int
err error
saved bool
width int
height int
}
func newTemplateFormModel(t *model.CommandTemplate, w, h int) *templateFormModel {
labels := []string{"Name", "Command", "Description"}
inputs := make([]textinput.Model, len(labels))
for i := range inputs {
inputs[i] = textinput.New()
inputs[i].CharLimit = 512
}
inputs[0].Placeholder = "uptime"
inputs[1].Placeholder = "uptime"
inputs[2].Placeholder = "optional"
inputs[0].Focus()
tf := &templateFormModel{inputs: inputs, labels: labels, width: w, height: h}
if t != nil {
tf.edit = true
tf.oldName = t.Name
inputs[0].SetValue(t.Name)
inputs[1].SetValue(t.Command)
inputs[2].SetValue(t.Description)
}
tf.updateFocus()
return tf
}
func (tf *templateFormModel) Init() tea.Cmd {
return nil
}
func (tf *templateFormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyTab, tea.KeyDown:
tf.focusIdx++
if tf.focusIdx > len(tf.inputs) {
tf.focusIdx = 0
}
tf.updateFocus()
return tf, nil
case tea.KeyShiftTab, tea.KeyUp:
tf.focusIdx--
if tf.focusIdx < 0 {
tf.focusIdx = len(tf.inputs)
}
tf.updateFocus()
return tf, nil
case tea.KeyEnter:
if tf.focusIdx == len(tf.inputs) {
return tf, tf.save()
}
tf.focusIdx++
tf.updateFocus()
return tf, nil
}
}
if tf.focusIdx < len(tf.inputs) {
var cmd tea.Cmd
tf.inputs[tf.focusIdx], cmd = tf.inputs[tf.focusIdx].Update(msg)
return tf, cmd
}
return tf, nil
}
func (tf *templateFormModel) updateFocus() {
for i := range tf.inputs {
tf.inputs[i].Blur()
tf.inputs[i].Prompt = blurredStyle.Render(tf.labels[i] + ": ")
}
if tf.focusIdx < len(tf.inputs) {
tf.inputs[tf.focusIdx].Focus()
tf.inputs[tf.focusIdx].Prompt = focusedStyle.Render(tf.labels[tf.focusIdx] + "> ")
}
}
func (tf *templateFormModel) save() tea.Cmd {
return func() tea.Msg {
if SaveCommandTemplate == nil {
return saveDoneMsg{err: fmt.Errorf("template storage is unavailable")}
}
t := &model.CommandTemplate{
Name: strings.TrimSpace(tf.inputs[0].Value()),
Command: strings.TrimSpace(tf.inputs[1].Value()),
Description: strings.TrimSpace(tf.inputs[2].Value()),
}
if t.Name == "" {
return saveDoneMsg{err: fmt.Errorf("name is required")}
}
if t.Command == "" {
return saveDoneMsg{err: fmt.Errorf("command is required")}
}
if err := SaveCommandTemplate(tf.oldName, t); err != nil {
return saveDoneMsg{err: err}
}
return saveDoneMsg{}
}
}
func (tf *templateFormModel) View() string {
var b strings.Builder
title := "Add Template"
if tf.edit {
title = "Edit Template"
}
b.WriteString(titleStyle.Render(title))
b.WriteString("\n\n")
for i := range tf.inputs {
b.WriteString(tf.inputs[i].View())
b.WriteString("\n")
}
button := "[ Save ]"
if tf.focusIdx == len(tf.inputs) {
button = selectedStyle.Render(button)
}
b.WriteString("\n" + button + "\n\n")
if tf.err != nil {
b.WriteString(errorStyle.Render(tf.err.Error()))
b.WriteString("\n")
}
b.WriteString(renderHelp([]helpItem{
{Key: "Tab/↓", Action: "next"},
{Key: "↑", Action: "prev"},
{Key: "Enter", Action: "select"},
{Key: "Esc", Action: "back"},
}, tf.width))
return b.String()
}