commit 0a9b317b4156e89ff147f2775b6514f608e4c015
parent b12defe0b9080a02a6dadce99d0253727a25e7aa
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:
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)
}