ugit/internal/ssh/tui/tui.go

169 lines
3.1 KiB
Go

package tui
import (
"fmt"
"os"
"sort"
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/ssh"
"go.jolheiser.com/ugit/internal/git"
)
type tui struct {
s ssh.Session
repoDir string
note *string
repo *git.Repo
nextCmd func() error
}
func New(s ssh.Session, repoDir string) error {
t := tui{
s: s,
repoDir: repoDir,
}
t.nextCmd = t.repoList
return t.run()
}
func (t *tui) run() error {
for {
if t.nextCmd == nil {
break
}
if err := t.nextCmd(); err != nil {
return err
}
}
return nil
}
func (t *tui) repoList() error {
repoPaths, err := os.ReadDir(t.repoDir)
if err != nil {
return err
}
repos := make([]*git.Repo, 0, len(repoPaths))
for _, repoName := range repoPaths {
if !strings.HasSuffix(repoName.Name(), ".git") {
continue
}
repo, err := git.NewRepo(t.repoDir, repoName.Name())
if err != nil {
return err
}
repos = append(repos, repo)
}
sort.Slice(repos, func(i, j int) bool {
return repos[i].Name() < repos[j].Name()
})
opts := make([]huh.Option[*git.Repo], 0, len(repos))
for _, repo := range repos {
opts = append(opts, huh.NewOption(repo.Name(), repo))
}
if err := t.form(huh.NewSelect[*git.Repo]().
Title("Repo Selection").
Options(opts...).
Value(&t.repo),
).Run(); err != nil {
return err
}
t.nextCmd = t.repoMenu
return nil
}
func (t *tui) repoMenu() error {
var sel string
if err := t.form(huh.NewSelect[string]().
Title(t.repo.Name()).
Options(
huh.NewOption("edit", "edit"),
huh.NewOption("delete", "delete"),
huh.NewOption("back", "back"),
).
Value(&sel),
).Run(); err != nil {
return err
}
switch sel {
case "edit":
t.nextCmd = t.repoEdit
case "delete":
t.nextCmd = t.repoDelete
case "back":
t.nextCmd = t.repoList
default:
t.nextCmd = nil
}
return nil
}
func (t *tui) repoEdit() error {
form := git.RepoMeta{
Description: t.repo.Meta.Description,
Private: t.repo.Meta.Private,
}
if err := t.form(
huh.NewText().
Title("Description").
Value(&form.Description),
huh.NewConfirm().
Title("Visibility").
Affirmative("Private").
Negative("Public").
Value(&form.Private),
).Run(); err != nil {
return err
}
t.repo.Meta = form
if err := t.repo.SaveMeta(); err != nil {
return err
}
t.note = note(fmt.Sprintf("%q edited", t.repo.Name()))
t.nextCmd = t.repoList
return nil
}
func (t *tui) repoDelete() error {
var del bool
if err := t.form(huh.NewConfirm().
Title(fmt.Sprintf("Are you sure you want to delete %q?", t.repo.Name())).
Value(&del),
).Run(); err != nil {
return err
}
if del {
if err := os.RemoveAll(t.repo.Path()); err != nil {
return err
}
t.note = note(fmt.Sprintf("%q deleted", t.repo.Name()))
}
t.nextCmd = t.repoList
return nil
}
func (t *tui) form(fields ...huh.Field) *huh.Form {
if t.note != nil {
fields = append([]huh.Field{huh.NewNote().Title(*t.note)}, fields...)
t.note = nil
}
return huh.NewForm(
huh.NewGroup(fields...),
).WithProgramOptions(tea.WithAltScreen()).WithInput(t.s).WithOutput(t.s).WithTheme(huh.ThemeCatppuccin())
}
func note(msg string) *string {
return &msg
}