charmbracelet / bubbles

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

fix(textinput): wrong placeholder/completion rendering #560

Open luevano opened 3 months ago

luevano commented 3 months ago

Fixes:

TODO/discuss:

Some examples, using contrasting colors for easier identification. Showing a half box below just to see the width, and appended a "Ñ" to see the end of the input view.

Before fix: Width set to 10, showing the placeholder color: image Extra padding after completion (what should be appended without completion), and the color under the cursor is what should be displayed for the whole completion: image Wrong completion character showing under cursor when overflowing (should show a 'k'): image

After fix: Correct completion style applied: image Correct completion char used under cursor when overflowing: image

meowgorithm commented 3 months ago

Thanks, @luevano. Could you provide some code we can use to see the before and after?

luevano commented 3 months ago

Thanks, @luevano. Could you provide some code we can use to see the before and after?

Hi @meowgorithm, no problem, made it as short as possible (ctrl+o: toggle size; ctrl+c: quit):

package main

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

type Model struct {
    input textinput.Model
    long  bool

    size,
    sizeShort int
    end string
}

func newModel() *Model {
    size, sizeShort := 64, 10
    input := textinput.New()
    input.Placeholder = "Long placeholder..."
    input.ShowSuggestions = true
    input.Width = size
    input.PlaceholderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#008080"))
    input.CompletionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#EB5E28"))

    input.SetSuggestions([]string{
        "hello",
        "world",
        "some long suggestion",
        "another very long suggestion...",
    })

    return &Model{
        input:     input,
        long:      true,
        size:      size,
        sizeShort: sizeShort,
        end:       lipgloss.NewStyle().Foreground(lipgloss.Color("#FF0000")).Render("Ñ"),
    }
}

func (m *Model) Init() tea.Cmd {
    m.input.CursorEnd()
    return tea.Sequence(
        m.input.Focus(),
        textinput.Blink,
    )
}

func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.Type {
        case tea.KeyCtrlC:
            return m, tea.Quit
        case tea.KeyCtrlO:
            m.long = !m.long
            if m.long {
                m.input.Width = m.size
            } else {
                m.input.Width = m.sizeShort
            }
        }
    }

    input, cmd := m.input.Update(msg)
    m.input = input
    return m, cmd
}

func (m *Model) View() string {
    return m.input.View() + m.end
}

func main() {
    if _, err := tea.NewProgram(newModel()).Run(); err != nil {
        panic(err)
    }
}
bashbunni commented 3 weeks ago

Hey @luevano sorry for the delay on this. The changes look great. I think width should include everything (even cursor + prompt) as that will be the most predictable behaviour. That said, I'll ask the team to see if they have any thoughts on this too.

bashbunni commented 3 weeks ago

Update, team agreed that the width should contain all components including the text field, cursor, and prompt

luevano commented 3 weeks ago

@bashbunni thanks for the feedback, I agree with that. I did, however, try to fix the whole thing to fit within the given width, fut failed miserably lol I did try to do that back when I was working on this fix, will give it another go and see what I can do.

bashbunni commented 2 weeks ago

@luevano no problem! I can see about fixing the width :) thanks again for your work on this