table.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package logs
  2. import (
  3. "encoding/json"
  4. "slices"
  5. "github.com/charmbracelet/bubbles/key"
  6. "github.com/charmbracelet/bubbles/table"
  7. tea "github.com/charmbracelet/bubbletea"
  8. "github.com/kujtimiihoxha/termai/internal/logging"
  9. "github.com/kujtimiihoxha/termai/internal/pubsub"
  10. "github.com/kujtimiihoxha/termai/internal/tui/layout"
  11. "github.com/kujtimiihoxha/termai/internal/tui/styles"
  12. "github.com/kujtimiihoxha/termai/internal/tui/util"
  13. )
  14. type TableComponent interface {
  15. tea.Model
  16. layout.Focusable
  17. layout.Sizeable
  18. layout.Bindings
  19. layout.Bordered
  20. }
  21. type tableCmp struct {
  22. table table.Model
  23. }
  24. func (i *tableCmp) BorderText() map[layout.BorderPosition]string {
  25. return map[layout.BorderPosition]string{
  26. layout.TopLeftBorder: "Logs",
  27. }
  28. }
  29. type selectedLogMsg logging.LogMessage
  30. func (i *tableCmp) Init() tea.Cmd {
  31. i.setRows()
  32. return nil
  33. }
  34. func (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. func (i *tableCmp) View() string {
  65. return i.table.View()
  66. }
  67. func (i *tableCmp) Blur() tea.Cmd {
  68. i.table.Blur()
  69. return nil
  70. }
  71. func (i *tableCmp) Focus() tea.Cmd {
  72. i.table.Focus()
  73. return nil
  74. }
  75. func (i *tableCmp) IsFocused() bool {
  76. return i.table.Focused()
  77. }
  78. func (i *tableCmp) GetSize() (int, int) {
  79. return i.table.Width(), i.table.Height()
  80. }
  81. func (i *tableCmp) SetSize(width int, height int) {
  82. i.table.SetWidth(width)
  83. i.table.SetHeight(height)
  84. cloumns := i.table.Columns()
  85. for i, col := range cloumns {
  86. col.Width = (width / len(cloumns)) - 2
  87. cloumns[i] = col
  88. }
  89. i.table.SetColumns(cloumns)
  90. }
  91. func (i *tableCmp) BindingKeys() []key.Binding {
  92. return layout.KeyMapToSlice(i.table.KeyMap)
  93. }
  94. func (i *tableCmp) setRows() {
  95. rows := []table.Row{}
  96. logs := logging.List()
  97. slices.SortFunc(logs, func(a, b logging.LogMessage) int {
  98. if a.Time.Before(b.Time) {
  99. return 1
  100. }
  101. if a.Time.After(b.Time) {
  102. return -1
  103. }
  104. return 0
  105. })
  106. for _, log := range logs {
  107. bm, _ := json.Marshal(log.Attributes)
  108. row := table.Row{
  109. log.ID,
  110. log.Time.Format("15:04:05"),
  111. log.Level,
  112. log.Message,
  113. string(bm),
  114. }
  115. rows = append(rows, row)
  116. }
  117. i.table.SetRows(rows)
  118. }
  119. func NewLogsTable() TableComponent {
  120. columns := []table.Column{
  121. {Title: "ID", Width: 4},
  122. {Title: "Time", Width: 4},
  123. {Title: "Level", Width: 10},
  124. {Title: "Message", Width: 10},
  125. {Title: "Attributes", Width: 10},
  126. }
  127. defaultStyles := table.DefaultStyles()
  128. defaultStyles.Selected = defaultStyles.Selected.Foreground(styles.Primary)
  129. tableModel := table.New(
  130. table.WithColumns(columns),
  131. table.WithStyles(defaultStyles),
  132. )
  133. return &tableCmp{
  134. table: tableModel,
  135. }
  136. }