-
Notifications
You must be signed in to change notification settings - Fork 813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lipgloss styles rendered incorrectly when .Width()
applied
#1225
Comments
The width is the issue I have an application that is now breaking due to 1.2.0 update, on 1.1.2 everything was ok. To replicate, type testModel struct {
w, h int
}
func (m testModel) Init() tea.Cmd {
return nil
}
func (m testModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.w = msg.Width
m.h = msg.Height
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
}
}
return m, nil
}
func (m testModel) View() string {
c := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder(), true).
BorderForeground(lipgloss.Color("#FFFDF5"))
return c.
Width(m.w - c.GetHorizontalFrameSize()).
Height(m.h - c.GetVerticalFrameSize()).
Render()
}
func main() {
if _, err := tea.NewProgram(testModel{}, tea.WithAltScreen()).Run(); err != nil {
log.Fatal(err)
}
} Run it with BubbleTea 1.1.2 and 1.2.0 and you will see the issue, hopefully. Should I open a new issue, or is it valid in this issue |
Hi @M0hammadUsman, I've looked into this and found a bug in the renderer. I've opened a new PR #1227 to fix this issue. |
@aymanbagabas Thank you so much, everything is now back to normal. |
@M0hammadUsman This is now fixed in v1.2.1. Thank you for reporting this issue 🙂 |
@aymanbagabas EDIT: I also downgraded to 1.1.2 and have the same bug |
The issue happens when the text after the cursor wraps around, try this example below package main
import (
"fmt"
"strings"
"time"
"github.com/charmbracelet/bubbles/stopwatch"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
// Styles
var (
terminalWidth, terminalHeight int
containerStyle = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
Padding(1, 2)
typedTextStyle = lipgloss.NewStyle()
untypedTextStyle = typedTextStyle.Faint(true)
cursorStyle = typedTextStyle.Reverse(true)
)
func main() {
prompt := strings.Repeat("the end is never ", 100)
p := tea.NewProgram(initialModel(prompt), tea.WithAltScreen())
p.Run()
}
type Model struct {
prompt string
cursor int
stopwatch stopwatch.Model
}
func initialModel(prompt string) Model {
return Model{
prompt: prompt,
stopwatch: stopwatch.NewWithInterval(100 * time.Millisecond),
}
}
func (m Model) Init() tea.Cmd {
return m.stopwatch.Start()
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
terminalWidth = msg.Width
terminalHeight = msg.Height
case tea.KeyMsg:
switch msg.Type {
case tea.KeyCtrlC, tea.KeyEsc:
return m, tea.Quit
}
case stopwatch.TickMsg:
m.cursor++
}
m.stopwatch, cmd = m.stopwatch.Update(msg)
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
var sb strings.Builder
sb.WriteString(typedTextStyle.Render(m.prompt[:m.cursor]))
sb.WriteString(cursorStyle.Render(string(m.prompt[m.cursor])))
sb.WriteString(untypedTextStyle.Render(" the issue happens when it wraps"))
sb.WriteString(fmt.Sprintf("\ncursor_coord: %d", m.cursor))
content := containerStyle.
Width(terminalWidth - containerStyle.GetHorizontalFrameSize()).
Render(sb.String())
res := lipgloss.JoinVertical(lipgloss.Center, content, m.stopwatch.View())
return lipgloss.Place(terminalWidth, terminalHeight, lipgloss.Center, lipgloss.Center, res)
} |
Interesting, is there an easy way to work around this? Is there a way to get the first line and apply style only to it in lipgloss? |
I don't think so that there is a way, as the text have to wrap dynamically as the window resizes, I've tried this to wrap the string myself, but no luck lim := terminalWidth - (containerStyle.GetHorizontalFrameSize() + containerStyle.GetHorizontalPadding())
content := containerStyle.
Width(terminalWidth - containerStyle.GetHorizontalFrameSize()).
Render(ansi.Wordwrap(sb.String(), lim, " ")) either the issue is with wrapping or the rendering, @aymanbagabas may help you with this. EDIT: If you really want, you can remove the text that is rendered after the cursor altogether. |
@aymanbagabas |
Hi all! Happy to reopen. That said, I'm trying to understand the exact issue(s). Is it basically that when text wraps inside a box with a border styling on text breaks? |
IIUC it doesn't have to be with a border, just wrapping text. Here is a more another example of the same bug (code derived from @M0hammadUsman's comment): package main
import (
"fmt"
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
var (
terminalWidth, terminalHeight int
containerStyle = lipgloss.NewStyle().
Border(lipgloss.RoundedBorder()).
Padding(1, 2)
typedTextStyle = lipgloss.NewStyle()
untypedTextStyle = typedTextStyle.Faint(true)
cursorStyle = typedTextStyle.Reverse(true)
)
func main() {
prompt := strings.Repeat("the end is never ", 100)
p := tea.NewProgram(initialModel(prompt), tea.WithAltScreen())
p.Run()
}
type Model struct {
prompt string
cursor int
}
func initialModel(prompt string) Model {
return Model{
prompt: prompt,
}
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
terminalWidth = msg.Width
terminalHeight = msg.Height
case tea.KeyMsg:
switch msg.Type {
case tea.KeyCtrlC, tea.KeyEsc:
return m, tea.Quit
case tea.KeyLeft:
m.cursor--
case tea.KeyRight:
m.cursor++
}
}
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
var sb strings.Builder
sb.WriteString(typedTextStyle.Render(m.prompt[:m.cursor]))
sb.WriteString(cursorStyle.Render(string(m.prompt[m.cursor])))
sb.WriteString(untypedTextStyle.Render(" the issue happens when it wraps"))
sb.WriteString(fmt.Sprintf("\ncursor_coord: %d", m.cursor))
content := containerStyle.
Width(50).
Render(sb.String())
return lipgloss.Place(terminalWidth, terminalHeight, lipgloss.Center, lipgloss.Center, content)
} The following happens when text starts to wrap: Screencast.from.2024-11-19.14-41-40.webm |
This is what I've found, run this example code package main
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
func main() {
p := tea.NewProgram(Model{}, tea.WithAltScreen())
p.Run()
}
type Model struct {
width, height int
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
case tea.KeyMsg:
switch msg.Type {
case tea.KeyCtrlC, tea.KeyEsc:
return m, tea.Quit
}
}
return m, nil
}
func (m Model) View() string {
text1WithStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FFC700")).Render("It breaks when it has to wrap, but the text it has to wrap")
text2WithStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5C00")).Render("is a combination of two different lipgloss style renders.")
text3WithStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#8b7000")).Render("\n\nDifferent colors make it obvious.")
content := lipgloss.NewStyle().
Border(lipgloss.RoundedBorder(), true).
Padding(1, 3).
Align(lipgloss.Center).
Width(40).
Render(text1WithStyle, text2WithStyle, text3WithStyle)
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, content)
} Edit: Also try to add |
Describe the bug
When applying a style dynamically to a string that has a width set to it by a
lipgloss
style, the style is rendered incorrectly. With 3 different styles applied to 3 different substrings there should be "typed" text (default), "untyped text" (default but faint) and cursor (default reversed), when applied to slices of a string, render incorrectly. The beginning of the string (which should be typed) is rendered fainted. Also, the borders and some text outside of the widget appear to be rendered fainted. This effect is temporal, as on first rendered frame everything appears to look fine and then rerenders incorrectly on subsequent renders (see video).Setup
Please complete the following information along with version numbers, if applicable.
To Reproduce
Steps to reproduce the behavior:
Source Code
Expected behavior
Cursor advances, all the text rendered before it is rendered normally, all the text after it is rendered fainted
Screenshots
Screencast.from.2024-11-06.23-01-18.webm
Additional context
At first glance this appears to be a lipgloss bug, but when I tried to replicate it with a minimal program, like so
The bug did not replicate, everything rendered as expected.
Also, setting the cursor to a static value also didn't work in the above code example also did not replicate the bug. Setting the
global_style
to not add the.Width()
also did not replicate the bug. So this is somewhere betweenlipgloss.Style.Width
and the rendering procedure ofbubbletea
The text was updated successfully, but these errors were encountered: