summaryrefslogtreecommitdiff
path: root/internal/tui/dashboard
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-05 19:20:18 +0200
committerPaul Buetow <paul@buetow.org>2026-03-05 19:20:18 +0200
commitbab716a6a5931c211fad2f17ee2f67e8d182a7fc (patch)
tree89b16ed3bf85198dd63751d2e1e54537c9340a1b /internal/tui/dashboard
parent96225fb6159212a8851043a08d781aba721b4e78 (diff)
feat(tui): migrate Bubble Tea stack to charm.land v2
Diffstat (limited to 'internal/tui/dashboard')
-rw-r--r--internal/tui/dashboard/files.go2
-rw-r--r--internal/tui/dashboard/histogram_test.go2
-rw-r--r--internal/tui/dashboard/model.go8
-rw-r--r--internal/tui/dashboard/model_test.go78
-rw-r--r--internal/tui/dashboard/overview.go2
-rw-r--r--internal/tui/dashboard/overview_test.go2
-rw-r--r--internal/tui/dashboard/processes.go2
-rw-r--r--internal/tui/dashboard/syscalls.go2
-rw-r--r--internal/tui/dashboard/tabs.go2
9 files changed, 50 insertions, 50 deletions
diff --git a/internal/tui/dashboard/files.go b/internal/tui/dashboard/files.go
index 80e3037..0393553 100644
--- a/internal/tui/dashboard/files.go
+++ b/internal/tui/dashboard/files.go
@@ -7,7 +7,7 @@ import (
"sort"
"strconv"
- "github.com/charmbracelet/bubbles/table"
+ "charm.land/bubbles/v2/table"
)
type DirSnapshot struct {
diff --git a/internal/tui/dashboard/histogram_test.go b/internal/tui/dashboard/histogram_test.go
index 7790394..48297a2 100644
--- a/internal/tui/dashboard/histogram_test.go
+++ b/internal/tui/dashboard/histogram_test.go
@@ -6,7 +6,7 @@ import (
"ior/internal/statsengine"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/lipgloss/v2"
)
func TestRenderHistogramNoBuckets(t *testing.T) {
diff --git a/internal/tui/dashboard/model.go b/internal/tui/dashboard/model.go
index fc9caf6..39150e8 100644
--- a/internal/tui/dashboard/model.go
+++ b/internal/tui/dashboard/model.go
@@ -8,8 +8,8 @@ import (
"strings"
"time"
- "github.com/charmbracelet/bubbles/key"
- tea "github.com/charmbracelet/bubbletea"
+ "charm.land/bubbles/v2/key"
+ tea "charm.land/bubbletea/v2"
)
const defaultRefreshMs = 1000
@@ -288,7 +288,7 @@ func (m *Model) SetPidFilter(pid int) {
}
// View renders the tab bar, active tab scaffold, and help bar.
-func (m Model) View() string {
+func (m Model) View() tea.View {
width, height := common.EffectiveViewport(m.width, m.height)
activeHeight := height
streamModel := m.streamModel
@@ -319,7 +319,7 @@ func (m Model) View() string {
} else {
b.WriteString(renderHelpHint(width))
}
- return common.ScreenStyle.Render(b.String())
+ return tea.NewView(common.ScreenStyle.Render(b.String()))
}
func tickCmd(d time.Duration) tea.Cmd {
diff --git a/internal/tui/dashboard/model_test.go b/internal/tui/dashboard/model_test.go
index 87b60e3..642c702 100644
--- a/internal/tui/dashboard/model_test.go
+++ b/internal/tui/dashboard/model_test.go
@@ -12,7 +12,7 @@ import (
"ior/internal/tui/eventstream"
"ior/internal/tui/messages"
- tea "github.com/charmbracelet/bubbletea"
+ tea "charm.land/bubbletea/v2"
)
type fakeSnapshotSource struct {
@@ -28,31 +28,31 @@ func (f *fakeSnapshotSource) Snapshot() *statsengine.Snapshot {
func TestKeySwitchingChangesActiveTab(t *testing.T) {
m := NewModelWithConfig(nil, nil, 250, common.DefaultKeyMap())
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'2'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'2'}[0], Text: string([]rune{'2'})})
model := next.(Model)
if model.activeTab != TabSyscalls {
t.Fatalf("expected syscalls tab, got %v", model.activeTab)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyTab})
+ next, _ = model.Update(tea.KeyPressMsg{Code: tea.KeyTab})
model = next.(Model)
if model.activeTab != TabFiles {
t.Fatalf("expected next tab to be files, got %v", model.activeTab)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyShiftTab})
+ next, _ = model.Update(tea.KeyPressMsg{Code: tea.KeyTab, Mod: tea.ModShift})
model = next.(Model)
if model.activeTab != TabSyscalls {
t.Fatalf("expected previous tab to be syscalls, got %v", model.activeTab)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'7'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'7'}[0], Text: string([]rune{'7'})})
model = next.(Model)
if model.activeTab != TabStream {
t.Fatalf("expected stream tab on key 7, got %v", model.activeTab)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'6'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'6'}[0], Text: string([]rune{'6'})})
model = next.(Model)
if model.activeTab != TabStream {
t.Fatalf("expected stream tab on key 6, got %v", model.activeTab)
@@ -62,25 +62,25 @@ func TestKeySwitchingChangesActiveTab(t *testing.T) {
func TestArrowAndViKeysDoNotCycleTabs(t *testing.T) {
m := NewModelWithConfig(nil, nil, 250, common.DefaultKeyMap())
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRight})
+ next, _ := m.Update(tea.KeyPressMsg{Code: tea.KeyRight})
model := next.(Model)
if model.activeTab != TabOverview {
t.Fatalf("expected right arrow not to change tabs, got %v", model.activeTab)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'l'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'l'}[0], Text: string([]rune{'l'})})
model = next.(Model)
if model.activeTab != TabOverview {
t.Fatalf("expected l not to change tabs, got %v", model.activeTab)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyLeft})
+ next, _ = model.Update(tea.KeyPressMsg{Code: tea.KeyLeft})
model = next.(Model)
if model.activeTab != TabOverview {
t.Fatalf("expected left arrow not to change tabs, got %v", model.activeTab)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'h'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'h'}[0], Text: string([]rune{'h'})})
model = next.(Model)
if model.activeTab != TabOverview {
t.Fatalf("expected h not to change tabs, got %v", model.activeTab)
@@ -93,13 +93,13 @@ func TestSyscallsTabScrollsWithJK(t *testing.T) {
snap := statsengine.NewSnapshot(nil, nil, nil, []statsengine.SyscallSnapshot{{Name: "read", Count: 1}, {Name: "write", Count: 1}}, nil, nil, statsengine.HistogramSnapshot{}, statsengine.HistogramSnapshot{})
m.latest = &snap
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'j'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'j'}[0], Text: string([]rune{'j'})})
model := next.(Model)
if model.syscallsOffset != 1 {
t.Fatalf("expected offset 1 after j, got %d", model.syscallsOffset)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'k'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'k'}[0], Text: string([]rune{'k'})})
model = next.(Model)
if model.syscallsOffset != 0 {
t.Fatalf("expected offset 0 after k, got %d", model.syscallsOffset)
@@ -112,13 +112,13 @@ func TestProcessesTabScrollsWithJK(t *testing.T) {
snap := statsengine.NewSnapshot(nil, nil, nil, nil, nil, []statsengine.ProcessSnapshot{{PID: 1}, {PID: 2}}, statsengine.HistogramSnapshot{}, statsengine.HistogramSnapshot{})
m.latest = &snap
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'j'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'j'}[0], Text: string([]rune{'j'})})
model := next.(Model)
if model.processesOffset != 1 {
t.Fatalf("expected processes offset 1 after j, got %d", model.processesOffset)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'k'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'k'}[0], Text: string([]rune{'k'})})
model = next.(Model)
if model.processesOffset != 0 {
t.Fatalf("expected processes offset 0 after k, got %d", model.processesOffset)
@@ -131,13 +131,13 @@ func TestFilesTabScrollsWithJK(t *testing.T) {
snap := statsengine.NewSnapshot(nil, nil, nil, nil, []statsengine.FileSnapshot{{Path: "/a"}, {Path: "/b"}}, nil, statsengine.HistogramSnapshot{}, statsengine.HistogramSnapshot{})
m.latest = &snap
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'j'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'j'}[0], Text: string([]rune{'j'})})
model := next.(Model)
if model.filesOffset != 1 {
t.Fatalf("expected files offset 1 after j, got %d", model.filesOffset)
}
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'k'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'k'}[0], Text: string([]rune{'k'})})
model = next.(Model)
if model.filesOffset != 0 {
t.Fatalf("expected files offset 0 after k, got %d", model.filesOffset)
@@ -155,7 +155,7 @@ func TestFilesTabGroupedScrollUsesDirectoryOffset(t *testing.T) {
}, nil, statsengine.HistogramSnapshot{}, statsengine.HistogramSnapshot{})
m.latest = &snap
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'j'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'j'}[0], Text: string([]rune{'j'})})
model := next.(Model)
if model.filesDirOffset != 1 {
t.Fatalf("expected grouped dir offset 1 after j, got %d", model.filesDirOffset)
@@ -171,7 +171,7 @@ func TestStreamSpaceUnpauseSchedulesStreamTick(t *testing.T) {
m.activeTab = TabStream
m.streamModel.HandleKey("space") // pause
- next, cmd := m.Update(tea.KeyMsg{Type: tea.KeySpace})
+ next, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeySpace})
_ = next
if cmd == nil {
t.Fatalf("expected stream tick command when unpausing stream")
@@ -200,34 +200,34 @@ func TestStreamPausedSupportsJKArrowsAndPageKeys(t *testing.T) {
m.streamModel.Refresh()
_ = m.View()
- next, _ = m.Update(tea.KeyMsg{Type: tea.KeySpace}) // pause
+ next, _ = m.Update(tea.KeyPressMsg{Code: tea.KeySpace}) // pause
m = next.(Model)
- before := rowFromStreamView(t, m.View())
+ before := rowFromStreamView(t, m.View().Content)
- next, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'k'}})
+ next, _ = m.Update(tea.KeyPressMsg{Code: []rune{'k'}[0], Text: string([]rune{'k'})})
m = next.(Model)
- afterK := rowFromStreamView(t, m.View())
+ afterK := rowFromStreamView(t, m.View().Content)
if afterK >= before {
t.Fatalf("expected k to scroll up while paused: before=%d afterK=%d", before, afterK)
}
- next, _ = m.Update(tea.KeyMsg{Type: tea.KeyDown})
+ next, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyDown})
m = next.(Model)
- afterDown := rowFromStreamView(t, m.View())
+ afterDown := rowFromStreamView(t, m.View().Content)
if afterDown <= afterK {
t.Fatalf("expected down arrow to scroll down while paused: afterK=%d afterDown=%d", afterK, afterDown)
}
- next, _ = m.Update(tea.KeyMsg{Type: tea.KeyPgUp})
+ next, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyPgUp})
m = next.(Model)
- afterPgUp := rowFromStreamView(t, m.View())
+ afterPgUp := rowFromStreamView(t, m.View().Content)
if afterPgUp >= afterDown {
t.Fatalf("expected pgup to scroll up while paused: afterDown=%d afterPgUp=%d", afterDown, afterPgUp)
}
- next, _ = m.Update(tea.KeyMsg{Type: tea.KeyPgDown})
+ next, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyPgDown})
m = next.(Model)
- afterPgDown := rowFromStreamView(t, m.View())
+ afterPgDown := rowFromStreamView(t, m.View().Content)
if afterPgDown <= afterPgUp {
t.Fatalf("expected pgdown to scroll down while paused: afterPgUp=%d afterPgDown=%d", afterPgUp, afterPgDown)
}
@@ -251,14 +251,14 @@ func TestDirGroupKeyTogglesOnlyOnFilesTab(t *testing.T) {
m := NewModelWithConfig(nil, nil, 250, common.DefaultKeyMap())
m.activeTab = TabFiles
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'d'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'d'}[0], Text: string([]rune{'d'})})
model := next.(Model)
if !model.filesDirGrouped {
t.Fatalf("expected filesDirGrouped to toggle on files tab")
}
model.activeTab = TabOverview
- next, _ = model.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'d'}})
+ next, _ = model.Update(tea.KeyPressMsg{Code: []rune{'d'}[0], Text: string([]rune{'d'})})
model = next.(Model)
if !model.filesDirGrouped {
t.Fatalf("expected filesDirGrouped unchanged outside files tab")
@@ -272,7 +272,7 @@ func TestScrollOffsetDoesNotGrowUnbounded(t *testing.T) {
m.latest = &snap
for i := 0; i < 50; i++ {
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'j'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'j'}[0], Text: string([]rune{'j'})})
m = next.(Model)
}
if m.syscallsOffset != 1 {
@@ -284,7 +284,7 @@ func TestRefreshKeyEmitsRefreshTick(t *testing.T) {
snap := &statsengine.Snapshot{TotalSyscalls: 13}
engine := &fakeSnapshotSource{snap: snap}
m := NewModelWithConfig(engine, nil, 250, common.DefaultKeyMap())
- next, cmd := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'r'}})
+ next, cmd := m.Update(tea.KeyPressMsg{Code: []rune{'r'}[0], Text: string([]rune{'r'})})
_ = next
if cmd == nil {
t.Fatalf("expected refresh command")
@@ -366,7 +366,7 @@ func TestStatsTickClampsGroupedFilesOffset(t *testing.T) {
func TestViewRendersTabBarAndHelp(t *testing.T) {
m := NewModelWithConfig(nil, nil, 1000, common.DefaultKeyMap())
- out := m.View()
+ out := m.View().Content
if !strings.Contains(out, "Overview") {
t.Fatalf("expected overview label in view")
}
@@ -405,7 +405,7 @@ func TestStreamTabViewKeepsTabAndHelpChromeVisible(t *testing.T) {
m.streamModel.SetSource(rb)
m.streamModel.Refresh()
- out := m.View()
+ out := m.View().Content
if !strings.Contains(out, "1:Overview") {
t.Fatalf("expected tab bar to remain visible in stream view")
}
@@ -416,21 +416,21 @@ func TestStreamTabViewKeepsTabAndHelpChromeVisible(t *testing.T) {
func TestHelpToggleWithH(t *testing.T) {
m := NewModelWithConfig(nil, nil, 1000, common.DefaultKeyMap())
- out := m.View()
+ out := m.View().Content
if !strings.Contains(out, "press H for help") {
t.Fatalf("expected default help hint")
}
- next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'H'}})
+ next, _ := m.Update(tea.KeyPressMsg{Code: []rune{'H'}[0], Text: string([]rune{'H'})})
m = next.(Model)
- out = m.View()
+ out = m.View().Content
if !strings.Contains(out, "tab next tab") {
t.Fatalf("expected expanded help after pressing h")
}
- next, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'H'}})
+ next, _ = m.Update(tea.KeyPressMsg{Code: []rune{'H'}[0], Text: string([]rune{'H'})})
m = next.(Model)
- out = m.View()
+ out = m.View().Content
if !strings.Contains(out, "press H for help") {
t.Fatalf("expected help hint after pressing h again")
}
diff --git a/internal/tui/dashboard/overview.go b/internal/tui/dashboard/overview.go
index 5b8fab8..3ddeaf6 100644
--- a/internal/tui/dashboard/overview.go
+++ b/internal/tui/dashboard/overview.go
@@ -8,7 +8,7 @@ import (
"time"
"unicode/utf8"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/lipgloss/v2"
)
func renderOverview(snap *statsengine.Snapshot, width, height int) string {
diff --git a/internal/tui/dashboard/overview_test.go b/internal/tui/dashboard/overview_test.go
index 9895490..7de411c 100644
--- a/internal/tui/dashboard/overview_test.go
+++ b/internal/tui/dashboard/overview_test.go
@@ -7,7 +7,7 @@ import (
"ior/internal/statsengine"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/lipgloss/v2"
)
func TestRenderOverviewIncludesCoreMetrics(t *testing.T) {
diff --git a/internal/tui/dashboard/processes.go b/internal/tui/dashboard/processes.go
index 281a86a..a9de382 100644
--- a/internal/tui/dashboard/processes.go
+++ b/internal/tui/dashboard/processes.go
@@ -6,7 +6,7 @@ import (
"strconv"
"strings"
- "github.com/charmbracelet/bubbles/table"
+ "charm.land/bubbles/v2/table"
)
func renderProcesses(snap *statsengine.Snapshot, width, height int) string {
diff --git a/internal/tui/dashboard/syscalls.go b/internal/tui/dashboard/syscalls.go
index 23fe37c..31f719e 100644
--- a/internal/tui/dashboard/syscalls.go
+++ b/internal/tui/dashboard/syscalls.go
@@ -6,7 +6,7 @@ import (
"strconv"
"time"
- "github.com/charmbracelet/bubbles/table"
+ "charm.land/bubbles/v2/table"
)
func renderSyscalls(snap *statsengine.Snapshot, width, height int) string {
diff --git a/internal/tui/dashboard/tabs.go b/internal/tui/dashboard/tabs.go
index df8f03e..62c7762 100644
--- a/internal/tui/dashboard/tabs.go
+++ b/internal/tui/dashboard/tabs.go
@@ -6,7 +6,7 @@ import (
"strings"
"unicode/utf8"
- "github.com/charmbracelet/lipgloss"
+ "charm.land/lipgloss/v2"
)
// Tab is a dashboard tab identifier.