135 lines
3.1 KiB
Go
135 lines
3.1 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
// confirmModel holds information about current model selection
|
|
type confirmModel struct {
|
|
header string
|
|
prompt string
|
|
command string
|
|
idx int
|
|
quit bool
|
|
yes bool
|
|
clearFirst bool
|
|
}
|
|
|
|
// runConfirm runs a confirm dialog with the given model and returns the user's
|
|
// choice.
|
|
func runConfirm(m confirmModel) (bool, error) {
|
|
p := tea.NewProgram(
|
|
m,
|
|
tea.WithInput(os.Stdin),
|
|
tea.WithOutput(os.Stdout),
|
|
tea.WithInputTTY(),
|
|
)
|
|
out, err := p.Run()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
fm, ok := out.(confirmModel)
|
|
if !ok {
|
|
return false, fmt.Errorf("unexpected model type from tui")
|
|
}
|
|
if fm.quit {
|
|
return false, nil
|
|
}
|
|
return fm.yes, nil
|
|
}
|
|
|
|
// RunConfirmInstall displays a yes/no confirmation dialog asking the user
|
|
// whether to install a missing harness. Returns true if the user confirms.
|
|
func RunConfirmInstall(header, harnessLabel, command string) (bool, error) {
|
|
return runConfirm(confirmModel{
|
|
header: header,
|
|
prompt: fmt.Sprintf("%s is not installed. Install now?", harnessLabel),
|
|
command: command,
|
|
})
|
|
}
|
|
|
|
// RunConfirmSettings displays a yes/no confirmation dialog asking the user
|
|
// whether to update the harness's native settings file. Returns true if the
|
|
// user confirms.
|
|
func RunConfirmSettings(harnessLabel, settingsPath string) (bool, error) {
|
|
return runConfirm(confirmModel{
|
|
prompt: fmt.Sprintf("Update %s settings? (%s)", harnessLabel, settingsPath),
|
|
clearFirst: true,
|
|
})
|
|
}
|
|
|
|
// Init implements tea.Model.
|
|
func (m confirmModel) Init() tea.Cmd {
|
|
if m.clearFirst {
|
|
return tea.ClearScreen
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Update implements tea.Model. Handles y/n, arrow keys, and enter for confirmation.
|
|
func (m confirmModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
s := msg.String()
|
|
switch s {
|
|
case "ctrl+c", "q", "esc":
|
|
m.quit = true
|
|
return m, tea.Quit
|
|
case "left", "h", "right", "l", "tab":
|
|
if m.idx == 0 {
|
|
m.idx = 1
|
|
} else {
|
|
m.idx = 0
|
|
}
|
|
return m, nil
|
|
case "y":
|
|
m.yes = true
|
|
return m, tea.Quit
|
|
case "n":
|
|
m.yes = false
|
|
return m, tea.Quit
|
|
case "enter":
|
|
m.yes = m.idx == 0
|
|
return m, tea.Quit
|
|
}
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// View implements tea.Model. Renders the confirmation prompt with Yes/No buttons.
|
|
func (m confirmModel) View() string {
|
|
selected := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("42"))
|
|
normal := lipgloss.NewStyle().Foreground(lipgloss.Color("245"))
|
|
|
|
yes := normal.Render("[ Yes ]")
|
|
no := normal.Render("[ No ]")
|
|
if m.idx == 0 {
|
|
yes = selected.Render("[ Yes ]")
|
|
} else {
|
|
no = selected.Render("[ No ]")
|
|
}
|
|
|
|
var b strings.Builder
|
|
if m.header != "" {
|
|
b.WriteString(m.header)
|
|
b.WriteString("\n")
|
|
}
|
|
b.WriteString(m.prompt)
|
|
b.WriteString("\n")
|
|
if m.command != "" {
|
|
b.WriteString("command: ")
|
|
b.WriteString(m.command)
|
|
b.WriteString("\n")
|
|
}
|
|
b.WriteString("\n")
|
|
b.WriteString(yes + " " + no)
|
|
b.WriteString("\n")
|
|
b.WriteString("enter: confirm, y/n quick choice, q: cancel")
|
|
return b.String()
|
|
}
|