commit 0c21684536e79eea0321f529fa2312a3350dd78d
parent 4c084e8a399a8521950a8edaf9b58c4f7eca67d2
Author: Damian Myrda <damian@prime8.dev>
Date: Wed, 21 Oct 2026 02:51:16 +0000
feat: dwm-style tab bar (web + tui)
Diffstat:
4 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/internal/tui/styles.go b/internal/tui/styles.go
@@ -46,7 +46,7 @@ var (
promptCmdStyle = lipgloss.NewStyle().Foreground(colorFg)
selectedRowStyle = lipgloss.NewStyle().Foreground(colorAccent).Bold(true)
- tabActiveStyle = selectedRowStyle
+ tabActiveStyle = lipgloss.NewStyle().Background(colorAccent).Foreground(lipgloss.Color("0")).Bold(true)
rowStyle = lipgloss.NewStyle().Foreground(colorFg)
dimStyle = lipgloss.NewStyle().Foreground(colorDim)
titleStyle = lipgloss.NewStyle().Foreground(colorAccent).Bold(true)
diff --git a/internal/tui/view.go b/internal/tui/view.go
@@ -133,7 +133,7 @@ func (m portfolioModel) renderBoot(width int, height int) string {
func (m portfolioModel) renderHeader(width int) string {
parts := make([]string, 0, len(tabLabels))
for i, label := range tabLabels {
- t := fmt.Sprintf("[%d] %s", i+1, label)
+ t := fmt.Sprintf(" %d %s ", i+1, label)
if i == m.activeTab {
t = tabActiveStyle.Render(t)
} else {
@@ -142,7 +142,7 @@ func (m portfolioModel) renderHeader(width int) string {
parts = append(parts, t)
}
- left := strings.Join(parts, " ")
+ left := strings.Join(parts, "")
right := ""
if width >= 96 {
right = dimStyle.Render("▲ ▼ ◀ ▶ navigate · tab cycle · ? help")
diff --git a/internal/web/styles/header.css b/internal/web/styles/header.css
@@ -18,16 +18,19 @@
.tab-nav {
display: flex;
- align-items: center;
- gap: 4px;
+ align-items: stretch;
+ align-self: stretch;
+ margin-left: -12px;
}
+/* dwm-style tags: flush cells, the selected one filled */
.tab {
- padding: 6px 12px;
+ display: flex;
+ align-items: center;
+ padding: 0 10px;
color: var(--fg-dim);
cursor: pointer;
- border-bottom: 2px solid transparent;
- transition: color 0.15s, border-color 0.15s;
+ text-decoration: none;
}
.tab:hover {
@@ -35,8 +38,9 @@
}
.tab.active {
- color: var(--accent);
- border-bottom-color: var(--accent);
+ background: var(--accent);
+ color: var(--bg);
+ font-weight: bold;
}
.tab-spacer {
@@ -82,18 +86,19 @@
flex-wrap: wrap;
height: auto;
min-height: 36px;
- padding: 8px 12px;
- gap: 8px;
+ padding: 0;
+ gap: 0;
}
.tab-nav {
width: 100%;
- justify-content: space-between;
- gap: 0;
+ margin-left: 0;
}
.tab {
- padding: 6px 8px;
+ justify-content: center;
+ min-height: 36px;
+ padding: 0 8px;
font-size: var(--fs-12);
flex: 1;
text-align: center;
diff --git a/internal/web/templates/partials/header.gohtml b/internal/web/templates/partials/header.gohtml
@@ -3,7 +3,7 @@
<nav class="tab-nav" aria-label="main navigation" preload="preload:init" preload-images="true">
{{range .Tabs}}
<a href="{{tabPath .ID}}" class="tab{{if eq .ID $.ActiveTab}} active{{end}}" data-tab="{{.ID}}" data-label="{{.ID}}" hx-get="{{tabPath .ID}}" hx-target="#content" hx-push-url="true">
- <span class="tab-num">[{{.Num}}] </span>{{.ID}}
+ <span class="tab-num">{{.Num}} </span>{{.ID}}
</a>
{{end}}
</nav>