summaryrefslogtreecommitdiff
path: root/internal/tui
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-05 22:20:05 +0200
committerPaul Buetow <paul@buetow.org>2026-03-05 22:20:05 +0200
commit501f514b1a357d695def12e262131d9f49a6c480 (patch)
treefa7521bda7b8025f0cdda4572df9f1322df6328b /internal/tui
parent043bbbb884560a0f91f5e12d0b7851ad60121d5a (diff)
task 351: add flamegraph TUI model scaffold
Diffstat (limited to 'internal/tui')
-rw-r--r--internal/tui/flamegraph/model.go104
-rw-r--r--internal/tui/flamegraph/model_test.go31
2 files changed, 135 insertions, 0 deletions
diff --git a/internal/tui/flamegraph/model.go b/internal/tui/flamegraph/model.go
new file mode 100644
index 0000000..dd77201
--- /dev/null
+++ b/internal/tui/flamegraph/model.go
@@ -0,0 +1,104 @@
+package flamegraph
+
+import (
+ "image/color"
+ coreflamegraph "ior/internal/flamegraph"
+ common "ior/internal/tui/common"
+
+ tea "charm.land/bubbletea/v2"
+)
+
+type snapshotNode struct {
+ Name string `json:"n"`
+ Value uint64 `json:"v"`
+ Total uint64 `json:"t"`
+ Children []*snapshotNode `json:"c,omitempty"`
+}
+
+type zoomState struct {
+ path string
+ selectedIdx int
+}
+
+type frameSpring struct{}
+
+// Model is the Bubble Tea model for the TUI flamegraph tab.
+type Model struct {
+ liveTrie *coreflamegraph.LiveTrie
+ lastVersion uint64
+ snapshot *snapshotNode
+
+ frames []tuiFrame
+ targetFrames []tuiFrame
+ width int
+ height int
+
+ selectedIdx int
+ zoomStack []zoomState
+ zoomRoot *snapshotNode
+
+ searchActive bool
+ searchQuery string
+ matchIndices map[int]bool
+
+ fieldPresets [][]string
+ fieldIndex int
+
+ springs []frameSpring
+ paused bool
+ isDark bool
+}
+
+// tuiFrame stores one terminal flamegraph frame cell.
+type tuiFrame struct {
+ Name string
+ Col int
+ Row int
+ Width int
+ Total uint64
+ Percent float64
+ Fill color.Color
+ Depth int
+ Path string
+}
+
+// NewModel constructs a flamegraph tab model with default state.
+func NewModel(liveTrie *coreflamegraph.LiveTrie) Model {
+ return Model{
+ liveTrie: liveTrie,
+ matchIndices: make(map[int]bool),
+ fieldPresets: [][]string{
+ {"comm", "path"},
+ {"tracepoint", "comm", "path"},
+ {"pid", "tid", "comm", "path"},
+ },
+ isDark: true,
+ }
+}
+
+// Init starts the flamegraph model.
+func (m Model) Init() tea.Cmd {
+ return nil
+}
+
+// Update handles incoming messages.
+func (m Model) Update(tea.Msg) (tea.Model, tea.Cmd) {
+ return m, nil
+}
+
+// View renders the flamegraph viewport.
+func (m Model) View() tea.View {
+ content := common.PanelStyle.Render("Flame: model scaffold")
+ return tea.NewView(content)
+}
+
+// SetViewport updates model render dimensions.
+func (m *Model) SetViewport(width, height int) {
+ m.width = width
+ m.height = height
+}
+
+// SetDarkMode sets the active color theme mode.
+func (m *Model) SetDarkMode(isDark bool) {
+ m.isDark = isDark
+}
diff --git a/internal/tui/flamegraph/model_test.go b/internal/tui/flamegraph/model_test.go
new file mode 100644
index 0000000..42729bb
--- /dev/null
+++ b/internal/tui/flamegraph/model_test.go
@@ -0,0 +1,31 @@
+package flamegraph
+
+import "testing"
+
+func TestNewModelDefaults(t *testing.T) {
+ m := NewModel(nil)
+ if m.liveTrie != nil {
+ t.Fatalf("expected nil liveTrie when constructor input is nil")
+ }
+ if m.matchIndices == nil {
+ t.Fatalf("expected matchIndices map to be initialized")
+ }
+ if len(m.fieldPresets) == 0 {
+ t.Fatalf("expected default field presets to be initialized")
+ }
+ if !m.isDark {
+ t.Fatalf("expected dark mode enabled by default")
+ }
+}
+
+func TestSetViewportAndDarkMode(t *testing.T) {
+ m := NewModel(nil)
+ m.SetViewport(120, 40)
+ m.SetDarkMode(false)
+ if m.width != 120 || m.height != 40 {
+ t.Fatalf("expected viewport 120x40, got %dx%d", m.width, m.height)
+ }
+ if m.isDark {
+ t.Fatalf("expected dark mode to be disabled")
+ }
+}