mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-08-20 01:07:02 +00:00
214 lines
5.7 KiB
Go
214 lines
5.7 KiB
Go
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/bubbles/textinput"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
// genericQuestionPage represents a page that asks a generic question
|
|
type genericQuestionPage struct {
|
|
genericInput textinput.Model
|
|
section YAMLPrompt
|
|
}
|
|
|
|
func (g genericQuestionPage) Init() tea.Cmd {
|
|
return textinput.Blink
|
|
}
|
|
|
|
func (g genericQuestionPage) Update(msg tea.Msg) (Page, tea.Cmd) {
|
|
var cmd tea.Cmd
|
|
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "enter":
|
|
if g.genericInput.Value() == "" && g.section.IfEmpty != "" {
|
|
// If the input is empty and IfEmpty is set, use IfEmpty value
|
|
g.genericInput.SetValue(g.section.IfEmpty)
|
|
}
|
|
// Now if the input is not empty, we can proceed
|
|
if g.genericInput.Value() != "" {
|
|
mainModel.log.Info("Setting value", g.genericInput.Value(), "for section:", g.section.YAMLSection)
|
|
setValueForSectionInMainModel(g.genericInput.Value(), g.section.YAMLSection)
|
|
return g, func() tea.Msg { return GoToPageMsg{PageID: "customization"} }
|
|
}
|
|
case "esc":
|
|
// Go back to customization page
|
|
return g, func() tea.Msg { return GoToPageMsg{PageID: "customization"} }
|
|
}
|
|
}
|
|
|
|
g.genericInput, cmd = g.genericInput.Update(msg)
|
|
|
|
return g, cmd
|
|
}
|
|
|
|
func (g genericQuestionPage) View() string {
|
|
s := g.section.Prompt + "\n\n"
|
|
s += g.genericInput.View() + "\n\n"
|
|
|
|
return s
|
|
}
|
|
|
|
func (g genericQuestionPage) Title() string {
|
|
return idFromSection(g.section)
|
|
}
|
|
|
|
func (g genericQuestionPage) Help() string {
|
|
return "Press Enter to submit your answer, or esc to cancel."
|
|
}
|
|
|
|
func (g genericQuestionPage) ID() string {
|
|
return idFromSection(g.section)
|
|
}
|
|
|
|
func idFromSection(section YAMLPrompt) string {
|
|
// Generate a unique ID based on the section's YAMLSection.
|
|
// This could be a simple hash or just the section name.
|
|
return strings.Replace(section.YAMLSection, ".", "_", -1)
|
|
}
|
|
|
|
func (g genericQuestionPage) Configured() bool {
|
|
// Check if the section has been configured in mainModel.extraFields
|
|
_, exists := getValueForSectionInMainModel(g.section.YAMLSection)
|
|
return exists
|
|
}
|
|
|
|
// newGenericQuestionPage initializes a new generic question page with a text input Model.
|
|
// Uses the provided section to set up the input Model.
|
|
func newGenericQuestionPage(section YAMLPrompt) *genericQuestionPage {
|
|
genericInput := textinput.New()
|
|
genericInput.Placeholder = section.PlaceHolder
|
|
genericInput.Width = 120
|
|
genericInput.Focus()
|
|
|
|
return &genericQuestionPage{
|
|
genericInput: genericInput,
|
|
section: section,
|
|
}
|
|
}
|
|
|
|
// genericBoolPage represents a page that asks a generic yes/no question
|
|
type genericBoolPage struct {
|
|
cursor int
|
|
options []string
|
|
section YAMLPrompt
|
|
}
|
|
|
|
func newGenericBoolPage(section YAMLPrompt) *genericBoolPage {
|
|
return &genericBoolPage{
|
|
options: []string{"Yes", "No"},
|
|
cursor: 1, // Default to "No"
|
|
section: section,
|
|
}
|
|
}
|
|
|
|
func (g *genericBoolPage) Title() string {
|
|
return idFromSection(g.section)
|
|
}
|
|
|
|
func (g *genericBoolPage) Help() string {
|
|
return genericNavigationHelp
|
|
}
|
|
|
|
func (g *genericBoolPage) ID() string {
|
|
return idFromSection(g.section)
|
|
}
|
|
|
|
func (g *genericBoolPage) Init() tea.Cmd {
|
|
return nil
|
|
}
|
|
|
|
func (g *genericBoolPage) Update(msg tea.Msg) (Page, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "up", "k":
|
|
g.cursor = 0
|
|
case "down", "j":
|
|
g.cursor = 1
|
|
case "enter":
|
|
// in both cases we just go back to customization
|
|
// Save the value to mainModel.extraFields
|
|
mainModel.log.Infof("Setting value %s for section %s:", g.options[g.cursor], g.section.YAMLSection)
|
|
setValueForSectionInMainModel(g.options[g.cursor], g.section.YAMLSection)
|
|
return g, func() tea.Msg { return GoToPageMsg{PageID: "customization"} }
|
|
}
|
|
}
|
|
return g, nil
|
|
}
|
|
|
|
func (g *genericBoolPage) View() string {
|
|
s := g.section.Prompt + "\n\n"
|
|
|
|
for i, option := range g.options {
|
|
cursor := " "
|
|
if g.cursor == i {
|
|
cursor = lipgloss.NewStyle().Foreground(kairosAccent).Render(">")
|
|
}
|
|
s += fmt.Sprintf("%s %s\n", cursor, option)
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (g *genericBoolPage) Configured() bool {
|
|
// Check if the section has been configured in mainModel.extraFields
|
|
_, exists := getValueForSectionInMainModel(g.section.YAMLSection)
|
|
return exists
|
|
}
|
|
|
|
// setValueForSectionInMainModel sets a value in the mainModel's extraFields map
|
|
// for a given section, which is specified as a dot-separated string.
|
|
// It creates nested maps as necessary to reach the specified section.
|
|
func setValueForSectionInMainModel(value string, section string) {
|
|
sections := strings.Split(section, ".")
|
|
// Transform "Yes" to "true" and "No" to "false"
|
|
if value == "Yes" {
|
|
value = "true"
|
|
} else if value == "No" {
|
|
value = "false"
|
|
}
|
|
// Ensure mainModel.extraFields is initialized
|
|
if mainModel.extraFields == nil {
|
|
mainModel.extraFields = make(map[string]interface{})
|
|
}
|
|
|
|
currentMap := mainModel.extraFields
|
|
for i, key := range sections {
|
|
if i == len(sections)-1 {
|
|
currentMap[key] = value
|
|
} else {
|
|
if nextMap, ok := currentMap[key].(map[string]interface{}); ok {
|
|
currentMap = nextMap
|
|
} else {
|
|
newMap := make(map[string]interface{})
|
|
currentMap[key] = newMap
|
|
currentMap = newMap
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// getValueForSectionInMainModel retrieves a value from the mainModel's extraFields map
|
|
// for a given section, which is specified as a dot-separated string.
|
|
func getValueForSectionInMainModel(section string) (string, bool) {
|
|
sections := strings.Split(section, ".")
|
|
currentMap := mainModel.extraFields
|
|
|
|
for _, key := range sections {
|
|
if nextMap, ok := currentMap[key].(map[string]interface{}); ok {
|
|
currentMap = nextMap
|
|
} else if value, ok := currentMap[key]; ok {
|
|
return fmt.Sprintf("%v", value), true
|
|
} else {
|
|
return "", false
|
|
}
|
|
}
|
|
return "", false
|
|
}
|