1package styles
2
3import (
4 "github.com/charmbracelet/bubbles/table"
5 "github.com/charmbracelet/lipgloss"
6)
7
8// NewStyledTable creates a themed table model.
9func NewStyledTable(theme *Theme, columns []table.Column, rows []table.Row, width, height int) table.Model {
10 t := table.New(
11 table.WithColumns(columns),
12 table.WithRows(rows),
13 table.WithFocused(true),
14 table.WithHeight(height),
15 table.WithWidth(width),
16 )
17
18 // Apply theme styles
19 s := table.DefaultStyles()
20 s.Header = s.Header.
21 BorderStyle(lipgloss.NormalBorder()).
22 BorderForeground(theme.Border).
23 BorderBottom(true).
24 Foreground(theme.Accent).
25 Bold(true)
26 s.Selected = s.Selected.
27 Foreground(theme.Text).
28 Background(theme.SurfaceVariant).
29 Bold(true)
30 s.Cell = s.Cell.
31 Foreground(theme.Text)
32
33 t.SetStyles(s)
34 return t
35}
36
37// StatsTableColumns returns columns for domain stats table.
38func StatsTableColumns() []table.Column {
39 return []table.Column{
40 {Title: "Domain", Width: 30},
41 {Title: "Visits", Width: 10},
42 {Title: "Entries", Width: 10},
43 {Title: "Last Visit", Width: 15},
44 }
45}
46
47// HistoryTableColumns returns columns for history list table.
48func HistoryTableColumns() []table.Column {
49 return []table.Column{
50 {Title: "Title", Width: 40},
51 {Title: "URL", Width: 30},
52 {Title: "Visits", Width: 8},
53 {Title: "Last Visit", Width: 12},
54 }
55}
56
57// DomainStatsRow converts domain stats to table row.
58type DomainStatsRow struct {
59 Domain string
60 Visits int
61 Entries int
62 LastVisit string
63}
64
65// ToRow converts to table.Row.
66func (d DomainStatsRow) ToRow() table.Row {
67 return table.Row{d.Domain, formatInt(d.Visits), formatInt(d.Entries), d.LastVisit}
68}
69
70// formatInt formats an integer for display.
71func formatInt(n int) string {
72 const (
73 formatMillion = 1_000_000
74 formatThousand = 1_000
75 )
76 switch {
77 case n >= formatMillion:
78 return formatFloat(float64(n)/formatMillion) + "M"
79 case n >= formatThousand:
80 return formatFloat(float64(n)/formatThousand) + "K"
81 default:
82 return intToString(n)
83 }
84}
85
86// formatFloat formats a float with one decimal.
87func formatFloat(f float64) string {
88 i := int(f * 10)
89 whole := i / 10
90 dec := i % 10
91 if dec == 0 {
92 return intToString(whole)
93 }
94 return intToString(whole) + "." + intToString(dec)
95}
96
97// intToString converts int to string without fmt.
98func intToString(n int) string {
99 if n == 0 {
100 return "0"
101 }
102 if n < 0 {
103 return "-" + intToString(-n)
104 }
105
106 digits := make([]byte, 0, 10)
107 for n > 0 {
108 digits = append(digits, byte('0'+n%10))
109 n /= 10
110 }
111
112 // Reverse
113 for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {
114 digits[i], digits[j] = digits[j], digits[i]
115 }
116 return string(digits)
117}