prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

commit a63af0c3b7de6c9e1b83d4d21d24963ed54d41fe
parent 0c21684536e79eea0321f529fa2312a3350dd78d
Author: Damian Myrda <damian@prime8.dev>
Date:   Wed, 21 Oct 2026 08:00:24 +0000

feat(tui): accent-colored shared border on the projects tab

The focused pane now owns the divider between the list and readme panes,
junctions included, so the shared border is always drawn in the accent
color instead of half grey. The divider stays in the same column when
focus moves.

Diffstat:
Minternal/tui/helpers.go | 26++++++++++++++++++++++++++
Minternal/tui/view.go | 31++++++++++++++++++++-----------
2 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/internal/tui/helpers.go b/internal/tui/helpers.go @@ -79,6 +79,32 @@ func panelStyleSharedTop(style lipgloss.Style) lipgloss.Style { return style.BorderStyle(b) } +// panelStyleSharedRight draws a full border whose right edge is the divider +// shared with a pane to its right (which then omits its left border). +func panelStyleSharedRight(style lipgloss.Style) lipgloss.Style { + b := lipgloss.NormalBorder() + b.TopRight = "┬" + b.BottomRight = "┴" + return style.BorderStyle(b) +} + +// panelStyleSharedBottom draws a full border whose bottom edge is the divider +// shared with a pane below it (which then omits its top border). +func panelStyleSharedBottom(style lipgloss.Style) lipgloss.Style { + b := lipgloss.NormalBorder() + b.BottomLeft = "├" + b.BottomRight = "┤" + return style.BorderStyle(b) +} + +func panelStyleWithoutLeft(style lipgloss.Style) lipgloss.Style { + return style.Border(lipgloss.NormalBorder(), true, true, true, false) +} + +func panelStyleWithoutTop(style lipgloss.Style) lipgloss.Style { + return style.Border(lipgloss.NormalBorder(), false, true, true, true) +} + func panelStyleWithoutRight(style lipgloss.Style) lipgloss.Style { return style.Border(lipgloss.NormalBorder(), true, false, true, true) } diff --git a/internal/tui/view.go b/internal/tui/view.go @@ -172,27 +172,36 @@ func (m portfolioModel) renderBody(width int, height int) string { } } +// renderProjectsBody draws the list and detail panes with one shared divider. +// The focused pane owns the divider (and its ┬/┴ or ├/┤ junctions), so the +// divider is always drawn in the accent color; the divider stays in the same +// column/row whichever pane has focus. func (m portfolioModel) renderProjectsBody(bodyW int, height int) string { - listPaneStyle := panelStyle - detailPaneStyle := panelStyle - if m.projectReadmeScroll { - detailPaneStyle = panelActiveStyle - } else { - listPaneStyle = panelActiveStyle - } + listFocused := !m.projectReadmeScroll + listContent := func(w, h int) string { return m.projectListContent(w, h) } if bodyW < 72 { stackTop := max(height/2, 3) stackBottom := max(height-stackTop, 1) - listPane := renderPanelWithStyle(panelStyleWithoutBottom(listPaneStyle), bodyW, stackTop, m.projectListContent(bodyW-4, stackTop-2)) - detailPane := renderPanelWithStyle(panelStyleSharedTop(detailPaneStyle), bodyW, stackBottom, m.viewport.View()) + if listFocused { + listPane := renderPanelWithStyle(panelStyleSharedBottom(panelActiveStyle), bodyW, stackTop+1, listContent(bodyW-4, stackTop-2)) + detailPane := renderPanelWithStyle(panelStyleWithoutTop(panelStyle), bodyW, max(stackBottom-1, 1), m.viewport.View()) + return lipgloss.JoinVertical(lipgloss.Left, listPane, detailPane) + } + listPane := renderPanelWithStyle(panelStyleWithoutBottom(panelStyle), bodyW, stackTop, listContent(bodyW-4, stackTop-2)) + detailPane := renderPanelWithStyle(panelStyleSharedTop(panelActiveStyle), bodyW, stackBottom, m.viewport.View()) return lipgloss.JoinVertical(lipgloss.Left, listPane, detailPane) } listW := m.sidebarWidth(bodyW) detailW := max(bodyW-listW, 12) - listPane := renderPanelWithStyle(panelStyleWithoutRight(listPaneStyle), listW, height, m.projectListContent(listW-4, height-2)) - detailPane := renderPanelWithStyle(panelStyleSharedLeft(detailPaneStyle), detailW, height, m.viewport.View()) + if listFocused { + listPane := renderPanelWithStyle(panelStyleSharedRight(panelActiveStyle), listW+1, height, listContent(listW-4, height-2)) + detailPane := renderPanelWithStyle(panelStyleWithoutLeft(panelStyle), max(detailW-1, 1), height, m.viewport.View()) + return lipgloss.JoinHorizontal(lipgloss.Top, listPane, detailPane) + } + listPane := renderPanelWithStyle(panelStyleWithoutRight(panelStyle), listW, height, listContent(listW-4, height-2)) + detailPane := renderPanelWithStyle(panelStyleSharedLeft(panelActiveStyle), detailW, height, m.viewport.View()) return lipgloss.JoinHorizontal(lipgloss.Top, listPane, detailPane) }