details.go

  1package logs
  2
  3import (
  4	"fmt"
  5	"strings"
  6	"time"
  7
  8	"github.com/charmbracelet/bubbles/key"
  9	"github.com/charmbracelet/bubbles/viewport"
 10	tea "github.com/charmbracelet/bubbletea"
 11	"github.com/charmbracelet/lipgloss"
 12	"github.com/kujtimiihoxha/termai/internal/logging"
 13	"github.com/kujtimiihoxha/termai/internal/tui/layout"
 14	"github.com/kujtimiihoxha/termai/internal/tui/styles"
 15)
 16
 17type DetailComponent interface {
 18	tea.Model
 19	layout.Focusable
 20	layout.Sizeable
 21	layout.Bindings
 22	layout.Bordered
 23}
 24
 25type detailCmp struct {
 26	width, height int
 27	focused       bool
 28	currentLog    logging.LogMessage
 29	viewport      viewport.Model
 30}
 31
 32func (i *detailCmp) Init() tea.Cmd {
 33	messages := logging.Get().List()
 34	if len(messages) == 0 {
 35		return nil
 36	}
 37	i.currentLog = messages[0]
 38	return nil
 39}
 40
 41func (i *detailCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 42	var (
 43		cmd  tea.Cmd
 44		cmds []tea.Cmd
 45	)
 46
 47	switch msg := msg.(type) {
 48	case selectedLogMsg:
 49		if msg.ID != i.currentLog.ID {
 50			i.currentLog = logging.LogMessage(msg)
 51			i.updateContent()
 52		}
 53	}
 54
 55	if i.focused {
 56		i.viewport, cmd = i.viewport.Update(msg)
 57		cmds = append(cmds, cmd)
 58	}
 59
 60	return i, tea.Batch(cmds...)
 61}
 62
 63func (i *detailCmp) updateContent() {
 64	var content strings.Builder
 65
 66	// Format the header with timestamp and level
 67	timeStyle := lipgloss.NewStyle().Foreground(styles.SubText0)
 68	levelStyle := getLevelStyle(i.currentLog.Level)
 69
 70	header := lipgloss.JoinHorizontal(
 71		lipgloss.Center,
 72		timeStyle.Render(i.currentLog.Time.Format(time.RFC3339)),
 73		"  ",
 74		levelStyle.Render(i.currentLog.Level),
 75	)
 76
 77	content.WriteString(lipgloss.NewStyle().Bold(true).Render(header))
 78	content.WriteString("\n\n")
 79
 80	// Message with styling
 81	messageStyle := lipgloss.NewStyle().Bold(true).Foreground(styles.Text)
 82	content.WriteString(messageStyle.Render("Message:"))
 83	content.WriteString("\n")
 84	content.WriteString(lipgloss.NewStyle().Padding(0, 2).Render(i.currentLog.Message))
 85	content.WriteString("\n\n")
 86
 87	// Attributes section
 88	if len(i.currentLog.Attributes) > 0 {
 89		attrHeaderStyle := lipgloss.NewStyle().Bold(true).Foreground(styles.Text)
 90		content.WriteString(attrHeaderStyle.Render("Attributes:"))
 91		content.WriteString("\n")
 92
 93		// Create a table-like display for attributes
 94		keyStyle := lipgloss.NewStyle().Foreground(styles.Primary).Bold(true)
 95		valueStyle := lipgloss.NewStyle().Foreground(styles.Text)
 96
 97		for _, attr := range i.currentLog.Attributes {
 98			attrLine := fmt.Sprintf("%s: %s",
 99				keyStyle.Render(attr.Key),
100				valueStyle.Render(attr.Value),
101			)
102			content.WriteString(lipgloss.NewStyle().Padding(0, 2).Render(attrLine))
103			content.WriteString("\n")
104		}
105	}
106
107	i.viewport.SetContent(content.String())
108}
109
110func getLevelStyle(level string) lipgloss.Style {
111	style := lipgloss.NewStyle().Bold(true)
112
113	switch strings.ToLower(level) {
114	case "info":
115		return style.Foreground(styles.Blue)
116	case "warn", "warning":
117		return style.Foreground(styles.Warning)
118	case "error", "err":
119		return style.Foreground(styles.Error)
120	case "debug":
121		return style.Foreground(styles.Green)
122	default:
123		return style.Foreground(styles.Text)
124	}
125}
126
127func (i *detailCmp) View() string {
128	return i.viewport.View()
129}
130
131func (i *detailCmp) Blur() tea.Cmd {
132	i.focused = false
133	return nil
134}
135
136func (i *detailCmp) Focus() tea.Cmd {
137	i.focused = true
138	return nil
139}
140
141func (i *detailCmp) IsFocused() bool {
142	return i.focused
143}
144
145func (i *detailCmp) GetSize() (int, int) {
146	return i.width, i.height
147}
148
149func (i *detailCmp) SetSize(width int, height int) {
150	i.width = width
151	i.height = height
152	i.viewport.Width = i.width
153	i.viewport.Height = i.height
154	i.updateContent()
155}
156
157func (i *detailCmp) BindingKeys() []key.Binding {
158	return []key.Binding{
159		i.viewport.KeyMap.PageDown,
160		i.viewport.KeyMap.PageUp,
161		i.viewport.KeyMap.HalfPageDown,
162		i.viewport.KeyMap.HalfPageUp,
163	}
164}
165
166func (i *detailCmp) BorderText() map[layout.BorderPosition]string {
167	return map[layout.BorderPosition]string{
168		layout.TopLeftBorder: "Log Details",
169	}
170}
171
172func NewLogsDetails() DetailComponent {
173	return &detailCmp{
174		viewport: viewport.New(0, 0),
175	}
176}