summaryrefslogtreecommitdiffhomepage
path: root/internal/tui/components/logs
diff options
context:
space:
mode:
Diffstat (limited to 'internal/tui/components/logs')
-rw-r--r--internal/tui/components/logs/details.go187
-rw-r--r--internal/tui/components/logs/table.go207
2 files changed, 0 insertions, 394 deletions
diff --git a/internal/tui/components/logs/details.go b/internal/tui/components/logs/details.go
deleted file mode 100644
index bc59fdc6f..000000000
--- a/internal/tui/components/logs/details.go
+++ /dev/null
@@ -1,187 +0,0 @@
-package logs
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "strings"
- "time"
-
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/viewport"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
- "github.com/sst/opencode/internal/logging"
- "github.com/sst/opencode/internal/tui/layout"
- "github.com/sst/opencode/internal/tui/styles"
- "github.com/sst/opencode/internal/tui/theme"
-)
-
-type DetailComponent interface {
- tea.Model
- layout.Sizeable
- layout.Bindings
-}
-
-type detailCmp struct {
- width, height int
- currentLog logging.Log
- viewport viewport.Model
- focused bool
-}
-
-func (i *detailCmp) Init() tea.Cmd {
- return nil
-}
-
-func (i *detailCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- var cmd tea.Cmd
- switch msg := msg.(type) {
- case selectedLogMsg:
- if msg.ID != i.currentLog.ID {
- i.currentLog = logging.Log(msg)
- // Defer content update to avoid blocking the UI
- cmd = tea.Tick(time.Millisecond*1, func(time.Time) tea.Msg {
- i.updateContent()
- return nil
- })
- }
- case tea.KeyMsg:
- // Only process keyboard input when focused
- if !i.focused {
- return i, nil
- }
- // Handle keyboard input for scrolling
- i.viewport, cmd = i.viewport.Update(msg)
- return i, cmd
- }
-
- return i, cmd
-}
-
-func (i *detailCmp) updateContent() {
- var content strings.Builder
- t := theme.CurrentTheme()
-
- // Format the header with timestamp and level
- timeStyle := lipgloss.NewStyle().Foreground(t.TextMuted())
- levelStyle := getLevelStyle(i.currentLog.Level)
-
- // Format timestamp
- timeStr := i.currentLog.Timestamp.Format(time.RFC3339)
-
- header := lipgloss.JoinHorizontal(
- lipgloss.Center,
- timeStyle.Render(timeStr),
- " ",
- levelStyle.Render(i.currentLog.Level),
- )
-
- content.WriteString(lipgloss.NewStyle().Bold(true).Render(header))
- content.WriteString("\n\n")
-
- // Message with styling
- messageStyle := lipgloss.NewStyle().Bold(true).Foreground(t.Text())
- content.WriteString(messageStyle.Render("Message:"))
- content.WriteString("\n")
- content.WriteString(lipgloss.NewStyle().Padding(0, 2).Width(i.width).Render(i.currentLog.Message))
- content.WriteString("\n\n")
-
- // Attributes section
- if len(i.currentLog.Attributes) > 0 {
- attrHeaderStyle := lipgloss.NewStyle().Bold(true).Foreground(t.Text())
- content.WriteString(attrHeaderStyle.Render("Attributes:"))
- content.WriteString("\n")
-
- // Create a table-like display for attributes
- keyStyle := lipgloss.NewStyle().Foreground(t.Primary()).Bold(true)
- valueStyle := lipgloss.NewStyle().Foreground(t.Text())
-
- for key, value := range i.currentLog.Attributes {
- // if value is JSON, render it with indentation
- if strings.HasPrefix(value, "{") {
- var indented bytes.Buffer
- if err := json.Indent(&indented, []byte(value), "", " "); err != nil {
- indented.WriteString(value)
- }
- value = indented.String()
- }
-
- attrLine := fmt.Sprintf("%s: %s",
- keyStyle.Render(key),
- valueStyle.Render(value),
- )
-
- content.WriteString(lipgloss.NewStyle().Padding(0, 2).Width(i.width).Render(attrLine))
- content.WriteString("\n")
- }
- }
-
- // Session ID if available
- if i.currentLog.SessionID != "" {
- sessionStyle := lipgloss.NewStyle().Bold(true).Foreground(t.Text())
- content.WriteString("\n")
- content.WriteString(sessionStyle.Render("Session:"))
- content.WriteString("\n")
- content.WriteString(lipgloss.NewStyle().Padding(0, 2).Width(i.width).Render(i.currentLog.SessionID))
- }
-
- i.viewport.SetContent(content.String())
-}
-
-func getLevelStyle(level string) lipgloss.Style {
- style := lipgloss.NewStyle().Bold(true)
- t := theme.CurrentTheme()
-
- switch strings.ToLower(level) {
- case "info":
- return style.Foreground(t.Info())
- case "warn", "warning":
- return style.Foreground(t.Warning())
- case "error", "err":
- return style.Foreground(t.Error())
- case "debug":
- return style.Foreground(t.Success())
- default:
- return style.Foreground(t.Text())
- }
-}
-
-func (i *detailCmp) View() string {
- t := theme.CurrentTheme()
- return styles.ForceReplaceBackgroundWithLipgloss(i.viewport.View(), t.Background())
-}
-
-func (i *detailCmp) GetSize() (int, int) {
- return i.width, i.height
-}
-
-func (i *detailCmp) SetSize(width int, height int) tea.Cmd {
- i.width = width
- i.height = height
- i.viewport.Width = i.width
- i.viewport.Height = i.height
- i.updateContent()
- return nil
-}
-
-func (i *detailCmp) BindingKeys() []key.Binding {
- return layout.KeyMapToSlice(i.viewport.KeyMap)
-}
-
-func NewLogsDetails() DetailComponent {
- return &detailCmp{
- viewport: viewport.New(0, 0),
- }
-}
-
-// Focus implements the focusable interface
-func (i *detailCmp) Focus() {
- i.focused = true
- i.viewport.SetYOffset(i.viewport.YOffset)
-}
-
-// Blur implements the blurable interface
-func (i *detailCmp) Blur() {
- i.focused = false
-}
diff --git a/internal/tui/components/logs/table.go b/internal/tui/components/logs/table.go
deleted file mode 100644
index 1fc1daa38..000000000
--- a/internal/tui/components/logs/table.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package logs
-
-import (
- // "context"
- "fmt"
- "log/slog"
-
- "github.com/charmbracelet/bubbles/key"
- "github.com/charmbracelet/bubbles/table"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/sst/opencode/internal/app"
- "github.com/sst/opencode/internal/logging"
- "github.com/sst/opencode/internal/pubsub"
- "github.com/sst/opencode/internal/tui/layout"
- "github.com/sst/opencode/internal/tui/state"
- "github.com/sst/opencode/internal/tui/theme"
-)
-
-type TableComponent interface {
- tea.Model
- layout.Sizeable
- layout.Bindings
-}
-
-type tableCmp struct {
- app *app.App
- table table.Model
- focused bool
- logs []logging.Log
- selectedLogID string
-}
-
-type selectedLogMsg logging.Log
-
-type LogsLoadedMsg struct {
- logs []logging.Log
-}
-
-func (i *tableCmp) Init() tea.Cmd {
- return i.fetchLogs()
-}
-
-func (i *tableCmp) fetchLogs() tea.Cmd {
- return func() tea.Msg {
- // ctx := context.Background()
-
- var logs []logging.Log
- var err error
-
- // Limit the number of logs to improve performance
- const logLimit = 100
- // TODO: Logs service not implemented in API yet
- logs = []logging.Log{}
- err = fmt.Errorf("logs service not implemented")
-
- if err != nil {
- slog.Error("Failed to fetch logs", "error", err)
- return nil
- }
-
- return LogsLoadedMsg{logs: logs}
- }
-}
-
-func (i *tableCmp) updateRows() tea.Cmd {
- return func() tea.Msg {
- rows := make([]table.Row, 0, len(i.logs))
-
- for _, log := range i.logs {
- timeStr := log.Timestamp.Local().Format("15:04:05")
-
- // Include ID as hidden first column for selection
- row := table.Row{
- log.ID,
- timeStr,
- log.Level,
- log.Message,
- }
- rows = append(rows, row)
- }
-
- i.table.SetRows(rows)
- return nil
- }
-}
-
-func (i *tableCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- var cmds []tea.Cmd
-
- switch msg := msg.(type) {
- case LogsLoadedMsg:
- i.logs = msg.logs
- return i, i.updateRows()
-
- case state.SessionSelectedMsg:
- return i, i.fetchLogs()
-
- case pubsub.Event[logging.Log]:
- if msg.Type == logging.EventLogCreated {
- // Add the new log to our list
- i.logs = append([]logging.Log{msg.Payload}, i.logs...)
- // Keep the list at a reasonable size
- if len(i.logs) > 100 {
- i.logs = i.logs[:100]
- }
- return i, i.updateRows()
- }
- return i, nil
- }
-
- // Only process keyboard input when focused
- if _, ok := msg.(tea.KeyMsg); ok && !i.focused {
- return i, nil
- }
-
- t, cmd := i.table.Update(msg)
- cmds = append(cmds, cmd)
- i.table = t
-
- selectedRow := i.table.SelectedRow()
- if selectedRow != nil {
- // Only send message if it's a new selection
- if i.selectedLogID != selectedRow[0] {
- cmds = append(cmds, func() tea.Msg {
- for _, log := range i.logs {
- if log.ID == selectedRow[0] {
- return selectedLogMsg(log)
- }
- }
- return nil
- })
- }
-
- i.selectedLogID = selectedRow[0]
- }
-
- return i, tea.Batch(cmds...)
-}
-
-func (i *tableCmp) View() string {
- t := theme.CurrentTheme()
- defaultStyles := table.DefaultStyles()
- defaultStyles.Selected = defaultStyles.Selected.Foreground(t.Primary())
- i.table.SetStyles(defaultStyles)
- return i.table.View()
-}
-
-func (i *tableCmp) GetSize() (int, int) {
- return i.table.Width(), i.table.Height()
-}
-
-func (i *tableCmp) SetSize(width int, height int) tea.Cmd {
- i.table.SetWidth(width)
- i.table.SetHeight(height)
- columns := i.table.Columns()
-
- // Calculate widths for visible columns
- timeWidth := 8 // Fixed width for Time column
- levelWidth := 7 // Fixed width for Level column
-
- // Message column gets the remaining space
- messageWidth := width - timeWidth - levelWidth - 5 // 5 for padding and borders
-
- // Set column widths
- columns[0].Width = 0 // ID column (hidden)
- columns[1].Width = timeWidth
- columns[2].Width = levelWidth
- columns[3].Width = messageWidth
-
- i.table.SetColumns(columns)
- return nil
-}
-
-func (i *tableCmp) BindingKeys() []key.Binding {
- return layout.KeyMapToSlice(i.table.KeyMap)
-}
-
-func NewLogsTable(app *app.App) TableComponent {
- columns := []table.Column{
- {Title: "ID", Width: 0}, // ID column with zero width
- {Title: "Time", Width: 8},
- {Title: "Level", Width: 7},
- {Title: "Message", Width: 30},
- }
-
- tableModel := table.New(
- table.WithColumns(columns),
- )
- tableModel.Focus()
- return &tableCmp{
- app: app,
- table: tableModel,
- logs: []logging.Log{},
- }
-}
-
-// Focus implements the focusable interface
-func (i *tableCmp) Focus() {
- i.focused = true
- i.table.Focus()
-}
-
-// Blur implements the blurable interface
-func (i *tableCmp) Blur() {
- i.focused = false
- i.table.Blur()
-}