first commit
This commit is contained in:
134
cli/internal/ui/tui/confirm.go
Normal file
134
cli/internal/ui/tui/confirm.go
Normal file
@@ -0,0 +1,134 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user