Some checks failed
CI/CD / Test (push) Successful in 3m10s
CI/CD / Lint (push) Successful in 1m31s
CI/CD / Integration Tests (push) Successful in 1m9s
CI/CD / Native Engine Tests (push) Successful in 1m2s
CI/CD / Build Binary (push) Successful in 54s
CI/CD / Test Release Build (push) Successful in 1m46s
CI/CD / Release Binaries (push) Failing after 11m4s
- Fix menu.go case 10/11 mismatch (separator vs profile item) - Add tea.InterruptMsg handlers for Bubbletea v1.3+ SIGINT handling: - archive_browser.go - restore_preview.go - confirmation.go - dbselector.go - cluster_db_selector.go - profile.go - Add missing ctrl+c key handlers to cluster_db_selector and profile - Fix ConfirmationModel fallback to use context.Background() if nil
153 lines
3.5 KiB
Go
Executable File
153 lines
3.5 KiB
Go
Executable File
package tui
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
"dbbackup/internal/config"
|
|
"dbbackup/internal/logger"
|
|
)
|
|
|
|
// ConfirmationModel for yes/no confirmations
|
|
type ConfirmationModel struct {
|
|
config *config.Config
|
|
logger logger.Logger
|
|
parent tea.Model
|
|
ctx context.Context
|
|
title string
|
|
message string
|
|
cursor int
|
|
choices []string
|
|
confirmed bool
|
|
onConfirm func() (tea.Model, tea.Cmd) // Callback when confirmed
|
|
}
|
|
|
|
func NewConfirmationModel(cfg *config.Config, log logger.Logger, parent tea.Model, title, message string) ConfirmationModel {
|
|
return ConfirmationModel{
|
|
config: cfg,
|
|
logger: log,
|
|
parent: parent,
|
|
title: title,
|
|
message: message,
|
|
choices: []string{"Yes", "No"},
|
|
}
|
|
}
|
|
|
|
func NewConfirmationModelWithAction(cfg *config.Config, log logger.Logger, parent tea.Model, title, message string, onConfirm func() (tea.Model, tea.Cmd)) ConfirmationModel {
|
|
return ConfirmationModel{
|
|
config: cfg,
|
|
logger: log,
|
|
parent: parent,
|
|
title: title,
|
|
message: message,
|
|
choices: []string{"Yes", "No"},
|
|
onConfirm: onConfirm,
|
|
}
|
|
}
|
|
|
|
func (m ConfirmationModel) Init() tea.Cmd {
|
|
// Auto-confirm in debug/auto-confirm mode
|
|
if m.config.TUIAutoConfirm {
|
|
if m.onConfirm != nil {
|
|
return func() tea.Msg {
|
|
return autoConfirmMsg{}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// autoConfirmMsg triggers automatic confirmation
|
|
type autoConfirmMsg struct{}
|
|
|
|
func (m ConfirmationModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case autoConfirmMsg:
|
|
// Auto-confirm triggered
|
|
if m.onConfirm != nil {
|
|
return m.onConfirm()
|
|
}
|
|
// Default fallback (should not be reached if onConfirm is always provided)
|
|
ctx := m.ctx
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
executor := NewBackupExecution(m.config, m.logger, m.parent, ctx, "cluster", "", 0)
|
|
return executor, executor.Init()
|
|
|
|
case tea.InterruptMsg:
|
|
// Handle Ctrl+C signal (SIGINT) - Bubbletea v1.3+ sends this instead of KeyMsg for ctrl+c
|
|
return m.parent, nil
|
|
|
|
case tea.KeyMsg:
|
|
// Auto-forward ESC/quit in auto-confirm mode
|
|
if m.config.TUIAutoConfirm {
|
|
return m.parent, tea.Quit
|
|
}
|
|
switch msg.String() {
|
|
case "ctrl+c", "q", "esc", "n":
|
|
return m.parent, nil
|
|
|
|
case "left", "h":
|
|
if m.cursor > 0 {
|
|
m.cursor--
|
|
}
|
|
|
|
case "right", "l":
|
|
if m.cursor < len(m.choices)-1 {
|
|
m.cursor++
|
|
}
|
|
|
|
case "enter", "y":
|
|
if msg.String() == "y" || m.cursor == 0 {
|
|
// Execute the onConfirm callback if provided
|
|
if m.onConfirm != nil {
|
|
return m.onConfirm()
|
|
}
|
|
// Default fallback (should not be reached if onConfirm is always provided)
|
|
ctx := m.ctx
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
executor := NewBackupExecution(m.config, m.logger, m, ctx, "cluster", "", 0)
|
|
return executor, executor.Init()
|
|
}
|
|
return m.parent, nil
|
|
}
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func (m ConfirmationModel) View() string {
|
|
var s strings.Builder
|
|
|
|
header := titleStyle.Render(m.title)
|
|
s.WriteString(fmt.Sprintf("\n%s\n\n", header))
|
|
|
|
s.WriteString(fmt.Sprintf("%s\n\n", m.message))
|
|
|
|
// Show choices
|
|
for i, choice := range m.choices {
|
|
cursor := " "
|
|
if m.cursor == i {
|
|
cursor = ">"
|
|
s.WriteString(selectedStyle.Render(fmt.Sprintf("%s [%s]", cursor, choice)))
|
|
} else {
|
|
s.WriteString(fmt.Sprintf("%s [%s]", cursor, choice))
|
|
}
|
|
s.WriteString(" ")
|
|
}
|
|
|
|
s.WriteString("\n\n[KEYS] <-/->: Select | Enter/y: Confirm | n/ESC: Cancel\n")
|
|
|
|
return s.String()
|
|
}
|
|
|
|
func (m ConfirmationModel) IsConfirmed() bool {
|
|
return m.confirmed
|
|
}
|