prime8dev

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

commit 4c83ff3b86587ff7c0f42378842ff127c2add16a
parent fb7385222352256b6576509ff7d333771fd3e13e
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) }