From 1cfa0571c83548ffe95e6be74a5122296b4bb09d Mon Sep 17 00:00:00 2001 From: Tyler Koenig Date: Thu, 2 Jul 2026 11:09:07 -0400 Subject: [PATCH] feat(tui): improve scrollbar visibility and extend to all panels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use block characters (█/░) with accent coloring for scrollbar thumb/track instead of box-drawing chars that blend with panel borders. Auto-derive scrollbar from content overflow in titledPanelH. Add scrollbar to logs and maintenance bottom panels. --- internal/tui/panel.go | 7 +++++-- internal/tui/tab_logs_sidebar.go | 14 ++++++++++++++ internal/tui/view_dashboard.go | 12 ++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/internal/tui/panel.go b/internal/tui/panel.go index a593fec..16e5fee 100644 --- a/internal/tui/panel.go +++ b/internal/tui/panel.go @@ -70,6 +70,9 @@ func (m Model) titledPanelH(title, content, footer string, width, height, scroll } visible := contentLines[scrollOffset:end] + if sb.total == 0 && len(contentLines) > bodyH { + sb = scrollbar{pos: scrollOffset, total: len(contentLines), visible: bodyH} + } sbVisible := sb.visible if sbVisible <= 0 { sbVisible = bodyH @@ -96,8 +99,8 @@ func (m Model) titledPanelH(title, content, footer string, width, height, scroll } } - scrollTrack := bc.Render("│") - scrollThumb := lipgloss.NewStyle().Foreground(m.theme.Muted).Render("┃") + scrollTrack := lipgloss.NewStyle().Foreground(m.theme.Border).Render("░") + scrollThumb := lipgloss.NewStyle().Foreground(m.theme.Accent).Render("█") borderLine := func(line string, idx int) string { rightBorder := bc.Render("│") diff --git a/internal/tui/tab_logs_sidebar.go b/internal/tui/tab_logs_sidebar.go index dcd5da5..8113aad 100644 --- a/internal/tui/tab_logs_sidebar.go +++ b/internal/tui/tab_logs_sidebar.go @@ -72,6 +72,20 @@ func (m Model) viewLogsStrip(width, maxLines int) string { return style.Render(strings.Join(visible, "\n")) } +func (m Model) filteredLogCount() int { + count := 0 + for _, entry := range m.engine.GetLogs() { + if strings.TrimSpace(entry.Message) == "" { + continue + } + if m.logFilterImportant && !isImportantLog(classifyLog(entry.Message)) { + continue + } + count++ + } + return count +} + func (m *Model) scrollLogs(delta int) { logs := m.engine.GetLogs() total := 0 diff --git a/internal/tui/view_dashboard.go b/internal/tui/view_dashboard.go index 8ddcb1b..b284340 100644 --- a/internal/tui/view_dashboard.go +++ b/internal/tui/view_dashboard.go @@ -189,12 +189,16 @@ func (m Model) viewMonitorsLayout() string { switch m.bottomPanel { case bottomLogs: - logContent := m.viewLogsStrip(availW-2, logsStripHeight-2) - logPanel := m.zones.Mark("panel-logs", m.titledPanel("Logs", logContent, availW, m.focusedPanel == panelLogs)) + maxLines := logsStripHeight - 2 + logContent := m.viewLogsStrip(availW-2, maxLines) + totalLogs := m.filteredLogCount() + logPanel := m.zones.Mark("panel-logs", m.titledPanelH("Logs", logContent, "", availW, logsStripHeight, 0, scrollbar{pos: m.logScrollOffset, total: totalLogs, visible: maxLines}, m.focusedPanel == panelLogs)) return top + "\n" + logPanel case bottomMaint: - maintContent := m.viewMaintStrip(availW-2, logsStripHeight-2) - maintPanel := m.zones.Mark("panel-maint", m.titledPanel("Maint", maintContent, availW, m.focusedPanel == panelMaint)) + maxLines := logsStripHeight - 2 + maintContent := m.viewMaintStrip(availW-2, maxLines) + totalMaint := len(m.activeMaintWindows()) + maintPanel := m.zones.Mark("panel-maint", m.titledPanelH("Maint", maintContent, "", availW, logsStripHeight, 0, scrollbar{pos: 0, total: totalMaint, visible: maxLines}, m.focusedPanel == panelMaint)) return top + "\n" + maintPanel } return top