package components import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) // ConfirmModel represents a confirmation modal type ConfirmModel struct { Message string Yes bool // true = yes, false = no Visible bool Width int Height int } // Styles var ( confirmModalStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("196")). Padding(1, 2). Background(lipgloss.Color("235")) confirmTitleStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("226")). Bold(true) confirmMessageStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("255")). Width(50) confirmHelpStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("241")). MarginTop(1) ) // NewConfirm creates a new confirmation modal func NewConfirm(message string, width, height int) *ConfirmModel { return &ConfirmModel{ Message: message, Yes: false, Visible: true, Width: width, Height: height, } } // Init initializes the confirmation modal func (m *ConfirmModel) Init() tea.Cmd { return nil } // Update handles messages for the confirmation modal func (m *ConfirmModel) Update(msg tea.Msg) (*ConfirmModel, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "y", "Y", "left": m.Yes = true case "n", "N", "right": m.Yes = false case "enter": // Confirmed - will be handled by parent return m, nil case "esc": m.Visible = false return m, nil } } return m, nil } // View renders the confirmation modal func (m *ConfirmModel) View() string { if !m.Visible { return "" } // Build modal content content := lipgloss.JoinVertical( lipgloss.Left, confirmTitleStyle.Render("⚠️ Confirm Action"), "", confirmMessageStyle.Render(m.Message), "", m.renderOptions(), ) // Apply modal style modal := confirmModalStyle.Render(content) // Center modal on screen modalWidth := lipgloss.Width(modal) modalHeight := lipgloss.Height(modal) x := (m.Width - modalWidth) / 2 if x < 0 { x = 0 } y := (m.Height - modalHeight) / 2 if y < 0 { y = 0 } return lipgloss.Place(m.Width, m.Height, lipgloss.Left, lipgloss.Top, modal, lipgloss.WithWhitespaceChars(" "), lipgloss.WithWhitespaceForeground(lipgloss.Color("235")), ) } // renderOptions renders the yes/no options func (m *ConfirmModel) renderOptions() string { yesStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("241")) noStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("241")) selectedStyle := lipgloss.NewStyle(). Foreground(lipgloss.Color("57")). Bold(true). Underline(true) var yesText, noText string if m.Yes { yesText = selectedStyle.Render("[Yes]") noText = noStyle.Render(" No ") } else { yesText = yesStyle.Render(" Yes ") noText = selectedStyle.Render("[No]") } helpText := confirmHelpStyle.Render("←/→ to choose • Enter to confirm • Esc to cancel") return lipgloss.JoinHorizontal(lipgloss.Left, yesText, " ", noText, "\n", helpText) } // IsConfirmed returns true if user confirmed with Yes func (m *ConfirmModel) IsConfirmed() bool { return m.Yes } // IsCancelled returns true if user cancelled func (m *ConfirmModel) IsCancelled() bool { return !m.Visible }