package tui import ( "time" tea "github.com/charmbracelet/bubbletea" ) // tickMsg is sent periodically to refresh the TUI type tickMsg time.Time // tickCmd returns a command that sends a tickMsg after 100ms func tickCmd() tea.Cmd { return tea.Tick(100*time.Millisecond, func(t time.Time) tea.Msg { return tickMsg(t) }) } // Init initializes the model and starts the tick loop func (m Model) Init() tea.Cmd { return tickCmd() } // Update handles messages and updates the model func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tickMsg: // Tick fired, refresh and continue ticking return m, tickCmd() case tea.KeyMsg: return m.handleKeypress(msg) case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height return m, nil } return m, nil } // handleKeypress handles keyboard input func (m Model) handleKeypress(msg tea.KeyMsg) (tea.Model, tea.Cmd) { switch msg.String() { case "q", "ctrl+c": m.quitting = true return m, tea.Quit case "tab": // Switch focus between panels if m.focusedPanel == PanelControls { m.focusedPanel = PanelLogs } else { m.focusedPanel = PanelControls } return m, nil case "up", "k": if m.focusedPanel == PanelControls { if m.focusedCtrl > 0 { m.focusedCtrl-- } } else { // Scroll logs up maxOffset := max(m.logBuffer.Len()-(m.height-10), 0) if m.logOffset < maxOffset { m.logOffset++ } } return m, nil case "down", "j": if m.focusedPanel == PanelControls { if m.focusedCtrl < len(m.controls)-1 { m.focusedCtrl++ } } else { // Scroll logs down if m.logOffset > 0 { m.logOffset-- } } return m, nil case "left", "h": if m.focusedPanel == PanelControls { ctrl := &m.controls[m.focusedCtrl] if ctrl.Type == ControlSlider { value := ctrl.GetValue() newValue := max(value-ctrl.Step, ctrl.Min) ctrl.SetValue(newValue) } } return m, nil case "right", "l": if m.focusedPanel == PanelControls { ctrl := &m.controls[m.focusedCtrl] if ctrl.Type == ControlSlider { value := ctrl.GetValue() newValue := min(value+ctrl.Step, ctrl.Max) ctrl.SetValue(newValue) } } return m, nil case "enter", " ": if m.focusedPanel == PanelControls { ctrl := &m.controls[m.focusedCtrl] if ctrl.Type == ControlButton && ctrl.Action != nil { ctrl.Action() } } return m, nil case "home": // Jump to top of logs if m.focusedPanel == PanelLogs { maxOffset := m.logBuffer.Len() - (m.height - 10) if maxOffset > 0 { m.logOffset = maxOffset } } return m, nil case "end": // Jump to bottom of logs if m.focusedPanel == PanelLogs { m.logOffset = 0 } return m, nil case "pgup": if m.focusedPanel == PanelLogs { pageSize := m.height - 10 maxOffset := max(m.logBuffer.Len()-pageSize, 0) m.logOffset = min(m.logOffset+pageSize, maxOffset) } return m, nil case "pgdown": if m.focusedPanel == PanelLogs { pageSize := m.height - 10 m.logOffset = max(m.logOffset-pageSize, 0) } return m, nil } return m, nil }