charmbracelet / bubbles

TUI components for Bubble Tea 🫧

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

progress: last color of bar with gradient isn't the gradient's second color (off by one)

residualmind opened this issue · comments

When building something with progress with a gradient from pure green to pure red, I noticed that the very last color of the progress bar is never exactly red, but a little off. Especially when the display size was reduced to a small number of chars, this is very obvious.

To illustrate this, I added a failing test (and a fix)

If there's anything I can do to help here, please let me know. I thought the test basically illustrates the problem enough.

Steps to reproduce manually:
Define a progress bar with a gradient. Make note of the second gradient color.
Render the progress bar.
Check the color of the very last character. It doesn't match the noted gradient color.

This becomes really obvious when you:
Make the gradient very obvious. like from #FF0000 to #00FF00,
and render the bar very narrow, like only 3,4, or 5 characters wide. Then the issue becomes visible to the naked eye.

By the way, there is another issue you might run into when testing this, which is actually a problem with lucasb-eyer/go-colorful in my opinion. I will file a separate issue/PR for this, because it can be fixed in progress as well (but could and should be fixed in go-colorful). - just mentioning this in case you run into this separate issue while testing this and get confused (like I did).

The progress component renders the gradient using LUV colorspace. LUV colors can translate to R,G, or B values greater than 100%. When such a color is rendered to HEX without clamping values to (0,1) first, an overflow occurs and e.g r=1.0,g=1.0,b=1.001 will be rendered as #FFFF00, because blue has overflowed.
This is due to the nature of LUV being a bigger colorspace than RGB. It becomes obvious when you render a gradient "at the edges" of RGB colorspace, e.g. from #FF0000 to #FFFF00 - the LUV gradient will leave the RGB space and cause an overflow, changing a value that should be FF to 0.

This can be fixed by adding a .Clamped() in the progress rendering function - but I will create a PR for this, too.