package ui import ( "strings" "thehouseoficarus/internal/color" ) type tableGlyphs struct { topLeft, topSep, topRight rune side, colSep rune sepLeft, sepCross, sepRight rune botLeft, botSep, botRight rune fillH, fillHSep rune titleSep rune } func tableGlyphSet(unicode bool) tableGlyphs { if unicode { return tableGlyphs{ topLeft: '╔', topSep: '╤', topRight: '╗', side: '║', colSep: '│', sepLeft: '╟', sepCross: '┼', sepRight: '╢', botLeft: '╚', botSep: '╧', botRight: '╝', fillH: '═', fillHSep: '─', titleSep: '┬', } } return tableGlyphs{ topLeft: '+', topSep: '+', topRight: '+', side: '|', colSep: '|', sepLeft: '+', sepCross: '+', sepRight: '+', botLeft: '+', botSep: '+', botRight: '+', fillH: '-', fillHSep: '-', titleSep: '+', } } type Table struct { Title string Columns []string Rows [][]string } func (t *Table) Render(unicode bool) []string { g := tableGlyphSet(unicode) nCols := len(t.Columns) if nCols == 0 && len(t.Rows) > 0 { nCols = len(t.Rows[0]) } if nCols == 0 { return nil } colWidths := make([]int, nCols) for i, h := range t.Columns { colWidths[i] = color.VisibleLen(h) } for _, row := range t.Rows { for i, cell := range row { if i >= nCols { break } vl := color.VisibleLen(cell) if vl > colWidths[i] { colWidths[i] = vl } } } makeSep := func(left, cross, right rune, fill rune) string { var b strings.Builder b.WriteRune(left) for i := 0; i < nCols; i++ { if i > 0 { b.WriteRune(cross) } b.WriteString(strings.Repeat(string(fill), colWidths[i]+2)) } b.WriteRune(right) return b.String() } makeRow := func(cells []string) string { var b strings.Builder b.WriteRune(g.side) for i := 0; i < nCols; i++ { if i > 0 { b.WriteRune(g.colSep) } cell := "" if i < len(cells) { cell = cells[i] } pad := colWidths[i] - color.VisibleLen(cell) if pad < 0 { pad = 0 } b.WriteString(" " + cell + strings.Repeat(" ", pad) + " ") } b.WriteRune(g.side) return b.String() } hasTitle := t.Title != "" hasCols := len(t.Columns) > 0 var out []string if hasTitle { innerWidth := 0 for _, w := range colWidths { innerWidth += w + 2 } innerWidth += nCols - 1 var tb strings.Builder tb.WriteRune(g.topLeft) tb.WriteString(strings.Repeat(string(g.fillH), innerWidth)) tb.WriteRune(g.topRight) out = append(out, tb.String()) var tr strings.Builder tr.WriteRune(g.side) tr.WriteString(" ") tr.WriteString(t.Title) padding := innerWidth - 2 - color.VisibleLen(t.Title) if padding < 0 { padding = 0 } tr.WriteString(strings.Repeat(" ", padding)) tr.WriteString(" ") tr.WriteRune(g.side) out = append(out, tr.String()) out = append(out, makeSep(g.sepLeft, g.titleSep, g.sepRight, g.fillHSep)) } else { out = append(out, makeSep(g.topLeft, g.topSep, g.topRight, g.fillH)) } if hasCols { out = append(out, makeRow(t.Columns)) out = append(out, makeSep(g.sepLeft, g.sepCross, g.sepRight, g.fillHSep)) } for _, row := range t.Rows { out = append(out, makeRow(row)) } out = append(out, makeSep(g.botLeft, g.botSep, g.botRight, g.fillH)) return out }