From 2baf3859fd65b1877b461b033277763fa8b0bfe7 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 2 Feb 2026 19:47:18 +0100 Subject: [PATCH] Add tab bar --- common/styles.go | 17 ++++++++++++++++ pages/main.go | 51 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/common/styles.go b/common/styles.go index f9aec89..c660919 100644 --- a/common/styles.go +++ b/common/styles.go @@ -27,6 +27,10 @@ type Styles struct { Form *huh.Theme TableStyle TableStyle + Tab lipgloss.Style + ActiveTab lipgloss.Style + TabBar lipgloss.Style + ColumnFocused lipgloss.Style ColumnBlurred lipgloss.Style ColumnInsert lipgloss.Style @@ -71,6 +75,19 @@ func NewStyles(config *taskwarrior.TWConfig) *Styles { styles.Form = formTheme + styles.Tab = lipgloss.NewStyle(). + Padding(0, 1). + Foreground(lipgloss.Color("240")) + + styles.ActiveTab = styles.Tab. + Foreground(lipgloss.Color("252")). + Bold(true) + + styles.TabBar = lipgloss.NewStyle(). + Border(lipgloss.NormalBorder(), false, false, true, false). + BorderForeground(lipgloss.Color("240")). + MarginBottom(1) + styles.ColumnFocused = lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), true).Padding(1) styles.ColumnBlurred = lipgloss.NewStyle().Border(lipgloss.HiddenBorder(), true).Padding(1) styles.ColumnInsert = lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), true).Padding(1) diff --git a/pages/main.go b/pages/main.go index f659718..fdb6e3b 100644 --- a/pages/main.go +++ b/pages/main.go @@ -5,6 +5,7 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" ) type MainPage struct { @@ -13,6 +14,9 @@ type MainPage struct { taskPage common.Component timePage common.Component + currentTab int + width int + height int } func NewMainPage(common *common.Common) *MainPage { @@ -24,6 +28,7 @@ func NewMainPage(common *common.Common) *MainPage { m.timePage = NewTimePage(common) m.activePage = m.taskPage + m.currentTab = 0 return m } @@ -37,17 +42,39 @@ func (m *MainPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height m.common.SetSize(msg.Width, msg.Height) + + tabHeight := lipgloss.Height(m.renderTabBar()) + contentHeight := msg.Height - tabHeight + if contentHeight < 0 { + contentHeight = 0 + } + + newMsg := tea.WindowSizeMsg{Width: msg.Width, Height: contentHeight} + activePage, cmd := m.activePage.Update(newMsg) + m.activePage = activePage.(common.Component) + return m, cmd + case tea.KeyMsg: // Only handle tab key for page switching when at the top level (no subpages active) if key.Matches(msg, m.common.Keymap.Next) && !m.common.HasSubpages() { if m.activePage == m.taskPage { m.activePage = m.timePage + m.currentTab = 1 } else { m.activePage = m.taskPage + m.currentTab = 0 } - // Re-size the new active page just in case - m.activePage.SetSize(m.common.Width(), m.common.Height()) + + tabHeight := lipgloss.Height(m.renderTabBar()) + contentHeight := m.height - tabHeight + if contentHeight < 0 { + contentHeight = 0 + } + m.activePage.SetSize(m.width, contentHeight) + // Trigger a refresh/init on switch? Maybe not needed if we keep state. // But we might want to refresh data. return m, m.activePage.Init() @@ -60,6 +87,22 @@ func (m *MainPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } -func (m *MainPage) View() string { - return m.activePage.View() +func (m *MainPage) renderTabBar() string { + var tabs []string + headers := []string{"Tasks", "Time"} + + for i, header := range headers { + style := m.common.Styles.Tab + if m.currentTab == i { + style = m.common.Styles.ActiveTab + } + tabs = append(tabs, style.Render(header)) + } + + row := lipgloss.JoinHorizontal(lipgloss.Top, tabs...) + return m.common.Styles.TabBar.Width(m.common.Width()).Render(row) +} + +func (m *MainPage) View() string { + return lipgloss.JoinVertical(lipgloss.Left, m.renderTabBar(), m.activePage.View()) }