From 09cf052b8adce2ea4d0f83202eadab0379568665 Mon Sep 17 00:00:00 2001 From: Rob Morgan Date: Mon, 26 Aug 2024 19:05:41 +0800 Subject: [PATCH 1/5] start using bubbletea --- cmd/cli/completion.go | 4 +- cmd/cli/root.go | 95 +------------ cmd/cli/term.go | 12 ++ cmd/cli/ui.go | 304 ++++++++++++++++++++++++++++++++++++++++++ go.mod | 26 +++- go.sum | 50 +++++++ 6 files changed, 393 insertions(+), 98 deletions(-) create mode 100644 cmd/cli/term.go create mode 100644 cmd/cli/ui.go diff --git a/cmd/cli/completion.go b/cmd/cli/completion.go index c305866..25155d0 100644 --- a/cmd/cli/completion.go +++ b/cmd/cli/completion.go @@ -21,7 +21,7 @@ type oaiClients struct { openAIClient openai.Client } -func newOAIClients() (oaiClients, error) { +func newOAIClients() oaiClients { var config openai.ClientConfig config = openai.DefaultConfig(*openAIAPIKey) @@ -45,7 +45,7 @@ func newOAIClients() (oaiClients, error) { clients := oaiClients{ openAIClient: *openai.NewClientWithConfig(config), } - return clients, nil + return clients } func gptCompletion(ctx context.Context, client oaiClients, prompts []string) (string, error) { diff --git a/cmd/cli/root.go b/cmd/cli/root.go index d4fca8b..d325e24 100644 --- a/cmd/cli/root.go +++ b/cmd/cli/root.go @@ -7,9 +7,7 @@ import ( "os/signal" "strconv" - "github.com/charmbracelet/glamour" - "github.com/janeczku/go-spinner" - "github.com/manifoldco/promptui" + tea "github.com/charmbracelet/bubbletea" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" flag "github.com/spf13/pflag" @@ -17,12 +15,6 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" ) -const ( - apply = "Apply" - dontApply = "Don't Apply" - reprompt = "Reprompt" -) - var ( openaiAPIURLv1 = "https://api.openai.com/v1" version = "dev" @@ -96,90 +88,9 @@ func run(args []string) error { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() - oaiClients, err := newOAIClients() - if err != nil { + if _, err := tea.NewProgram(newModel(args), tea.WithContext(ctx)).Run(); err != nil { return err } - var action, completion string - for action != apply { - args = append(args, action) - - s := spinner.NewSpinner("Processing...") - if !*debug && !*raw { - s.SetCharset([]string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}) - s.Start() - } - - completion, err = gptCompletion(ctx, oaiClients, args) - if err != nil { - return err - } - - s.Stop() - - if *raw { - completion = trimTicks(completion) - fmt.Println(completion) - return nil - } - - text := fmt.Sprintf("✨ Attempting to apply the following manifest:\n%s", completion) - r, err := glamour.NewTermRenderer(glamour.WithAutoStyle()) - if err != nil { - return err - } - out, err := r.Render(text) - if err != nil { - return err - } - fmt.Print(out) - // remove unnessary backticks if they are in the output - completion = trimTicks(completion) - - action, err = userActionPrompt() - if err != nil { - return err - } - - if action == dontApply { - return nil - } - } - - return applyManifest(completion) -} - -func userActionPrompt() (string, error) { - // if require confirmation is not set, immediately return apply - if !*requireConfirmation { - return apply, nil - } - - var result string - var err error - items := []string{apply, dontApply} - currentContext, err := getCurrentContextName() - label := fmt.Sprintf("Would you like to apply this? [%[1]s/%[2]s/%[3]s]", reprompt, apply, dontApply) - if err == nil { - label = fmt.Sprintf("(context: %[1]s) %[2]s", currentContext, label) - } - - prompt := promptui.SelectWithAdd{ - Label: label, - Items: items, - AddLabel: reprompt, - } - _, result, err = prompt.Run() - if err != nil { - // workaround for bug in promptui when input is piped in from stdin - // however, this will not block for ui input - // for now, we will not apply the yaml, but user can pipe to input to kubectl - if err.Error() == "^D" { - return dontApply, nil - } - return dontApply, err - } - - return result, nil + return nil } diff --git a/cmd/cli/term.go b/cmd/cli/term.go new file mode 100644 index 0000000..66bc1fd --- /dev/null +++ b/cmd/cli/term.go @@ -0,0 +1,12 @@ +package cli + +import ( + "os" + "sync" + + "github.com/charmbracelet/x/term" +) + +var isInputTTY = sync.OnceValue(func() bool { + return term.IsTerminal(os.Stdin.Fd()) +}) diff --git a/cmd/cli/ui.go b/cmd/cli/ui.go new file mode 100644 index 0000000..c30bc46 --- /dev/null +++ b/cmd/cli/ui.go @@ -0,0 +1,304 @@ +package cli + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "math" + "net/http" + "os" + "strings" + "time" + + "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/spinner" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/lipgloss" + openai "github.com/sashabaranov/go-openai" + log "github.com/sirupsen/logrus" +) + +const listHeight = 14 + +var ( + titleStyle = lipgloss.NewStyle().MarginLeft(2) + itemStyle = lipgloss.NewStyle().PaddingLeft(4) + selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) + paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) + helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) + quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4) +) + +type item string + +func (i item) FilterValue() string { return "" } + +type itemDelegate struct{} + +func (d itemDelegate) Height() int { return 1 } +func (d itemDelegate) Spacing() int { return 0 } +func (d itemDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil } +func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { + i, ok := listItem.(item) + if !ok { + return + } + + str := fmt.Sprintf("%d. %s", index+1, i) + + fn := itemStyle.Render + if index == m.Index() { + fn = func(s ...string) string { + return selectedItemStyle.Render("> " + strings.Join(s, " ")) + } + } + + fmt.Fprint(w, fn(str)) +} + +type model struct { + list list.Model + choice string + spinner spinner.Model + glam *glamour.TermRenderer + renderedYaml string + + pipedInput string + promptArgs []string + completion string + retries int + oaiClients oaiClients + cancelRequest context.CancelFunc + + config *config +} + +type config struct { + maxRetries int +} + +func defaultConfig() *config { + return &config{ + maxRetries: maxRetries, + } +} + +func newModel(promptArgs []string) model { + s := spinner.New(spinner.WithSpinner(spinner.Dot)) + + items := []list.Item{ + item("Apply"), + item("Don't Apply"), + item("Reprompt"), + } + + const defaultWidth = 20 + + l := list.New(items, itemDelegate{}, defaultWidth, listHeight) + l.Title = "Would you like to apply this?" + l.SetShowStatusBar(false) + l.SetFilteringEnabled(false) + l.Styles.Title = titleStyle + l.Styles.PaginationStyle = paginationStyle + l.Styles.HelpStyle = helpStyle + + // Discard the error, because we can recover from it by falling back to the default style. + gr, _ := glamour.NewTermRenderer(glamour.WithAutoStyle()) + + return model{ + config: defaultConfig(), + oaiClients: newOAIClients(), + spinner: s, + glam: gr, + list: l, + promptArgs: promptArgs, + } +} + +// Init implements tea.Model. +func (m model) Init() tea.Cmd { + return tea.Batch(m.spinner.Tick, m.readStdinCmd()) +} + +// Update implements tea.Model. +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var ( + cmds []tea.Cmd + listCmd tea.Cmd + ) + + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.list.SetWidth(msg.Width) + return m, nil + case tea.KeyMsg: + switch msg.String() { + case "ctrl+c", "q": + return m, tea.Quit + case "enter": + i, ok := m.list.SelectedItem().(item) + if ok { + m.choice = string(i) + } + return m, tea.Quit + } + case completionInput: + return m, m.startCompletionCmd(msg.content) + case completionOutput: + // update the model with the latest completion + m.completion = msg.content + + // TODO - we'll probably handle the raw flag here. + // if *raw { + // fmt.Println(msg.content) + // return nil + // } + + renderedYaml, err := m.glam.Render(msg.content) + if err != nil { + // TODO - maybe return a uiError here instead, so we can abort + renderedYaml = fmt.Sprintf("There was an error rendering the completion: %s", err) + } + m.renderedYaml = renderedYaml + case uiError: + // TODO - should I add the error rather than return? + // TODO - ensure the state is set to show any errors + //return m, m.errorCmd(msg.err, msg.reason) + case spinner.TickMsg: + var cmd tea.Cmd + m.spinner, cmd = m.spinner.Update(msg) + cmds = append(cmds, cmd) + } + + m.list, listCmd = m.list.Update(msg) + cmds = append(cmds, listCmd) + return m, tea.Batch(cmds...) +} + +// View implements tea.Model. +func (m model) View() string { + s := strings.Builder{} + + s.WriteString("✨ Attempting to apply the following manifest:\n") + + s.WriteString(m.spinner.View() + "Processing..." + "\n") + + s.WriteString(m.renderedYaml + "\n") + + s.WriteString(m.list.View()) + + // currentContext, err := getCurrentContextName() + // label := fmt.Sprintf("Would you like to apply this? [%[1]s/%[2]s/%[3]s]", reprompt, apply, dontApply) + // if err == nil { + // label = fmt.Sprintf("(context: %[1]s) %[2]s", currentContext, label) + // } + + return s.String() +} + +// completionInput is a tea.Msg that wraps the content read from stdin. +type completionInput struct { + content string +} + +// completionOutput a tea.Msg that wraps the content returned from OpenAI. +type completionOutput struct { + content string +} + +// uiError is a wrapper around an error that adds additional context. +type uiError struct { + err error + reason string +} + +func (u uiError) Error() string { + return u.err.Error() +} + +func (u uiError) Reason() string { + return u.reason +} + +func (m *model) readStdinCmd() tea.Cmd { + return func() tea.Msg { + if !isInputTTY() { + reader := bufio.NewReader(os.Stdin) + stdinBytes, err := io.ReadAll(reader) + if err != nil { + return uiError{err, "Unable to read stdin."} + } + + return completionInput{increaseIndent(string(stdinBytes))} + } + return completionInput{""} + } +} + +func (m *model) retry(content string, err uiError) tea.Msg { + m.retries++ + if m.retries >= m.config.maxRetries { + return err + } + wait := time.Millisecond * 100 * time.Duration(math.Pow(2, float64(m.retries))) //nolint:mnd + time.Sleep(wait) + return completionInput{content} +} + +func (m *model) startCompletionCmd(content string) tea.Cmd { + return func() tea.Msg { + temp := float32(*temperature) + var prompt strings.Builder + + // did we receive any piped input? + if m.pipedInput != "" { + fmt.Fprintf(&prompt, "Depending on the input, either edit or append to the input YAML. Do not generate new YAML without including the input YAML either original or edited.\nUse the following YAML as the input: \n%s\n", m.pipedInput) + } + + for _, p := range m.promptArgs { + fmt.Fprintf(&prompt, "%s", p) + } + + ctx, cancel := context.WithCancel(context.Background()) + m.cancelRequest = cancel + + resp, err := m.oaiClients.openaiGptChatCompletion(ctx, &prompt, temp) + if err != nil { + return m.handleRequestError(err, content) + } + + // TODO - do this later, because we need them in the output for glamour to render properly + // remove unnecessary backticks if they are in the output + //cleanedResp := trimTicks(resp) + + return completionOutput{content: resp} + } +} + +func (m *model) handleRequestError(err error, content string) tea.Msg { + ae := &openai.APIError{} + if errors.As(err, &ae) { + //return m.handleAPIError(ae, mod, content) + switch ae.HTTPStatusCode { + case http.StatusTooManyRequests, http.StatusRequestTimeout, http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: + log.Debugf("retrying due to status code %d: %s", ae.HTTPStatusCode, ae.Message) + //return retry.RetryableError(err) + return m.retry(content, uiError{err: err, reason: fmt.Sprintf("retrying due to status code %d: %s", ae.HTTPStatusCode, ae.Message)}) + } + } + return uiError{ae, fmt.Sprintf( + "There was a problem with the API request: %s", + err.Error(), + )} +} + +func increaseIndent(s string) string { + lines := strings.Split(s, "\n") + for i := 0; i < len(lines); i++ { + lines[i] = "\t" + lines[i] + } + return strings.Join(lines, "\n") +} diff --git a/go.mod b/go.mod index 6f669c2..c766290 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.22.0 toolchain go1.22.5 require ( + github.com/charmbracelet/bubbletea v0.26.6 github.com/charmbracelet/glamour v0.7.0 + github.com/charmbracelet/x/term v0.1.1 github.com/janeczku/go-spinner v0.0.0-20150530144529-cf8ef1d64394 github.com/manifoldco/promptui v0.9.0 github.com/sashabaranov/go-openai v1.27.1 @@ -19,9 +21,24 @@ require ( k8s.io/client-go v0.30.3 ) +require ( + github.com/charmbracelet/bubbles v0.18.0 // indirect + github.com/charmbracelet/lipgloss v0.9.1 // indirect + github.com/charmbracelet/x/ansi v0.1.2 // indirect + github.com/charmbracelet/x/input v0.1.0 // indirect + github.com/charmbracelet/x/windows v0.1.0 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect +) + require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/alecthomas/chroma/v2 v2.8.0 // indirect + github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect @@ -51,7 +68,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.18 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/microcosm-cc/bluemonday v1.0.25 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -63,7 +80,8 @@ require ( github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/yuin/goldmark v1.5.4 // indirect github.com/yuin/goldmark-emoji v1.0.2 // indirect @@ -71,8 +89,8 @@ require ( golang.org/x/crypto v0.21.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index ecba188..7beb158 100644 --- a/go.sum +++ b/go.sum @@ -8,13 +8,35 @@ github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1 github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 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/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= +github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/charmbracelet/x v0.1.0 h1:/MAmoZV+UIm4P9LxQBoQ2oK49wu/ewabHLFG4Gspc4c= +github.com/charmbracelet/x v0.1.0/go.mod h1:tfPuVPqtgK3n1KbwMw4oEq3Fruetob3nM14pFgMVFzs= +github.com/charmbracelet/x/ansi v0.1.0 h1:o4NbQQCoVgbLpD5RC1cI687baoLwrLZyCOTGlF0gne4= +github.com/charmbracelet/x/ansi v0.1.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= +github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= +github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4= +github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -22,6 +44,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -37,6 +61,8 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +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/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -123,10 +149,14 @@ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= @@ -138,6 +168,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +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/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= @@ -160,9 +196,13 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sashabaranov/go-openai v1.27.1 h1:7Nx6db5NXbcoutNmAUQulEQZEpHG/SkzfexP2X5RWMk= github.com/sashabaranov/go-openai v1.27.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= @@ -190,6 +230,8 @@ github.com/walles/env v0.0.4 h1:v+cQHLwlASHaybe9VPfRZsmHsdL9HNxfX1yvNkEQsno= github.com/walles/env v0.0.4/go.mod h1:YBVhW14DflZB4j6OO2hyHzjSi3cBDi4lzPXG45hfoTo= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -231,6 +273,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -238,10 +282,16 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= From 52dde89e3bed36378f4ab5c60f8a66050e25ef17 Mon Sep 17 00:00:00 2001 From: Rob Morgan Date: Wed, 28 Aug 2024 17:05:26 +0800 Subject: [PATCH 2/5] display loading sequence properly --- cmd/cli/ui.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/cmd/cli/ui.go b/cmd/cli/ui.go index c30bc46..872da6d 100644 --- a/cmd/cli/ui.go +++ b/cmd/cli/ui.go @@ -24,6 +24,7 @@ import ( const listHeight = 14 var ( + viewDefaultStyle = lipgloss.NewStyle().Padding(1, 0, 0, 2) titleStyle = lipgloss.NewStyle().MarginLeft(2) itemStyle = lipgloss.NewStyle().PaddingLeft(4) selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) @@ -60,6 +61,8 @@ func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list } type model struct { + loading bool + list list.Model choice string spinner spinner.Model @@ -146,8 +149,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Quit } case completionInput: + m.loading = true return m, m.startCompletionCmd(msg.content) case completionOutput: + m.loading = false + // update the model with the latest completion m.completion = msg.content @@ -182,12 +188,18 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) View() string { s := strings.Builder{} - s.WriteString("✨ Attempting to apply the following manifest:\n") - - s.WriteString(m.spinner.View() + "Processing..." + "\n") + if m.loading { + s.WriteString(m.spinner.View() + "Processing..." + "\n") + return s.String() + } - s.WriteString(m.renderedYaml + "\n") + contentView := lipgloss.JoinVertical( + lipgloss.Left, + viewDefaultStyle.Render("✨ Attempting to apply the following manifest:"), + m.renderedYaml+"\n", + ) + s.WriteString(contentView + "\n") s.WriteString(m.list.View()) // currentContext, err := getCurrentContextName() @@ -270,6 +282,9 @@ func (m *model) startCompletionCmd(content string) tea.Cmd { return m.handleRequestError(err, content) } + // trim the opening new line if it exists + resp = strings.TrimPrefix(resp, "\n") + // TODO - do this later, because we need them in the output for glamour to render properly // remove unnecessary backticks if they are in the output //cleanedResp := trimTicks(resp) From 9e11744350bc3bed86f6d05d1162b30df5985b65 Mon Sep 17 00:00:00 2001 From: Rob Morgan Date: Thu, 29 Aug 2024 17:22:43 +0800 Subject: [PATCH 3/5] finalize bubbletea rewrite --- cmd/cli/completion.go | 49 --------- cmd/cli/root.go | 48 +++++++- cmd/cli/term.go | 24 ++++ cmd/cli/ui.go | 251 +++++++++++++++++++++++++++++------------- 4 files changed, 245 insertions(+), 127 deletions(-) diff --git a/cmd/cli/completion.go b/cmd/cli/completion.go index 25155d0..0a279e8 100644 --- a/cmd/cli/completion.go +++ b/cmd/cli/completion.go @@ -1,18 +1,9 @@ package cli import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "os" "strings" - "time" openai "github.com/sashabaranov/go-openai" - "github.com/sethvargo/go-retry" - log "github.com/sirupsen/logrus" ) const maxRetries = 10 @@ -47,43 +38,3 @@ func newOAIClients() oaiClients { } return clients } - -func gptCompletion(ctx context.Context, client oaiClients, prompts []string) (string, error) { - temp := float32(*temperature) - var prompt strings.Builder - - // read from stdin - stat, _ := os.Stdin.Stat() - if (stat.Mode() & os.ModeCharDevice) == 0 { - stdin, err := io.ReadAll(os.Stdin) - if err != nil { - return "", err - } - fmt.Fprintf(&prompt, "Depending on the input, either edit or append to the input YAML. Do not generate new YAML without including the input YAML either original or edited.\nUse the following YAML as the input: \n%s\n", string(stdin)) - } - - for _, p := range prompts { - fmt.Fprintf(&prompt, "%s", p) - } - - var resp string - var err error - r := retry.WithMaxRetries(maxRetries, retry.NewExponential(1*time.Second)) - if err := retry.Do(ctx, r, func(ctx context.Context) error { - resp, err = client.openaiGptChatCompletion(ctx, &prompt, temp) - - requestErr := &openai.APIError{} - if errors.As(err, &requestErr) { - switch requestErr.HTTPStatusCode { - case http.StatusTooManyRequests, http.StatusRequestTimeout, http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: - log.Debugf("retrying due to status code %d: %s", requestErr.HTTPStatusCode, requestErr.Message) - return retry.RetryableError(err) - } - } - return nil - }); err != nil { - return "", err - } - - return resp, nil -} diff --git a/cmd/cli/root.go b/cmd/cli/root.go index d325e24..1fc812e 100644 --- a/cmd/cli/root.go +++ b/cmd/cli/root.go @@ -2,6 +2,7 @@ package cli import ( "context" + "errors" "fmt" "os" "os/signal" @@ -39,6 +40,7 @@ func InitAndExecute() { } if err := RootCmd().Execute(); err != nil { + handleError(err) os.Exit(1) } } @@ -88,9 +90,51 @@ func run(args []string) error { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() - if _, err := tea.NewProgram(newModel(args), tea.WithContext(ctx)).Run(); err != nil { - return err + var k8sContext string + currentContext, err := getCurrentContextName() + if err == nil { + log.Debugf("current-context: %s", currentContext) + k8sContext = currentContext + } + + p := tea.NewProgram(newModel(args, k8sContext, !*requireConfirmation), tea.WithContext(ctx)) + m, err := p.Run() + if err != nil { + return uiError{err, "Couldn't start Bubble Tea program."} + } + + model, ok := m.(model) + if !ok { + return fmt.Errorf("unexpected model type %T", m) + } else if model.error != nil { + return *model.error + } + + // Create a manifest from the last completion + manifest := trimTicks(model.completion) + + if model.state == apply || model.state == autoApply { + return applyManifest(manifest) } return nil } + +func handleError(err error) { + format := "\n%s\n\n" + + var args []interface{} + var merr uiError + + if errors.As(err, &merr) { + args = []interface{}{ + stderrStyles().ErrPadding.Render(stderrStyles().ErrorHeader.String(), merr.reason), + } + } else { + args = []interface{}{ + stderrStyles().ErrPadding.Render(stderrStyles().ErrorDetails.Render(err.Error())), + } + } + + fmt.Fprintf(os.Stderr, format, args...) +} diff --git a/cmd/cli/term.go b/cmd/cli/term.go index 66bc1fd..003600f 100644 --- a/cmd/cli/term.go +++ b/cmd/cli/term.go @@ -4,9 +4,33 @@ import ( "os" "sync" + "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/term" + "github.com/muesli/termenv" ) var isInputTTY = sync.OnceValue(func() bool { return term.IsTerminal(os.Stdin.Fd()) }) + +var stderrRenderer = sync.OnceValue(func() *lipgloss.Renderer { + return lipgloss.NewRenderer(os.Stderr, termenv.WithColorCache(true)) +}) + +var stderrStyles = sync.OnceValue(func() styles { + return makeStyles(stderrRenderer()) +}) + +type styles struct { + ErrorHeader, + ErrorDetails, + ErrPadding lipgloss.Style +} + +func makeStyles(r *lipgloss.Renderer) (s styles) { + const horizontalEdgePadding = 2 + s.ErrorHeader = r.NewStyle().Foreground(lipgloss.Color("#F1F1F1")).Background(lipgloss.Color("#FF5F87")).Bold(true).Padding(0, 1).SetString("ERROR") + s.ErrorDetails = r.NewStyle().Foreground(lipgloss.Color("#757575")) + s.ErrPadding = r.NewStyle().Padding(0, horizontalEdgePadding) + return s +} diff --git a/cmd/cli/ui.go b/cmd/cli/ui.go index 872da6d..72e51a0 100644 --- a/cmd/cli/ui.go +++ b/cmd/cli/ui.go @@ -11,9 +11,11 @@ import ( "os" "strings" "time" + "unicode" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/spinner" + "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/glamour" "github.com/charmbracelet/lipgloss" @@ -21,16 +23,26 @@ import ( log "github.com/sirupsen/logrus" ) -const listHeight = 14 +const ( + defaultWidth = 20 + tabWidth = 4 + listHeight = 8 + showList = "Show List" + showError = "Show Error" + apply = "Apply" + autoApply = "Auto Apply" + dontApply = "Don't Apply" + reprompt = "Reprompt" + rawOutput = "Raw Output" +) var ( - viewDefaultStyle = lipgloss.NewStyle().Padding(1, 0, 0, 2) - titleStyle = lipgloss.NewStyle().MarginLeft(2) + viewDefaultStyle = lipgloss.NewStyle().Margin(0, 2).PaddingTop(1) + titleStyle = lipgloss.NewStyle().MarginLeft(0) itemStyle = lipgloss.NewStyle().PaddingLeft(4) selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170")) - paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4) - helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1) - quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4) + helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(2).PaddingBottom(1) + contextStyle = lipgloss.NewStyle().PaddingLeft(2).MarginBottom(1).Foreground(lipgloss.Color("170")) ) type item string @@ -61,17 +73,21 @@ func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list } type model struct { + state string loading bool + error *uiError - list list.Model - choice string - spinner spinner.Model - glam *glamour.TermRenderer - renderedYaml string + spinner spinner.Model + glam *glamour.TermRenderer + list list.Model + choice string + textInput textinput.Model - pipedInput string + k8sContext string + autoApply bool promptArgs []string completion string + renderedYaml string retries int oaiClients oaiClients cancelRequest context.CancelFunc @@ -89,98 +105,146 @@ func defaultConfig() *config { } } -func newModel(promptArgs []string) model { +func newModel(promptArgs []string, k8sContext string, autoApply bool) model { s := spinner.New(spinner.WithSpinner(spinner.Dot)) items := []list.Item{ - item("Apply"), - item("Don't Apply"), - item("Reprompt"), + item(apply), + item(dontApply), + item(reprompt), } - const defaultWidth = 20 - l := list.New(items, itemDelegate{}, defaultWidth, listHeight) l.Title = "Would you like to apply this?" l.SetShowStatusBar(false) l.SetFilteringEnabled(false) l.Styles.Title = titleStyle - l.Styles.PaginationStyle = paginationStyle + l.SetShowPagination(false) l.Styles.HelpStyle = helpStyle - // Discard the error, because we can recover from it by falling back to the default style. + ti := textinput.New() + ti.Placeholder = "Enter your new prompt" + ti.CharLimit = 156 + ti.Width = 38 + + // Discard the error, because we are using the auto style which always exists. gr, _ := glamour.NewTermRenderer(glamour.WithAutoStyle()) return model{ + state: showList, config: defaultConfig(), oaiClients: newOAIClients(), spinner: s, glam: gr, list: l, + textInput: ti, promptArgs: promptArgs, + k8sContext: k8sContext, + autoApply: autoApply, } } // Init implements tea.Model. func (m model) Init() tea.Cmd { - return tea.Batch(m.spinner.Tick, m.readStdinCmd()) + return tea.Batch(m.spinner.Tick, textinput.Blink, m.readStdinCmd()) } // Update implements tea.Model. func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var ( - cmds []tea.Cmd - listCmd tea.Cmd + cmds []tea.Cmd + listCmd tea.Cmd + textInputCmd tea.Cmd ) switch msg := msg.(type) { case tea.WindowSizeMsg: m.list.SetWidth(msg.Width) return m, nil + case tea.KeyMsg: switch msg.String() { - case "ctrl+c", "q": - return m, tea.Quit + case "ctrl+c": + return m, m.quit case "enter": - i, ok := m.list.SelectedItem().(item) - if ok { - m.choice = string(i) + if m.state == showList { + i, ok := m.list.SelectedItem().(item) + if ok { + m.choice = string(i) + switch m.choice { + case apply: + m.state = apply + return m, m.quit + case dontApply: + m.state = dontApply + return m, m.quit + case reprompt: + m.textInput.SetValue("") + m.state = reprompt + cmds = append(cmds, m.textInput.Focus()) + } + } + } else if m.state == reprompt { + val := m.textInput.Value() + m.promptArgs = append(m.promptArgs, removeWhitespace(val)) + m.state = showList + m.loading = true + return m, m.startCompletionCmd(m.promptArgs) } - return m, tea.Quit } + case completionInput: m.loading = true - return m, m.startCompletionCmd(msg.content) + + if removeWhitespace(msg.content) != "" { + m.promptArgs = append(m.promptArgs, removeWhitespace(msg.content)) + } + + return m, m.startCompletionCmd(m.promptArgs) + case completionOutput: m.loading = false // update the model with the latest completion m.completion = msg.content - // TODO - we'll probably handle the raw flag here. - // if *raw { - // fmt.Println(msg.content) - // return nil - // } + // handle the raw flag + if *raw { + m.state = rawOutput + return m, m.quit + } - renderedYaml, err := m.glam.Render(msg.content) - if err != nil { - // TODO - maybe return a uiError here instead, so we can abort - renderedYaml = fmt.Sprintf("There was an error rendering the completion: %s", err) + return m, m.renderWithGlamour(msg.content) + + case renderedYamlMsg: + m.renderedYaml = string(msg) + + // handle the auto apply setting + if m.autoApply { + m.state = autoApply + return m, m.quit } - m.renderedYaml = renderedYaml + case uiError: - // TODO - should I add the error rather than return? - // TODO - ensure the state is set to show any errors - //return m, m.errorCmd(msg.err, msg.reason) + m.error = &msg + m.state = showError + return m, m.quit + case spinner.TickMsg: var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(msg) cmds = append(cmds, cmd) } - m.list, listCmd = m.list.Update(msg) - cmds = append(cmds, listCmd) + switch m.state { + case showList: + m.list, listCmd = m.list.Update(msg) + cmds = append(cmds, listCmd) + case reprompt: + m.textInput, textInputCmd = m.textInput.Update(msg) + cmds = append(cmds, textInputCmd) + } + return m, tea.Batch(cmds...) } @@ -188,6 +252,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) View() string { s := strings.Builder{} + if m.state == rawOutput { + s.WriteString(trimTicks(m.completion)) + return s.String() + } + if m.loading { s.WriteString(m.spinner.View() + "Processing..." + "\n") return s.String() @@ -196,17 +265,30 @@ func (m model) View() string { contentView := lipgloss.JoinVertical( lipgloss.Left, viewDefaultStyle.Render("✨ Attempting to apply the following manifest:"), - m.renderedYaml+"\n", + m.renderedYaml, ) s.WriteString(contentView + "\n") - s.WriteString(m.list.View()) - // currentContext, err := getCurrentContextName() - // label := fmt.Sprintf("Would you like to apply this? [%[1]s/%[2]s/%[3]s]", reprompt, apply, dontApply) - // if err == nil { - // label = fmt.Sprintf("(context: %[1]s) %[2]s", currentContext, label) - // } + if m.k8sContext != "" { + s.WriteString( + lipgloss.JoinHorizontal(lipgloss.Top, + contextStyle.Render("☸️ Context: "), + m.k8sContext, + ) + "\n", + ) + } + + if m.state == autoApply { + return s.String() + } + + if m.state == reprompt { + s.WriteString(m.textInput.View()) + return s.String() + } + + s.WriteString(m.list.View()) return s.String() } @@ -235,6 +317,13 @@ func (u uiError) Reason() string { return u.reason } +func (m *model) quit() tea.Msg { + if m.cancelRequest != nil { + m.cancelRequest() + } + return tea.Quit() +} + func (m *model) readStdinCmd() tea.Cmd { return func() tea.Msg { if !isInputTTY() { @@ -244,7 +333,11 @@ func (m *model) readStdinCmd() tea.Cmd { return uiError{err, "Unable to read stdin."} } - return completionInput{increaseIndent(string(stdinBytes))} + var prompt strings.Builder + pipedInput := string(stdinBytes) + fmt.Fprintf(&prompt, "Depending on the input, either edit or append to the input YAML. Do not generate new YAML without including the input YAML either original or edited.\nUse the following YAML as the input: \n%s\n", pipedInput) + + return completionInput{prompt.String()} } return completionInput{""} } @@ -260,18 +353,13 @@ func (m *model) retry(content string, err uiError) tea.Msg { return completionInput{content} } -func (m *model) startCompletionCmd(content string) tea.Cmd { +func (m *model) startCompletionCmd(promptArgs []string) tea.Cmd { return func() tea.Msg { temp := float32(*temperature) var prompt strings.Builder - // did we receive any piped input? - if m.pipedInput != "" { - fmt.Fprintf(&prompt, "Depending on the input, either edit or append to the input YAML. Do not generate new YAML without including the input YAML either original or edited.\nUse the following YAML as the input: \n%s\n", m.pipedInput) - } - - for _, p := range m.promptArgs { - fmt.Fprintf(&prompt, "%s", p) + for _, arg := range promptArgs { + fmt.Fprintf(&prompt, "%s\n", arg) } ctx, cancel := context.WithCancel(context.Background()) @@ -279,16 +367,9 @@ func (m *model) startCompletionCmd(content string) tea.Cmd { resp, err := m.oaiClients.openaiGptChatCompletion(ctx, &prompt, temp) if err != nil { - return m.handleRequestError(err, content) + return m.handleRequestError(err, prompt.String()) } - // trim the opening new line if it exists - resp = strings.TrimPrefix(resp, "\n") - - // TODO - do this later, because we need them in the output for glamour to render properly - // remove unnecessary backticks if they are in the output - //cleanedResp := trimTicks(resp) - return completionOutput{content: resp} } } @@ -296,11 +377,9 @@ func (m *model) startCompletionCmd(content string) tea.Cmd { func (m *model) handleRequestError(err error, content string) tea.Msg { ae := &openai.APIError{} if errors.As(err, &ae) { - //return m.handleAPIError(ae, mod, content) switch ae.HTTPStatusCode { case http.StatusTooManyRequests, http.StatusRequestTimeout, http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: log.Debugf("retrying due to status code %d: %s", ae.HTTPStatusCode, ae.Message) - //return retry.RetryableError(err) return m.retry(content, uiError{err: err, reason: fmt.Sprintf("retrying due to status code %d: %s", ae.HTTPStatusCode, ae.Message)}) } } @@ -310,10 +389,30 @@ func (m *model) handleRequestError(err error, content string) tea.Msg { )} } -func increaseIndent(s string) string { - lines := strings.Split(s, "\n") - for i := 0; i < len(lines); i++ { - lines[i] = "\t" + lines[i] +type renderedYamlMsg string + +func (m *model) renderWithGlamour(md string) tea.Cmd { + return func() tea.Msg { + s, err := m.glam.Render(md) + if err != nil { + return uiError{ + err: err, + reason: fmt.Sprintf("There was an error rendering the completion: %s", err), + } + } + + s = strings.TrimRightFunc(s, unicode.IsSpace) + s = strings.ReplaceAll(s, "\t", strings.Repeat(" ", tabWidth)) + s += "\n" + + return renderedYamlMsg(s) + } +} + +// if the input is whitespace only, make it empty. +func removeWhitespace(s string) string { + if strings.TrimSpace(s) == "" { + return "" } - return strings.Join(lines, "\n") + return s } From 0f5c28c39e1e53fb93aee71af04e6398425b4de3 Mon Sep 17 00:00:00 2001 From: Rob Morgan Date: Thu, 29 Aug 2024 17:29:27 +0800 Subject: [PATCH 4/5] go mod tidy --- go.mod | 12 +++--------- go.sum | 36 ++++-------------------------------- 2 files changed, 7 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index c766290..f2bcc17 100644 --- a/go.mod +++ b/go.mod @@ -8,10 +8,8 @@ require ( github.com/charmbracelet/bubbletea v0.26.6 github.com/charmbracelet/glamour v0.7.0 github.com/charmbracelet/x/term v0.1.1 - github.com/janeczku/go-spinner v0.0.0-20150530144529-cf8ef1d64394 - github.com/manifoldco/promptui v0.9.0 + github.com/muesli/termenv v0.15.2 github.com/sashabaranov/go-openai v1.27.1 - github.com/sethvargo/go-retry v0.2.4 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 @@ -22,12 +20,11 @@ require ( ) require ( - github.com/charmbracelet/bubbles v0.18.0 // indirect - github.com/charmbracelet/lipgloss v0.9.1 // indirect + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/lipgloss v0.9.1 github.com/charmbracelet/x/ansi v0.1.2 // indirect github.com/charmbracelet/x/input v0.1.0 // indirect github.com/charmbracelet/x/windows v0.1.0 // indirect - github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect @@ -41,7 +38,6 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect @@ -75,7 +71,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect @@ -86,7 +81,6 @@ require ( github.com/yuin/goldmark v1.5.4 // indirect github.com/yuin/goldmark-emoji v1.0.2 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/crypto v0.21.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.7.0 // indirect diff --git a/go.sum b/go.sum index 7beb158..a653e98 100644 --- a/go.sum +++ b/go.sum @@ -17,18 +17,12 @@ github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd3 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= -github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= -github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= -github.com/charmbracelet/x v0.1.0 h1:/MAmoZV+UIm4P9LxQBoQ2oK49wu/ewabHLFG4Gspc4c= -github.com/charmbracelet/x v0.1.0/go.mod h1:tfPuVPqtgK3n1KbwMw4oEq3Fruetob3nM14pFgMVFzs= -github.com/charmbracelet/x/ansi v0.1.0 h1:o4NbQQCoVgbLpD5RC1cI687baoLwrLZyCOTGlF0gne4= -github.com/charmbracelet/x/ansi v0.1.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= @@ -37,15 +31,10 @@ github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXD github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= github.com/charmbracelet/x/windows v0.1.0 h1:gTaxdvzDM5oMa/I2ZNF7wN78X/atWemG9Wph7Ika2k4= github.com/charmbracelet/x/windows v0.1.0/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -55,8 +44,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dmolesUC/go-spinner v0.0.0-20190903171623-0c332afb0926 h1:UV3snoSWHmkNeyGTO6WO9mjp033hWtabYv9wVUmmXgo= -github.com/dmolesUC/go-spinner v0.0.0-20190903171623-0c332afb0926/go.mod h1:WcFEJ3AsmcR5cApVBZyUWkRdu0IoDRakYe1wJmMm8LQ= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -137,14 +124,14 @@ 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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= 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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= -github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= @@ -153,8 +140,6 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= @@ -168,8 +153,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= 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= @@ -194,7 +177,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 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= @@ -207,8 +189,6 @@ github.com/sashabaranov/go-openai v1.27.1 h1:7Nx6db5NXbcoutNmAUQulEQZEpHG/Skzfex github.com/sashabaranov/go-openai v1.27.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= -github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= @@ -244,9 +224,9 @@ go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -271,12 +251,9 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -284,12 +261,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From 3287527efb081b991b1cbfa2180d0db947fc1d15 Mon Sep 17 00:00:00 2001 From: Rob Morgan Date: Thu, 29 Aug 2024 17:32:40 +0800 Subject: [PATCH 5/5] use the latest glamour --- go.mod | 30 +++++++++++++++--------------- go.sum | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index f2bcc17..7f7fcc4 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ toolchain go1.22.5 require ( github.com/charmbracelet/bubbletea v0.26.6 - github.com/charmbracelet/glamour v0.7.0 + github.com/charmbracelet/glamour v0.8.0 github.com/charmbracelet/x/term v0.1.1 - github.com/muesli/termenv v0.15.2 + github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a github.com/sashabaranov/go-openai v1.27.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 @@ -21,8 +21,8 @@ require ( require ( github.com/charmbracelet/bubbles v0.18.0 - github.com/charmbracelet/lipgloss v0.9.1 - github.com/charmbracelet/x/ansi v0.1.2 // indirect + github.com/charmbracelet/lipgloss v0.12.1 + github.com/charmbracelet/x/ansi v0.1.4 // indirect github.com/charmbracelet/x/input v0.1.0 // indirect github.com/charmbracelet/x/windows v0.1.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect @@ -34,12 +34,12 @@ require ( require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/alecthomas/chroma/v2 v2.8.0 // indirect + github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/dlclark/regexp2 v1.11.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/go-errors/errors v1.4.2 // indirect @@ -54,7 +54,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/css v1.0.0 // indirect + github.com/gorilla/css v1.0.1 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -63,9 +63,9 @@ require ( github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/microcosm-cc/bluemonday v1.0.25 // indirect + github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -78,15 +78,15 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect github.com/xlab/treeprint v1.2.0 // indirect - github.com/yuin/goldmark v1.5.4 // indirect - github.com/yuin/goldmark-emoji v1.0.2 // indirect + github.com/yuin/goldmark v1.7.4 // indirect + github.com/yuin/goldmark-emoji v1.0.3 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/go.sum b/go.sum index a653e98..c8dfb12 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2 github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264= github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= +github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= +github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= @@ -21,10 +23,16 @@ github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqp github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= github.com/charmbracelet/glamour v0.7.0 h1:2BtKGZ4iVJCDfMF229EzbeR1QRKLWztO9dMtjmqZSng= github.com/charmbracelet/glamour v0.7.0/go.mod h1:jUMh5MeihljJPQbJ/wf4ldw2+yBP59+ctV36jASy7ps= +github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= +github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= +github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= +github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ= github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28= github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= @@ -44,6 +52,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -103,6 +113,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= +github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -136,6 +148,8 @@ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -144,6 +158,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= +github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= +github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -161,6 +177,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= +github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -217,8 +235,13 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= +github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= +github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= +github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= +github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -243,6 +266,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= @@ -264,14 +289,20 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=