1package logs
2
3import (
4 "encoding/json"
5 "slices"
6
7 "github.com/charmbracelet/bubbles/key"
8 "github.com/charmbracelet/bubbles/table"
9 tea "github.com/charmbracelet/bubbletea"
10 "github.com/kujtimiihoxha/termai/internal/logging"
11 "github.com/kujtimiihoxha/termai/internal/pubsub"
12 "github.com/kujtimiihoxha/termai/internal/tui/layout"
13 "github.com/kujtimiihoxha/termai/internal/tui/styles"
14 "github.com/kujtimiihoxha/termai/internal/tui/util"
15)
16
17type TableComponent interface {
18 tea.Model
19 layout.Focusable
20 layout.Sizeable
21 layout.Bindings
22}
23
24var logger = logging.Get()
25
26type tableCmp struct {
27 table table.Model
28}
29
30type selectedLogMsg logging.LogMessage
31
32func (i *tableCmp) Init() tea.Cmd {
33 i.setRows()
34 return nil
35}
36
37func (i *tableCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
38 var cmds []tea.Cmd
39 if i.table.Focused() {
40 switch msg.(type) {
41 case pubsub.Event[logging.LogMessage]:
42 i.setRows()
43 return i, nil
44 }
45 prevSelectedRow := i.table.SelectedRow()
46 t, cmd := i.table.Update(msg)
47 cmds = append(cmds, cmd)
48 i.table = t
49 selectedRow := i.table.SelectedRow()
50 if selectedRow != nil {
51 if prevSelectedRow == nil || selectedRow[0] == prevSelectedRow[0] {
52 var log logging.LogMessage
53 for _, row := range logging.Get().List() {
54 if row.ID == selectedRow[0] {
55 log = row
56 break
57 }
58 }
59 if log.ID != "" {
60 cmds = append(cmds, util.CmdHandler(selectedLogMsg(log)))
61 }
62 }
63 }
64 }
65 return i, tea.Batch(cmds...)
66}
67
68func (i *tableCmp) View() string {
69 return i.table.View()
70}
71
72func (i *tableCmp) Blur() tea.Cmd {
73 i.table.Blur()
74 return nil
75}
76
77func (i *tableCmp) Focus() tea.Cmd {
78 i.table.Focus()
79 return nil
80}
81
82func (i *tableCmp) IsFocused() bool {
83 return i.table.Focused()
84}
85
86func (i *tableCmp) GetSize() (int, int) {
87 return i.table.Width(), i.table.Height()
88}
89
90func (i *tableCmp) SetSize(width int, height int) {
91 i.table.SetWidth(width)
92 i.table.SetHeight(height)
93 cloumns := i.table.Columns()
94 for i, col := range cloumns {
95 col.Width = (width / len(cloumns)) - 2
96 cloumns[i] = col
97 }
98 i.table.SetColumns(cloumns)
99}
100
101func (i *tableCmp) BindingKeys() []key.Binding {
102 return layout.KeyMapToSlice(i.table.KeyMap)
103}
104
105func (i *tableCmp) setRows() {
106 rows := []table.Row{}
107
108 logs := logger.List()
109 slices.SortFunc(logs, func(a, b logging.LogMessage) int {
110 if a.Time.Before(b.Time) {
111 return 1
112 }
113 if a.Time.After(b.Time) {
114 return -1
115 }
116 return 0
117 })
118
119 for _, log := range logs {
120 bm, _ := json.Marshal(log.Attributes)
121
122 row := table.Row{
123 log.ID,
124 log.Time.Format("15:04:05"),
125 log.Level,
126 log.Message,
127 string(bm),
128 }
129 rows = append(rows, row)
130 }
131 i.table.SetRows(rows)
132}
133
134func NewLogsTable() TableComponent {
135 columns := []table.Column{
136 {Title: "ID", Width: 4},
137 {Title: "Time", Width: 4},
138 {Title: "Level", Width: 10},
139 {Title: "Message", Width: 10},
140 {Title: "Attributes", Width: 10},
141 }
142 defaultStyles := table.DefaultStyles()
143 defaultStyles.Selected = defaultStyles.Selected.Foreground(styles.Primary)
144 tableModel := table.New(
145 table.WithColumns(columns),
146 table.WithStyles(defaultStyles),
147 )
148 return &tableCmp{
149 table: tableModel,
150 }
151}