feat(tui): improve scrollbar visibility and extend to all panels
CI / test (pull_request) Successful in 1m46s
CI / lint (pull_request) Successful in 1m16s
CI / vulncheck (pull_request) Successful in 56s

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.
This commit is contained in:
2026-07-02 11:09:07 -04:00
parent d6ba7d9af8
commit 1cfa0571c8
3 changed files with 27 additions and 6 deletions
+5 -2
View File
@@ -70,6 +70,9 @@ func (m Model) titledPanelH(title, content, footer string, width, height, scroll
} }
visible := contentLines[scrollOffset:end] visible := contentLines[scrollOffset:end]
if sb.total == 0 && len(contentLines) > bodyH {
sb = scrollbar{pos: scrollOffset, total: len(contentLines), visible: bodyH}
}
sbVisible := sb.visible sbVisible := sb.visible
if sbVisible <= 0 { if sbVisible <= 0 {
sbVisible = bodyH sbVisible = bodyH
@@ -96,8 +99,8 @@ func (m Model) titledPanelH(title, content, footer string, width, height, scroll
} }
} }
scrollTrack := bc.Render("") scrollTrack := lipgloss.NewStyle().Foreground(m.theme.Border).Render("")
scrollThumb := lipgloss.NewStyle().Foreground(m.theme.Muted).Render("") scrollThumb := lipgloss.NewStyle().Foreground(m.theme.Accent).Render("")
borderLine := func(line string, idx int) string { borderLine := func(line string, idx int) string {
rightBorder := bc.Render("│") rightBorder := bc.Render("│")
+14
View File
@@ -72,6 +72,20 @@ func (m Model) viewLogsStrip(width, maxLines int) string {
return style.Render(strings.Join(visible, "\n")) 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) { func (m *Model) scrollLogs(delta int) {
logs := m.engine.GetLogs() logs := m.engine.GetLogs()
total := 0 total := 0
+8 -4
View File
@@ -189,12 +189,16 @@ func (m Model) viewMonitorsLayout() string {
switch m.bottomPanel { switch m.bottomPanel {
case bottomLogs: case bottomLogs:
logContent := m.viewLogsStrip(availW-2, logsStripHeight-2) maxLines := logsStripHeight - 2
logPanel := m.zones.Mark("panel-logs", m.titledPanel("Logs", logContent, availW, m.focusedPanel == panelLogs)) 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 return top + "\n" + logPanel
case bottomMaint: case bottomMaint:
maintContent := m.viewMaintStrip(availW-2, logsStripHeight-2) maxLines := logsStripHeight - 2
maintPanel := m.zones.Mark("panel-maint", m.titledPanel("Maint", maintContent, availW, m.focusedPanel == panelMaint)) 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 + "\n" + maintPanel
} }
return top return top