Skip to content
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

How to define the color of a specified cell in a table #502

Open
JJ-H opened this issue Mar 29, 2024 · 2 comments · May be fixed by #617
Open

How to define the color of a specified cell in a table #502

JJ-H opened this issue Mar 29, 2024 · 2 comments · May be fixed by #617
Milestone

Comments

@JJ-H
Copy link

JJ-H commented Mar 29, 2024

Describe the bug
When I specified the color of cell text,I got a unexpected table style, cell text disappear

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

  • OS [ macOS]
  • Shell [ zsh]
  • Terminal Emulator [ iterm]
  • Terminal Multiplexer [tmux]
  • Locale [ zh_CN.UTF-8.]

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Source Code

acceptMsg := lipgloss.NewStyle().Inline(true).Foreground(lipgloss.Color("#BBFFFF")).Render("Accept")
rows = append(rows, table.Row{pr.Title, pr.Number, pr.Creator.Name, acceptMsg, conflictMsg, mergeCheckMsg})

Expected behavior
AcceptMsg display with color and table style is correct.

Screenshots
Accept disappeared !
image

Additional context
Add any other context about the problem here.

@bashbunni bashbunni added this to the v0.20.0 milestone Aug 15, 2024
@Broderick-Westrope
Copy link

For others that may be trying to recreate this:

I used the table example from bubbletea (examples/table/main.go). This git diff shows the minimal required changes to reproduce this issue:

diff --git a/examples/table/main.go b/examples/table/main.go
index 76736f3..ca0bf4d 100644
--- a/examples/table/main.go
+++ b/examples/table/main.go
@@ -54,8 +54,13 @@ func main() {
                {Title: "Population", Width: 10},
        }

+       japanStr := lipgloss.NewStyle().
+               Foreground(lipgloss.Color("#BBFFFF")).
+               Render("Japan")
+       //japanStr = ansi.Strip(japanStr)
+
        rows := []table.Row{
-               {"1", "Tokyo", "Japan", "37,274,000"},
+               {"1", "Tokyo", japanStr, "37,274,000"},
                {"2", "Delhi", "India", "32,065,760"},
                {"3", "Shanghai", "China", "28,516,904"},
                {"4", "Dhaka", "Bangladesh", "22,478,116"},

This produces output where "Japan" is omitted:
image

By uncommenting the ansi.Strip line (shown in the git diff) the output changes to include "Japan", however without the desired foreground color:
image

This suggests that color ANSI escape sequences are to blame for this behaviour. I'm continuing to look into the cause.

@Broderick-Westrope
Copy link

Broderick-Westrope commented Aug 28, 2024

I believe I've found the source of the issue. If anything I say is wrong please correct me, I'm not a Go pro ;)

The contents of every cell is truncated to be no greater than the assigned column width. This is done using the Truncate function from the mattn/go-runewidth package. When we use ligploss to color a cells foreground we are writing escape sequences on either side of the cell "content". These escape sequences are not normally visible when printed but there are ways to see them like this:

japanStr := lipgloss.NewStyle().Foreground(lipgloss.Color("#BBFFFF")).Render("Japan")
fmt.Sprintf("%#v", japanStr)
// prints: "\x1b[38;2;187;255;255mJapan\x1b[0m"

Even though the escape sequences are not visible when printing normally, they are composed of runes which are modified during the runewidth.Truncate call.

I've thrown together a (hopefully) simple example to demonstrate this, see below. Currently this prints colors that overflow into other columns and cells, but by commenting the runewidth.Truncate line (near the bottom) the issues go away. This is because the "closing" escape sequence is removed during the truncation. I've included the original color code as row 1 in my example below. I am uncertain why this is the only line that completely disappears.

package main

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/charmbracelet/bubbles/table"
	"github.com/charmbracelet/bubbles/viewport"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/mattn/go-runewidth"
)

func main() {
	m := newModel()

	_, err := tea.NewProgram(m).Run()
	if err != nil {
		panic(err)
	}
}

type model struct {
	viewport    viewport.Model
	borderStyle lipgloss.Style

	cols []table.Column
	rows []table.Row
}

