charmbracelet / bubbles

TUI components for Bubble Tea 🫧
MIT License
5.49k stars 264 forks source link

Textarea Input showing in reverse using nested format #616

Closed bytesByHarsh closed 3 weeks ago

bytesByHarsh commented 1 month ago

Describe the bug I am not sure if it is a bug or if I am missing out on something. Basically, I am trying to create a nested model in which the Update of the Base Model is calling the Update of child model. Teaxarea is present in the child model and for some reason, all the inputs are coming in reverse. But if I use the child model independently then I am facing no issue.

Setup Please complete the following information along with version numbers, if applicable.

Source Code I am sharing part of the code: Base Model eg:

type subInterface interface {
    Update(msg tea.Msg) (tea.Model, tea.Cmd)
    View() string
}

type model struct {
    Tabs             list.Model
    TabContent       []string
    activeTab        mode
    subControlActive bool
    error            string
    activeModel      subInterface
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    var cmds []tea.Cmd
    switch msg := msg.(type) {
       case tea.KeyMsg:
        if m.subControlActive {
                    _, cmd := m.activeModel.Update(msg)
                cmds = append(cmds, cmd)
                }
       }
       return m, tea.Batch(cmds...)
}

Child model:

type PingModel struct {
    textarea     textarea.Model
    viewport     viewport.Model
    pingMsgStyle lipgloss.Style
    messages     []string
    error        string
}
func InitModel() PingModel {
    ta := textarea.New()
    ta.Placeholder = "Enter Link/IP"
    ta.Focus()

    ta.Prompt = "┃ "
    ta.CharLimit = 280

    ta.SetWidth(50)
    ta.SetHeight(3)
    ta.EndOfBufferCharacter = ' '

    // Remove cursor line styling
    ta.FocusedStyle.CursorLine = lipgloss.NewStyle()

    ta.ShowLineNumbers = false

    vp := viewport.New(30, 5)
    vp.SetContent(`Welcome to the ping room!
Type the link/IP and press Enter to send.`)

    ta.KeyMap.InsertNewline.SetEnabled(false)

    return PingModel{
        textarea:     ta,
        viewport:     vp,
        messages:     []string{},
        pingMsgStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("5")),
        error:        "",
    }
}

func (m PingModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    var (
        tiCmd tea.Cmd
        vpCmd tea.Cmd
    )
        m.textarea, tiCmd = m.textarea.Update(msg)
    m.viewport, vpCmd = m.viewport.Update(msg)
}

Screenshots image

Additional context In the above screenshot I am trying to have the base model as the left-side list of different options and on the right will be the active child model. In the screenshot I have tried entering localhost but the whole string is reversed.

I wanted to confirm the way that I am doing is correct or am I missing out on something.

Broderick-Westrope commented 3 weeks ago

Hi @bytesByHarsh, the code you provided was not complete so I couldn't recreate the issue. I have created a simple example with only a textarea which I will include at the end.

If this example does not help you find the issue could you please share a minimal version of your code which has the issue and can compile on its own. This way I will be able to run it on my machine and see what issue you're facing. I noticed you're also using the viewport and list components but you have not shared the View methods for either of your models.

With more information I'd be happy to help :)

Also, the textinput component might be better suited to localhost input, unless you need multi-line input. I'll leave that one up to you though ;)

main.go:

package main

import (
    tea "github.com/charmbracelet/bubbletea"
    "log"
)

func main() {
    m := NewParentModel()

    if _, err := tea.NewProgram(m).Run(); err != nil {
        log.Fatalf("error running program: %s", err)
    }
}

parent.go:

package main

import tea "github.com/charmbracelet/bubbletea"

type ParentModel struct {
    child tea.Model
}

func NewParentModel() ParentModel {
    return ParentModel{
        child: NewChildModel(),
    }
}

func (m ParentModel) Init() tea.Cmd {
    return nil
}

func (m ParentModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    var cmd tea.Cmd

    // Operations that take priority over everything else
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "ctrl+c", "q":
            return m, tea.Quit
        }
    }

    // Update dependencies / children
    m.child, cmd = m.child.Update(msg)

    return m, cmd
}

func (m ParentModel) View() string {
    return m.child.View() + "\n"
}

child.go:

package main

import (
    "github.com/charmbracelet/bubbles/textarea"
    tea "github.com/charmbracelet/bubbletea"
)

type ChildModel struct {
    textarea textarea.Model
}

func NewChildModel() ChildModel {
    ta := textarea.New()
    ta.Focus()

    return ChildModel{
        textarea: ta,
    }
}

func (m ChildModel) Init() tea.Cmd {
    return nil
}

func (m ChildModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    var cmd tea.Cmd

    m.textarea, cmd = m.textarea.Update(msg)

    return m, cmd
}

func (m ChildModel) View() string {
    return m.textarea.View()
}
bytesByHarsh commented 3 weeks ago

Thanks @Broderick-Westrope for the sample code, i'll try once. Meanwhile, I have pushed my code on this repository: https://github.com/bytesByHarsh/network_cli

Can you please try out this code as well?

bytesByHarsh commented 3 weeks ago

Hi @Broderick-Westrope

I was able to solve this, thanks to your code. It was a minor issue

Old Code:

_, cmd = m.child.Update(msg)

Updated Code:

m.child, cmd = m.child.Update(msg)