From c2218d741e7f09b95e8fdbf404ec181eac402399 Mon Sep 17 00:00:00 2001 From: Pranchal Shah Date: Sun, 30 Mar 2025 00:13:50 -0400 Subject: [PATCH 1/4] basic bubbletea working --- go.mod | 14 +++++ go.sum | 31 ++++++++++ pkg/plugin/util/bubbletea.go | 112 +++++++++++++++++++++++++++++++++++ pkg/plugins/golang/v4/api.go | 12 ++-- 4 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 pkg/plugin/util/bubbletea.go diff --git a/go.mod b/go.mod index 3d544e5321d..76c0321482a 100644 --- a/go.mod +++ b/go.mod @@ -18,14 +18,28 @@ require ( ) require ( + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/bubbletea v1.3.4 // indirect + github.com/charmbracelet/lipgloss v1.0.0 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/stretchr/testify v1.9.0 // indirect golang.org/x/mod v0.24.0 // indirect diff --git a/go.sum b/go.sum index 190e18967d7..803d61e962c 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,21 @@ +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= +github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -24,6 +36,20 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0= github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= @@ -32,6 +58,9 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= @@ -59,7 +88,9 @@ golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= diff --git a/pkg/plugin/util/bubbletea.go b/pkg/plugin/util/bubbletea.go new file mode 100644 index 00000000000..c49cf829f77 --- /dev/null +++ b/pkg/plugin/util/bubbletea.go @@ -0,0 +1,112 @@ +package util + +import ( + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +// Define styles +var ( + questionStyle = lipgloss.NewStyle().Bold(true) + selectedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF4D8B")) + helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")) +) + +// YesNoModel is a Bubbletea model for yes/no prompts +type YesNoModel struct { + Question string + Cursor int + Options []string + Selected bool + Result bool +} + +func (m YesNoModel) Init() tea.Cmd { + return nil +} + +func (m YesNoModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "up", "k", "left", "h": + if m.Cursor > 0 { + m.Cursor-- + } + case "down", "j", "right", "l": + if m.Cursor < len(m.Options)-1 { + m.Cursor++ + } + case "enter", " ": + m.Selected = true + m.Result = m.Cursor == 0 // Yes is at index 0 + return m, tea.Quit + case "q", "ctrl+c", "esc": + return m, tea.Quit + } + } + return m, nil +} + +func (m YesNoModel) View() string { + if m.Selected { + return "" + } + + s := "\n " + questionStyle.Render(m.Question) + "\n\n" + + for i, option := range m.Options { + cursor := " " + style := lipgloss.NewStyle() + + if m.Cursor == i { + cursor = "›" + style = selectedStyle + + // Add checkbox for visual feedback + option = fmt.Sprintf("[x] %s", option) + } else { + option = fmt.Sprintf("[ ] %s", option) + } + + s += fmt.Sprintf(" %s %s\n", cursor, style.Render(option)) + } + + s += "\n " + helpStyle.Render("j/k: up/down • enter: select • q: quit") + "\n" + + return s +} + +// BubbleTeaYesNo prompts the user with a yes/no question using Bubbletea UI +func BubbleTeaYesNo(question string) bool { + model := YesNoModel{ + Question: question, + Options: []string{"Yes", "No"}, + Cursor: 0, + } + + p := tea.NewProgram(model) + finalModel, err := p.Run() + if err != nil { + // Fallback to simple prompt if Bubbletea fails + fmt.Printf("%s [y/n] ", question) + var response string + fmt.Scanln(&response) + return strings.ToLower(response) == "y" + } + + m, ok := finalModel.(YesNoModel) + if !ok { + return false + } + + // If the user quit without selecting, default to false + if !m.Selected { + return false + } + + return m.Result +} diff --git a/pkg/plugins/golang/v4/api.go b/pkg/plugins/golang/v4/api.go index ac690f46a31..c6bb84a066e 100644 --- a/pkg/plugins/golang/v4/api.go +++ b/pkg/plugins/golang/v4/api.go @@ -17,12 +17,12 @@ limitations under the License. package v4 import ( - "bufio" + // "bufio" "errors" "fmt" "os" - log "github.com/sirupsen/logrus" + // log "github.com/sirupsen/logrus" "github.com/spf13/pflag" "sigs.k8s.io/kubebuilder/v4/pkg/config" @@ -126,14 +126,12 @@ func (p *createAPISubcommand) InjectConfig(c config.Config) error { func (p *createAPISubcommand) InjectResource(res *resource.Resource) error { p.resource = res - reader := bufio.NewReader(os.Stdin) if !p.resourceFlag.Changed { - log.Println("Create Resource [y/n]") - p.options.DoAPI = util.YesNo(reader) + p.options.DoAPI = util.BubbleTeaYesNo("Create Resource") } + if !p.controllerFlag.Changed { - log.Println("Create Controller [y/n]") - p.options.DoController = util.YesNo(reader) + p.options.DoController = util.BubbleTeaYesNo("Create Controller") } // Ensure that external API options cannot be used when creating an API in the project. From 08b78a9fdba8f763417f55ca10f89f6931004543 Mon Sep 17 00:00:00 2001 From: Pranchal Shah Date: Sun, 30 Mar 2025 00:16:52 -0400 Subject: [PATCH 2/4] little cleaner --- pkg/plugin/util/bubbletea.go | 81 +++++++++++++----------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/pkg/plugin/util/bubbletea.go b/pkg/plugin/util/bubbletea.go index c49cf829f77..636a6d1f4ab 100644 --- a/pkg/plugin/util/bubbletea.go +++ b/pkg/plugin/util/bubbletea.go @@ -2,26 +2,15 @@ package util import ( "fmt" - "strings" - tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) -// Define styles -var ( - questionStyle = lipgloss.NewStyle().Bold(true) - selectedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FF4D8B")) - helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")) -) - // YesNoModel is a Bubbletea model for yes/no prompts type YesNoModel struct { Question string Cursor int - Options []string Selected bool - Result bool } func (m YesNoModel) Init() tea.Cmd { @@ -32,17 +21,20 @@ func (m YesNoModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { - case "up", "k", "left", "h": - if m.Cursor > 0 { - m.Cursor-- - } - case "down", "j", "right", "l": - if m.Cursor < len(m.Options)-1 { - m.Cursor++ - } + case "left", "h", "up", "k": + m.Cursor = 0 // Yes + case "right", "l", "down", "j": + m.Cursor = 1 // No + case "y", "Y": + m.Cursor = 0 + m.Selected = true + return m, tea.Quit + case "n", "N": + m.Cursor = 1 + m.Selected = true + return m, tea.Quit case "enter", " ": m.Selected = true - m.Result = m.Cursor == 0 // Yes is at index 0 return m, tea.Quit case "q", "ctrl+c", "esc": return m, tea.Quit @@ -56,57 +48,42 @@ func (m YesNoModel) View() string { return "" } - s := "\n " + questionStyle.Render(m.Question) + "\n\n" - - for i, option := range m.Options { - cursor := " " - style := lipgloss.NewStyle() - - if m.Cursor == i { - cursor = "›" - style = selectedStyle - - // Add checkbox for visual feedback - option = fmt.Sprintf("[x] %s", option) - } else { - option = fmt.Sprintf("[ ] %s", option) - } - - s += fmt.Sprintf(" %s %s\n", cursor, style.Render(option)) + question := lipgloss.NewStyle().Bold(true).Render(m.Question) + yes := "[ ] Yes" + no := "[ ] No" + + // Mark selected option + if m.Cursor == 0 { + yes = "[x] " + lipgloss.NewStyle().Foreground(lipgloss.Color("#FF4D8B")).Render("Yes") + } else { + no = "[x] " + lipgloss.NewStyle().Foreground(lipgloss.Color("#FF4D8B")).Render("No") } - - s += "\n " + helpStyle.Render("j/k: up/down • enter: select • q: quit") + "\n" - - return s + + return fmt.Sprintf("\n %s\n\n › %s\n › %s\n\n %s\n", + question, yes, no, + lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render("← →: select • enter: choose • y/n: quick select")) } // BubbleTeaYesNo prompts the user with a yes/no question using Bubbletea UI func BubbleTeaYesNo(question string) bool { model := YesNoModel{ Question: question, - Options: []string{"Yes", "No"}, - Cursor: 0, + Cursor: 0, // Default to Yes } p := tea.NewProgram(model) finalModel, err := p.Run() if err != nil { - // Fallback to simple prompt if Bubbletea fails fmt.Printf("%s [y/n] ", question) var response string fmt.Scanln(&response) - return strings.ToLower(response) == "y" + return response == "y" || response == "Y" } m, ok := finalModel.(YesNoModel) - if !ok { - return false - } - - // If the user quit without selecting, default to false - if !m.Selected { + if !ok || !m.Selected { return false } - return m.Result + return m.Cursor == 0 // Return true if Yes (index 0) was selected } From 598966e04c48938c550ae63ee45cb311826253c2 Mon Sep 17 00:00:00 2001 From: Pranchal Shah Date: Sun, 30 Mar 2025 20:50:51 -0400 Subject: [PATCH 3/4] better docs --- pkg/plugin/util/bubbletea.go | 107 ++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/pkg/plugin/util/bubbletea.go b/pkg/plugin/util/bubbletea.go index 636a6d1f4ab..0e6d1bec2f8 100644 --- a/pkg/plugin/util/bubbletea.go +++ b/pkg/plugin/util/bubbletea.go @@ -6,35 +6,41 @@ import ( "github.com/charmbracelet/lipgloss" ) -// YesNoModel is a Bubbletea model for yes/no prompts +// Color constants +const ( + HighlightColor = "#FF4D8B" + HelpTextColor = "#626262" +) + +// YesNoModel represents a simple yes/no selection prompt. +// It implements the tea.Model interface for use with Bubble Tea. type YesNoModel struct { + // Question is the prompt text shown to the user Question string - Cursor int - Selected bool + // Choice tracks the selection (true = Yes, false = No) + Choice bool + // Done indicates whether the selection has been confirmed + Done bool } +// Init fulfills the tea.Model interface but doesn't need to do anything +// for this simple model. func (m YesNoModel) Init() tea.Cmd { return nil } +// Update handles user input and updates the model state accordingly. +// It processes keystrokes to navigate between options and confirm selections. func (m YesNoModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { - case "left", "h", "up", "k": - m.Cursor = 0 // Yes - case "right", "l", "down", "j": - m.Cursor = 1 // No - case "y", "Y": - m.Cursor = 0 - m.Selected = true - return m, tea.Quit - case "n", "N": - m.Cursor = 1 - m.Selected = true - return m, tea.Quit + case "y", "Y", "left", "up": + m.Choice = true + case "n", "N", "right", "down": + m.Choice = false case "enter", " ": - m.Selected = true + m.Done = true return m, tea.Quit case "q", "ctrl+c", "esc": return m, tea.Quit @@ -43,37 +49,63 @@ func (m YesNoModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, nil } +// View renders the current state of the model as a string. +// When done, it shows the final selection instead of clearing the screen. func (m YesNoModel) View() string { - if m.Selected { - return "" + if m.Done { + result := "No" + if m.Choice { + result = "Yes" + } + return fmt.Sprintf("%s: %s", m.Question, result) } - question := lipgloss.NewStyle().Bold(true).Render(m.Question) + // Style definitions + highlightStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(HighlightColor)) + boldStyle := lipgloss.NewStyle().Bold(true) + helpStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(HelpTextColor)) + + // Prepare options based on current selection yes := "[ ] Yes" no := "[ ] No" - - // Mark selected option - if m.Cursor == 0 { - yes = "[x] " + lipgloss.NewStyle().Foreground(lipgloss.Color("#FF4D8B")).Render("Yes") + if m.Choice { + yes = "[x] " + highlightStyle.Render("Yes") } else { - no = "[x] " + lipgloss.NewStyle().Foreground(lipgloss.Color("#FF4D8B")).Render("No") + no = "[x] " + highlightStyle.Render("No") } - - return fmt.Sprintf("\n %s\n\n › %s\n › %s\n\n %s\n", - question, yes, no, - lipgloss.NewStyle().Foreground(lipgloss.Color("#626262")).Render("← →: select • enter: choose • y/n: quick select")) + + // Render the full prompt + return fmt.Sprintf("\n %s\n\n › %s\n › %s\n\n %s\n", + boldStyle.Render(m.Question), + yes, no, + helpStyle.Render("y/n: select • enter: confirm")) } -// BubbleTeaYesNo prompts the user with a yes/no question using Bubbletea UI +// BubbleTeaYesNo presents a user with a yes/no prompt using Bubble Tea UI. +// +// It displays an interactive prompt with the given question and returns +// the user's selection as a boolean (true = Yes, false = No). +// +// If Bubble Tea encounters an error, it falls back to a simple terminal prompt. +// +// Example usage: +// +// if util.BubbleTeaYesNo("Create Resource?") { +// // User selected "Yes" +// } else { +// // User selected "No" +// } func BubbleTeaYesNo(question string) bool { model := YesNoModel{ Question: question, - Cursor: 0, // Default to Yes + Choice: true, // Default to Yes } - p := tea.NewProgram(model) + p := tea.NewProgram(model) finalModel, err := p.Run() + if err != nil { + // Fallback to simple prompt if error fmt.Printf("%s [y/n] ", question) var response string fmt.Scanln(&response) @@ -81,9 +113,18 @@ func BubbleTeaYesNo(question string) bool { } m, ok := finalModel.(YesNoModel) - if !ok || !m.Selected { + if !ok || !m.Done { + // User quit without confirming - print the result + fmt.Printf("%s: No (cancelled)\n", question) return false } - return m.Cursor == 0 // Return true if Yes (index 0) was selected + // User made a selection - print the result + result := "No" + if m.Choice { + result = "Yes" + } + fmt.Printf("%s: %s\n", question, result) + + return m.Choice } From 70e6c69e0bc9b8a440eda18f9b8c47435ad6faef Mon Sep 17 00:00:00 2001 From: Pranchal Shah Date: Sun, 30 Mar 2025 21:15:40 -0400 Subject: [PATCH 4/4] remove comments --- pkg/plugins/golang/v4/api.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/plugins/golang/v4/api.go b/pkg/plugins/golang/v4/api.go index c6bb84a066e..c17e46719a1 100644 --- a/pkg/plugins/golang/v4/api.go +++ b/pkg/plugins/golang/v4/api.go @@ -17,12 +17,10 @@ limitations under the License. package v4 import ( - // "bufio" "errors" "fmt" "os" - // log "github.com/sirupsen/logrus" "github.com/spf13/pflag" "sigs.k8s.io/kubebuilder/v4/pkg/config"