package tui import ( "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/table" ) var ( tableHeaderStyle lipgloss.Style tableCellStyle lipgloss.Style tableSelectedStyle lipgloss.Style tableBorderStyle lipgloss.Style tableZebraStyle lipgloss.Style ) type StyleOverride func(row, col int) *lipgloss.Style type colDef struct { short string full string minWidth int maxWidth int flex bool } func (m Model) computeTableLayout(cols []colDef, maxContentWidth int) ([]string, []int) { numCols := len(cols) borderOverhead := 2 + (numCols - 1) usable := m.termWidth - chromePadH - 2 - borderOverhead if usable < 40 { usable = 40 } fixedMin := 0 flexIdx := -1 for i, c := range cols { if c.flex { flexIdx = i continue } fixedMin += c.minWidth } flexW := usable - fixedMin if maxContentWidth > 0 && flexW > maxContentWidth { flexW = maxContentWidth } if flexW < 8 { flexW = 8 } surplus := usable - fixedMin - flexW if surplus < 0 { surplus = 0 } headers := make([]string, numCols) widths := make([]int, numCols) for i, c := range cols { if c.flex { headers[i] = c.full widths[i] = flexW continue } w := c.minWidth expand := c.maxWidth - c.minWidth if surplus >= expand { w = c.maxWidth surplus -= expand } else if surplus > 0 { w += surplus surplus = 0 } if w >= len(c.full)+2 { headers[i] = c.full } else { headers[i] = c.short } widths[i] = w } if surplus > 0 && flexIdx >= 0 { widths[flexIdx] += surplus } return headers, widths } func (m Model) renderTable(headers []string, items int, buildRows func(start, end int) [][]string, colWidths []int, styleOverride StyleOverride) string { if items == 0 { return "" } end := m.tableOffset + m.maxTableRows if end > items { end = items } selectedVisual := m.cursor - m.tableOffset rows := buildRows(m.tableOffset, end) tableWidth := m.termWidth - chromePadH - 2 if tableWidth < 40 { tableWidth = 40 } t := table.New(). Border(lipgloss.RoundedBorder()). BorderStyle(tableBorderStyle). Width(tableWidth). Headers(headers...). Rows(rows...). StyleFunc(func(row, col int) lipgloss.Style { if row == table.HeaderRow { h := tableHeaderStyle if col < len(colWidths) && colWidths[col] > 0 { h = h.Width(colWidths[col]).MaxWidth(colWidths[col]) } return h } isSelected := row == selectedVisual if styleOverride != nil { if s := styleOverride(row, col); s != nil { style := *s if isSelected { style = tableSelectedStyle.Foreground(s.GetForeground()) } if col < len(colWidths) && colWidths[col] > 0 { style = style.Width(colWidths[col]).MaxWidth(colWidths[col]) } return style } } base := tableCellStyle if row%2 == 1 { base = tableZebraStyle } if isSelected { base = tableSelectedStyle } if col < len(colWidths) && colWidths[col] > 0 { base = base.Width(colWidths[col]).MaxWidth(colWidths[col]) } return base }) return "\n" + t.Render() }