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)
15
16type TableComponent interface {
17 tea.Model
18 layout.Focusable
19 layout.Sizeable
20 layout.Bindings
21}
22
23var logger = logging.Get()
24
25type tableCmp struct {
26 table table.Model
27}
28
29func (i *tableCmp) Init() tea.Cmd {
30 i.setRows()
31 return nil
32}
33
34func (i *tableCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
35 if i.table.Focused() {
36 switch msg := msg.(type) {
37 case pubsub.Event[logging.Message]:
38 i.setRows()
39 return i, nil
40 case tea.KeyMsg:
41 if msg.String() == "ctrl+s" {
42 logger.Info("Saving logs...",
43 "rows", len(i.table.Rows()),
44 )
45 }
46 }
47 t, cmd := i.table.Update(msg)
48 i.table = t
49 return i, cmd
50 }
51 return i, nil
52}
53
54func (i *tableCmp) View() string {
55 return i.table.View()
56}
57
58func (i *tableCmp) Blur() tea.Cmd {
59 i.table.Blur()
60 return nil
61}
62
63func (i *tableCmp) Focus() tea.Cmd {
64 i.table.Focus()
65 return nil
66}
67
68func (i *tableCmp) IsFocused() bool {
69 return i.table.Focused()
70}
71
72func (i *tableCmp) GetSize() (int, int) {
73 return i.table.Width(), i.table.Height()
74}
75
76func (i *tableCmp) SetSize(width int, height int) {
77 i.table.SetWidth(width)
78 i.table.SetHeight(height)
79 cloumns := i.table.Columns()
80 for i, col := range cloumns {
81 col.Width = (width / len(cloumns)) - 2
82 cloumns[i] = col
83 }
84 i.table.SetColumns(cloumns)
85}
86
87func (i *tableCmp) BindingKeys() []key.Binding {
88 return layout.KeyMapToSlice(i.table.KeyMap)
89}
90
91func (i *tableCmp) setRows() {
92 rows := []table.Row{}
93
94 logs := logger.List()
95 slices.SortFunc(logs, func(a, b logging.Message) int {
96 if a.Time.Before(b.Time) {
97 return 1
98 }
99 if a.Time.After(b.Time) {
100 return -1
101 }
102 return 0
103 })
104
105 for _, log := range logs {
106 bm, _ := json.Marshal(log.Attributes)
107
108 row := table.Row{
109 log.Time.Format("15:04:05"),
110 log.Level,
111 log.Message,
112 string(bm),
113 }
114 rows = append(rows, row)
115 }
116 i.table.SetRows(rows)
117}
118
119func NewLogsTable() TableComponent {
120 columns := []table.Column{
121 {Title: "Time", Width: 4},
122 {Title: "Level", Width: 10},
123 {Title: "Message", Width: 10},
124 {Title: "Attributes", Width: 10},
125 }
126 defaultStyles := table.DefaultStyles()
127 defaultStyles.Selected = defaultStyles.Selected.Foreground(styles.Primary)
128 tableModel := table.New(
129 table.WithColumns(columns),
130 table.WithStyles(defaultStyles),
131 )
132 return &tableCmp{
133 table: tableModel,
134 }
135}