func newModel() model {
	columns := []table.Column{
		{Title: "Rank", Width: 4},
		{Title: "City", Width: 10},
		{Title: "Country", Width: 10},
		{Title: "Population", Width: 10},
		{Title: "Country Escaped", Width: 50},
	}

	rows := []table.Row{
		{"1", "Tokyo", "Japan", "37,274,000"},
		{"2", "Delhi", "India", "32,065,760"},
		{"3", "Shanghai", "China", "28,516,904"},
		{"4", "Dhaka", "Bangladesh", "22,478,116"},
		{"5", "São Paulo", "Brazil", "22,429,800"},
		{"6", "Mexico City", "Mexico", "22,085,140"},
		{"7", "Cairo", "Egypt", "21,750,020"},
		{"8", "Beijing", "China", "21,333,332"},
		{"9", "Mumbai", "India", "20,961,472"},
		{"10", "Osaka", "Japan", "19,059,856"},
		{"11", "Chongqing", "China", "16,874,740"},
		{"12", "Karachi", "Pakistan", "16,839,950"},
		{"13", "Istanbul", "Turkey", "15,636,243"},
		{"14", "Kinshasa", "DR Congo", "15,628,085"},
		{"15", "Lagos", "Nigeria", "15,387,639"},
		{"16", "Buenos Aires", "Argentina", "15,369,919"},
		{"17", "Kolkata", "India", "15,133,888"},
		{"18", "Manila", "Philippines", "14,406,059"},
		{"19", "Tianjin", "China", "14,011,828"},
		{"20", "Guangzhou", "China", "13,964,637"},
		{"21", "Rio De Janeiro", "Brazil", "13,634,274"},
		{"22", "Lahore", "Pakistan", "13,541,764"},
		{"23", "Bangalore", "India", "13,193,035"},
		{"24", "Shenzhen", "China", "12,831,330"},
		{"25", "Moscow", "Russia", "12,640,818"},
		{"26", "Chennai", "India", "11,503,293"},
		{"27", "Bogota", "Colombia", "11,344,312"},
		{"28", "Paris", "France", "11,142,303"},
		{"29", "Jakarta", "Indonesia", "11,074,811"},
		{"30", "Lima", "Peru", "11,044,607"},
	}

	for i := range rows {
		color := lipgloss.Color(strconv.Itoa(i))
		if i == 0 {
			color = "#BBFFFF"
		}

		rows[i][2] = lipgloss.NewStyle().Foreground(color).Render(rows[i][2])
		rows[i] = append(rows[i], fmt.Sprintf("%#v", rows[i][2]))
	}

	m := model{
		viewport:    viewport.New(100, 30),
		borderStyle: lipgloss.NewStyle().BorderStyle(lipgloss.NormalBorder()),
		cols:        columns,
		rows:        rows,
	}

	m.UpdateViewport()

	return m
}

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.KeyMsg:
		switch msg.String() {
		case "q":
			return m, tea.Quit
		}
	}

	var cmd tea.Cmd
	m.viewport, cmd = m.viewport.Update(msg)
	return m, cmd
}

func (m model) View() string {
	var output string

	output += m.viewport.View()
	output = m.borderStyle.Render(output)

	return output
}

func (m *model) UpdateViewport() {
	// Assumption: cols will not contain a large number of elements
	renderedRows := make([]string, 0, len(m.rows))
	for i := 0; i < len(m.rows); i++ {
		renderedRows = append(renderedRows, m.renderRow(i))
	}

	m.viewport.SetContent(
		strings.Join(renderedRows, "\n"),
	)
}

func (m *model) renderRow(rowID int) string {
	var rowCells = make([]string, 0, len(m.cols))

	for i, value := range m.rows[rowID] {
		// COMMENT THIS LINE TO FIX THE BUG
		value = runewidth.Truncate(value, m.cols[i].Width, "…")

		style := lipgloss.NewStyle().Width(m.cols[i].Width).MaxWidth(m.cols[i].Width).Inline(true)
		value = style.Render(value)
		rowCells = append(rowCells, value)
	}

	return lipgloss.JoinHorizontal(lipgloss.Left, rowCells...)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants