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)
 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}