mirror of https://git.jolheiser.com/ugit.git
231 lines
5.0 KiB
Go
231 lines
5.0 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/bubbles/textinput"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
"go.jolheiser.com/ugit/internal/git"
|
|
)
|
|
|
|
type repoForm struct {
|
|
inputs []textinput.Model
|
|
isPrivate bool
|
|
focusIndex int
|
|
width int
|
|
height int
|
|
done bool
|
|
save bool
|
|
selectedRepo *git.Repo
|
|
}
|
|
|
|
// newRepoForm creates a new repository editing form
|
|
func newRepoForm() repoForm {
|
|
var inputs []textinput.Model
|
|
|
|
nameInput := textinput.New()
|
|
nameInput.Placeholder = "Repository name"
|
|
nameInput.Focus()
|
|
nameInput.Width = 50
|
|
inputs = append(inputs, nameInput)
|
|
|
|
descInput := textinput.New()
|
|
descInput.Placeholder = "Repository description"
|
|
descInput.Width = 50
|
|
inputs = append(inputs, descInput)
|
|
|
|
tagsInput := textinput.New()
|
|
tagsInput.Placeholder = "Tags (comma separated)"
|
|
tagsInput.Width = 50
|
|
inputs = append(inputs, tagsInput)
|
|
|
|
return repoForm{
|
|
inputs: inputs,
|
|
focusIndex: 0,
|
|
}
|
|
}
|
|
|
|
// setValues sets the form values from the selected repo
|
|
func (f *repoForm) setValues(repo *git.Repo) {
|
|
f.inputs[0].SetValue(repo.Name())
|
|
f.inputs[1].SetValue(repo.Meta.Description)
|
|
f.inputs[2].SetValue(strings.Join(repo.Meta.Tags.Slice(), ", "))
|
|
f.isPrivate = repo.Meta.Private
|
|
|
|
f.inputs[0].Focus()
|
|
f.focusIndex = 0
|
|
}
|
|
|
|
// setSize sets the form dimensions
|
|
func (f *repoForm) setSize(width, height int) {
|
|
f.width = width
|
|
f.height = height
|
|
|
|
for i := range f.inputs {
|
|
f.inputs[i].Width = width - 10
|
|
}
|
|
}
|
|
|
|
// isPrivateToggleFocused returns true if the private toggle is focused
|
|
func (f *repoForm) isPrivateToggleFocused() bool {
|
|
return f.focusIndex == len(f.inputs)
|
|
}
|
|
|
|
// isSaveButtonFocused returns true if the save button is focused
|
|
func (f *repoForm) isSaveButtonFocused() bool {
|
|
return f.focusIndex == len(f.inputs)+1
|
|
}
|
|
|
|
// isCancelButtonFocused returns true if the cancel button is focused
|
|
func (f *repoForm) isCancelButtonFocused() bool {
|
|
return f.focusIndex == len(f.inputs)+2
|
|
}
|
|
|
|
// Update handles form updates
|
|
func (f repoForm) Update(msg tea.Msg) (repoForm, tea.Cmd) {
|
|
var cmds []tea.Cmd
|
|
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "tab", "shift+tab", "up", "down":
|
|
if msg.String() == "up" || msg.String() == "shift+tab" {
|
|
f.focusIndex--
|
|
if f.focusIndex < 0 {
|
|
f.focusIndex = len(f.inputs) + 3 - 1
|
|
}
|
|
} else {
|
|
f.focusIndex++
|
|
if f.focusIndex >= len(f.inputs)+3 {
|
|
f.focusIndex = 0
|
|
}
|
|
}
|
|
|
|
for i := range f.inputs {
|
|
if i == f.focusIndex {
|
|
cmds = append(cmds, f.inputs[i].Focus())
|
|
} else {
|
|
f.inputs[i].Blur()
|
|
}
|
|
}
|
|
|
|
case "enter":
|
|
if f.isSaveButtonFocused() {
|
|
f.done = true
|
|
f.save = true
|
|
return f, nil
|
|
}
|
|
|
|
if f.isCancelButtonFocused() {
|
|
f.done = true
|
|
f.save = false
|
|
return f, nil
|
|
}
|
|
|
|
case "esc":
|
|
f.done = true
|
|
f.save = false
|
|
return f, nil
|
|
|
|
case " ":
|
|
if f.isPrivateToggleFocused() {
|
|
f.isPrivate = !f.isPrivate
|
|
}
|
|
|
|
if f.isSaveButtonFocused() {
|
|
f.done = true
|
|
f.save = true
|
|
return f, nil
|
|
}
|
|
|
|
if f.isCancelButtonFocused() {
|
|
f.done = true
|
|
f.save = false
|
|
return f, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := range f.inputs {
|
|
if i == f.focusIndex {
|
|
var cmd tea.Cmd
|
|
f.inputs[i], cmd = f.inputs[i].Update(msg)
|
|
cmds = append(cmds, cmd)
|
|
}
|
|
}
|
|
|
|
return f, tea.Batch(cmds...)
|
|
}
|
|
|
|
// View renders the form
|
|
func (f repoForm) View() string {
|
|
var b strings.Builder
|
|
|
|
formStyle := lipgloss.NewStyle().
|
|
BorderStyle(lipgloss.RoundedBorder()).
|
|
BorderForeground(lipgloss.Color("170")).
|
|
Padding(1, 2)
|
|
|
|
titleStyle := lipgloss.NewStyle().
|
|
Bold(true).
|
|
Foreground(lipgloss.Color("170")).
|
|
MarginBottom(1)
|
|
|
|
b.WriteString(titleStyle.Render("Edit Repository"))
|
|
b.WriteString("\n\n")
|
|
|
|
b.WriteString("Repository Name:\n")
|
|
b.WriteString(f.inputs[0].View())
|
|
b.WriteString("\n\n")
|
|
|
|
b.WriteString("Description:\n")
|
|
b.WriteString(f.inputs[1].View())
|
|
b.WriteString("\n\n")
|
|
|
|
b.WriteString("Tags (comma separated):\n")
|
|
b.WriteString(f.inputs[2].View())
|
|
b.WriteString("\n\n")
|
|
|
|
toggleStyle := lipgloss.NewStyle()
|
|
if f.isPrivateToggleFocused() {
|
|
toggleStyle = toggleStyle.Foreground(lipgloss.Color("170")).Bold(true)
|
|
}
|
|
|
|
visibility := "Public 🔓"
|
|
if f.isPrivate {
|
|
visibility = "Private 🔒"
|
|
}
|
|
|
|
b.WriteString(toggleStyle.Render(fmt.Sprintf("[%s] %s", visibility, "Toggle with Space")))
|
|
b.WriteString("\n\n")
|
|
|
|
buttonStyle := lipgloss.NewStyle().
|
|
Padding(0, 3).
|
|
MarginRight(1)
|
|
|
|
focusedButtonStyle := buttonStyle.Copy().
|
|
Foreground(lipgloss.Color("0")).
|
|
Background(lipgloss.Color("170")).
|
|
Bold(true)
|
|
|
|
saveButton := buttonStyle.Render("[ Save ]")
|
|
cancelButton := buttonStyle.Render("[ Cancel ]")
|
|
|
|
if f.isSaveButtonFocused() {
|
|
saveButton = focusedButtonStyle.Render("[ Save ]")
|
|
}
|
|
|
|
if f.isCancelButtonFocused() {
|
|
cancelButton = focusedButtonStyle.Render("[ Cancel ]")
|
|
}
|
|
|
|
b.WriteString(saveButton + cancelButton)
|
|
b.WriteString("\n\n")
|
|
|
|
b.WriteString("\nTab: Next • Shift+Tab: Previous • Enter: Select • Esc: Cancel")
|
|
|
|
return formStyle.Width(f.width - 4).Render(b.String())
|
|
}
|