152 lines
3.5 KiB
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()
|
|
}
|