table.go

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