erikgeiser / promptkit

Go prompt library
MIT License
249 stars 21 forks source link

can't use confirmation prompt as a bubbletea model #25

Closed kalensk closed 1 year ago

kalensk commented 1 year ago

How can I use the confirmation prompt as a bubbletea model?

Specifically, can confirmation.New() return a tea.model? Alternatively, I tried to do something like the following in my code but, validateKeyMap is not exported.

type Model struct {
    model  *confirmation.Confirmation
    result bool

    err error
}

func NewConfirmationPrompt(prompt string) Model {
    err := validateKeyMap(c.KeyMap)
    if err != nil {
        return false, fmt.Errorf("insufficient key map: %w", err)
    }

    m := NewModel(c)

    return Model{
        model: model,
        err:   nil,
    }
}
erikgeiser commented 1 year ago

Here is an example of the confirmation prompt as a bubbletea model/widget: https://github.com/erikgeiser/promptkit/blob/main/examples/bubbletea_widget/main.go

kalensk commented 1 year ago

Thanks! After playing around some more, looking at your example, and getting more familiar with BubbleTea and promptkit I think I figured it all out.

I'll close this for now and if needed I can always re-open with a specific question or issue.

Thank you for a wonderful model! Super useful and looks good. : )

ryuheechul commented 1 year ago

@erikgeiser https://github.com/erikgeiser/promptkit/blob/main/examples/bubbletea_widget/main.go uses Selection not Confirmation, no?

@kalensk do you mind sharing what you learned on how to fit Confirmation into bubbltea?

kalensk commented 1 year ago

@ryuheechul Sure thing! Added https://github.com/erikgeiser/promptkit/pull/26 with an example for confirmation prompt. Feel free to not use the PR and close it since the example is so similar to the existing selection bubbletea example. I'll leave it to you.

ryuheechul commented 1 year ago

Thanks @kalensk! Now it's clear what I was doing wrong. I should have been looking for confirmation.Model.View but I was looking for confirmation.View instead. But that's crystal clear now thanks to your example code 👍🏼.

kalensk commented 1 year ago

Glad you got it working!

I'll paste the example code for confirmation prompt here for others:

// Package main demonstrates how promptkit can be used as a bubbletea widget.
package main

import (
    "fmt"
    "os"
    "strings"

    tea "github.com/charmbracelet/bubbletea"

    "github.com/erikgeiser/promptkit"
    "github.com/erikgeiser/promptkit/confirmation"
)

type confirmationPrompt struct {
    prompt       string
    selection    bool
    confirmation *confirmation.Model
    err          error
}

func newConfirmationPrompt(prompt string) *confirmationPrompt {
    return &confirmationPrompt{prompt: prompt}
}

var _ tea.Model = &confirmationPrompt{}

func (c *confirmationPrompt) Init() tea.Cmd {
    // Here we show how to specify your own keyMap since the default uses left
    // and right arrow keys which you may already be using.
    keyMap := &confirmation.KeyMap{
        Yes:    []string{"y", "Y"},
        No:     []string{"n", "N"},
        Toggle: []string{"tab"},
        Submit: []string{"enter"},
        Abort:  []string{"ctrl+c"},
    }

    conf := &confirmation.Confirmation{
        Prompt:         c.prompt,
        DefaultValue:   confirmation.Undecided,
        Template:       confirmation.DefaultTemplate,
        ResultTemplate: confirmation.DefaultResultTemplate,
        KeyMap:         keyMap,
        WrapMode:       promptkit.Truncate,
    }
    c.confirmation = confirmation.NewModel(conf)

    return c.confirmation.Init()
}

func (c *confirmationPrompt) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    keyMsg, ok := msg.(tea.KeyMsg)
    if !ok {
        return c, nil
    }

    switch {
    case keyMsg.String() == "enter":
        selection, err := c.confirmation.Value()
        if err != nil {
            c.err = err
            return c, tea.Quit
        }

        c.selection = selection
    case keyMsg.String() == "esc":
        return c, tea.Quit
    default:
        _, cmd := c.confirmation.Update(msg)
        return c, cmd
    }

    return c, nil
}

func (c *confirmationPrompt) View() string {
    if c.err != nil {
        return fmt.Sprintf("Error: %v", c.err)
    }

    var b strings.Builder
    b.WriteString(c.confirmation.View())
    b.WriteString("\n=== You Chose: ===\n")
    b.WriteString(fmt.Sprintf("%t", c.selection))

    return b.String()
}

func main() {
    model := newConfirmationPrompt("Would you like to go shopping?")

    p := tea.NewProgram(model)

    _, err := p.Run()
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v", err)

        os.Exit(1)
    }
}