diff options
Diffstat (limited to 'internal/tui')
| -rw-r--r-- | internal/tui/common/keys.go | 6 | ||||
| -rw-r--r-- | internal/tui/dashboard/files.go | 18 | ||||
| -rw-r--r-- | internal/tui/dashboard/files_test.go | 7 | ||||
| -rw-r--r-- | internal/tui/dashboard/histogram.go | 9 | ||||
| -rw-r--r-- | internal/tui/dashboard/model.go | 6 | ||||
| -rw-r--r-- | internal/tui/dashboard/model_test.go | 6 | ||||
| -rw-r--r-- | internal/tui/dashboard/overview.go | 3 | ||||
| -rw-r--r-- | internal/tui/dashboard/sparkline.go | 7 | ||||
| -rw-r--r-- | internal/tui/dashboard/tabs.go | 7 | ||||
| -rw-r--r-- | internal/tui/dashboard/tabs_test.go | 6 | ||||
| -rw-r--r-- | internal/tui/eventstream/render.go | 42 | ||||
| -rw-r--r-- | internal/tui/eventstream/render_test.go | 7 |
12 files changed, 76 insertions, 48 deletions
diff --git a/internal/tui/common/keys.go b/internal/tui/common/keys.go index 1abf214..7b70d5a 100644 --- a/internal/tui/common/keys.go +++ b/internal/tui/common/keys.go @@ -33,8 +33,8 @@ func DefaultKeyMap() KeyMap { Two: key.NewBinding(key.WithKeys("2"), key.WithHelp("2", "syscalls")), Three: key.NewBinding(key.WithKeys("3"), key.WithHelp("3", "files")), Four: key.NewBinding(key.WithKeys("4"), key.WithHelp("4", "processes")), - Five: key.NewBinding(key.WithKeys("5"), key.WithHelp("5", "latency")), - Six: key.NewBinding(key.WithKeys("6"), key.WithHelp("6", "gaps")), + Five: key.NewBinding(key.WithKeys("5"), key.WithHelp("5", "lat+gaps")), + Six: key.NewBinding(key.WithKeys("6"), key.WithHelp("6", "stream")), Seven: key.NewBinding(key.WithKeys("7"), key.WithHelp("7", "stream")), Export: key.NewBinding(key.WithKeys("e"), key.WithHelp("e", "export")), Quit: key.NewBinding(key.WithKeys("q", "ctrl+c"), key.WithHelp("q", "quit")), @@ -64,7 +64,7 @@ func (k KeyMap) DashboardFullHelp() [][]key.Binding { controls = append(controls, k.Refresh, k.Help, k.Quit) return [][]key.Binding{ - {k.One, k.Two, k.Three, k.Four, k.Five, k.Six, k.Seven}, + {k.One, k.Two, k.Three, k.Four, k.Five, k.Six}, controls, { helpTextBinding("left/right", "tab"), diff --git a/internal/tui/dashboard/files.go b/internal/tui/dashboard/files.go index 945869e..faade8d 100644 --- a/internal/tui/dashboard/files.go +++ b/internal/tui/dashboard/files.go @@ -17,18 +17,19 @@ func renderFilesWithOffset(snap *statsengine.Snapshot, width, height, offset int return "Files: waiting for stats..." } - rows := fileRows(snap.Files()) + pathWidth := filePathWidth(width) + rows := fileRows(snap.Files(), pathWidth) if len(rows) == 0 { return "Files: no data" } columns := []table.Column{ - {Title: "Path", Width: filePathWidth(width)}, {Title: "Accesses", Width: 8}, {Title: "Read", Width: 9}, {Title: "Write", Width: 9}, {Title: "Avg Latency", Width: 11}, {Title: "Max Latency", Width: 11}, + {Title: "Path", Width: pathWidth}, } tbl := table.New( @@ -43,16 +44,16 @@ func renderFilesWithOffset(snap *statsengine.Snapshot, width, height, offset int return tbl.View() + fmt.Sprintf("\nRow %d/%d", cursor+1, len(rows)) } -func fileRows(files []statsengine.FileSnapshot) []table.Row { +func fileRows(files []statsengine.FileSnapshot, pathWidth int) []table.Row { rows := make([]table.Row, 0, len(files)) for _, f := range files { rows = append(rows, table.Row{ - truncatePathMiddle(f.Path, 48), strconv.FormatUint(f.Accesses, 10), formatBytes(float64(f.BytesRead)), formatBytes(float64(f.BytesWritten)), formatDurationNs(f.AvgLatencyNs), formatDurationUintNs(f.MaxLatencyNs), + truncatePathMiddle(f.Path, pathWidth), }) } return rows @@ -62,15 +63,12 @@ func filePathWidth(width int) int { if width <= 0 { return 24 } - // Reserve enough room for non-path columns and table separators so - // latency columns remain visible even on narrower terminals. - w := width - 70 + // Keep fixed metrics visible and let path consume the remaining space. + // Fixed columns sum to 48 chars; reserve extra for separators/padding. + w := width - 58 if w < 14 { return 14 } - if w > 52 { - return 52 - } return w } diff --git a/internal/tui/dashboard/files_test.go b/internal/tui/dashboard/files_test.go index b0a5dbf..6d73b14 100644 --- a/internal/tui/dashboard/files_test.go +++ b/internal/tui/dashboard/files_test.go @@ -49,3 +49,10 @@ func TestTruncatePathMiddle(t *testing.T) { t.Fatalf("expected head and tail preservation, got %q", got) } } + +func TestFilePathWidthExpandsOnWideTerminal(t *testing.T) { + got := filePathWidth(180) + if got <= 80 { + t.Fatalf("expected wide path column to use remaining space, got %d", got) + } +} diff --git a/internal/tui/dashboard/histogram.go b/internal/tui/dashboard/histogram.go index b2bb88e..1e68a7b 100644 --- a/internal/tui/dashboard/histogram.go +++ b/internal/tui/dashboard/histogram.go @@ -29,6 +29,15 @@ func renderGapsTab(snap *statsengine.Snapshot, width, height int) string { return strings.Join([]string{hist, spark}, "\n") } +func renderLatencyGapsTab(snap *statsengine.Snapshot, width, height int) string { + if snap == nil { + return common.PanelStyle.Render("Latency+Gaps: waiting for stats...") + } + lat := renderLatencyTab(snap, width, height) + gap := renderGapsTab(snap, width, height) + return strings.Join([]string{lat, gap}, "\n") +} + func renderHistogram(hist statsengine.HistogramSnapshot, title string, width, height int) string { buckets := hist.Buckets() if len(buckets) == 0 { diff --git a/internal/tui/dashboard/model.go b/internal/tui/dashboard/model.go index 407802f..78da351 100644 --- a/internal/tui/dashboard/model.go +++ b/internal/tui/dashboard/model.go @@ -127,7 +127,7 @@ func (m Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { m.activeTab = TabLatency handled = true case key.Matches(msg, m.keys.Six): - m.activeTab = TabGaps + m.activeTab = TabStream handled = true case key.Matches(msg, m.keys.Seven): m.activeTab = TabStream @@ -278,9 +278,7 @@ func renderActiveTab(tab Tab, snap *statsengine.Snapshot, streamModel *eventstre case TabProcesses: return renderProcessesWithOffset(snap, width, height, processesOffset) case TabLatency: - return renderLatencyTab(snap, width, height) - case TabGaps: - return renderGapsTab(snap, width, height) + return renderLatencyGapsTab(snap, width, height) default: return common.PanelStyle.Render("Unknown tab") } diff --git a/internal/tui/dashboard/model_test.go b/internal/tui/dashboard/model_test.go index b0ce933..29b698d 100644 --- a/internal/tui/dashboard/model_test.go +++ b/internal/tui/dashboard/model_test.go @@ -47,6 +47,12 @@ func TestKeySwitchingChangesActiveTab(t *testing.T) { 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'}}) + model = next.(Model) + if model.activeTab != TabStream { + t.Fatalf("expected stream tab on key 6, got %v", model.activeTab) + } } func TestArrowAndViKeysCycleTabs(t *testing.T) { diff --git a/internal/tui/dashboard/overview.go b/internal/tui/dashboard/overview.go index 1bdb64f..9feafab 100644 --- a/internal/tui/dashboard/overview.go +++ b/internal/tui/dashboard/overview.go @@ -214,8 +214,5 @@ func sparklineWidth(width int) int { if w < 8 { return 8 } - if w > 80 { - return 80 - } return w } diff --git a/internal/tui/dashboard/sparkline.go b/internal/tui/dashboard/sparkline.go index 1531ca6..9c1f2c4 100644 --- a/internal/tui/dashboard/sparkline.go +++ b/internal/tui/dashboard/sparkline.go @@ -11,8 +11,10 @@ func renderSparkline(data []float64, width int) string { samples := sampleForWidth(data, width) min, max := minMax(samples) + line := "" if min == max { - return repeatRune('▄', len(samples)) + line = repeatRune('▄', len(samples)) + return line + "\n" + line } out := make([]rune, len(samples)) @@ -28,7 +30,8 @@ func renderSparkline(data []float64, width int) string { } out[i] = sparkChars[idx] } - return string(out) + line = string(out) + return line + "\n" + line } func sampleForWidth(data []float64, width int) []float64 { diff --git a/internal/tui/dashboard/tabs.go b/internal/tui/dashboard/tabs.go index 9aae218..a2fe366 100644 --- a/internal/tui/dashboard/tabs.go +++ b/internal/tui/dashboard/tabs.go @@ -22,8 +22,6 @@ const ( TabProcesses // TabLatency is the latency histogram tab. TabLatency - // TabGaps is the inter-syscall gap tab. - TabGaps // TabStream is the live event stream tab. TabStream ) @@ -34,7 +32,6 @@ var allTabs = []Tab{ TabFiles, TabProcesses, TabLatency, - TabGaps, TabStream, } @@ -49,9 +46,7 @@ func (t Tab) String() string { case TabProcesses: return "Processes" case TabLatency: - return "Latency" - case TabGaps: - return "Gaps" + return "Latency+Gaps" case TabStream: return "Stream" default: diff --git a/internal/tui/dashboard/tabs_test.go b/internal/tui/dashboard/tabs_test.go index d40cc68..0fc36f2 100644 --- a/internal/tui/dashboard/tabs_test.go +++ b/internal/tui/dashboard/tabs_test.go @@ -6,8 +6,8 @@ import ( ) func TestTabNavigationWraps(t *testing.T) { - if got := nextTab(TabGaps); got != TabStream { - t.Fatalf("expected next after gaps to be stream, got %v", got) + if got := nextTab(TabLatency); got != TabStream { + t.Fatalf("expected next after latency+gaps to be stream, got %v", got) } if got := nextTab(TabStream); got != TabOverview { t.Fatalf("expected wrap to overview from stream, got %v", got) @@ -19,7 +19,7 @@ func TestTabNavigationWraps(t *testing.T) { func TestRenderTabBarContainsLabels(t *testing.T) { out := renderTabBar(TabOverview, 80) - for _, label := range []string{"Overview", "Syscalls", "Files", "Processes", "Latency", "Gaps", "Stream"} { + for _, label := range []string{"Overview", "Syscalls", "Files", "Processes", "Latency+Gaps", "Stream"} { if !strings.Contains(out, label) { t.Fatalf("expected tab label %q in tab bar", label) } diff --git a/internal/tui/eventstream/render.go b/internal/tui/eventstream/render.go index 1f748a3..e1781f8 100644 --- a/internal/tui/eventstream/render.go +++ b/internal/tui/eventstream/render.go @@ -94,30 +94,38 @@ func computeColumnLayout(width int) columnLayout { width = 100 } - gap := 8 - latency := 9 - comm := 14 - pidTid := 12 - syscall := 11 - ret := 6 - bytes := 10 + // Keep non-file columns compact so file paths can use most of the row. + gap := 7 + latency := 8 + comm := 10 + pidTid := 10 + syscall := 9 + ret := 5 + bytes := 8 fixed := gap + latency + comm + pidTid + syscall + ret + bytes + 7 file := width - fixed - if file >= 18 { + if file >= 28 { + // On wider terminals, give a little more room back to descriptive columns. + if width >= 140 { + comm = 12 + syscall = 11 + pidTid = 11 + fixed = gap + latency + comm + pidTid + syscall + ret + bytes + 7 + file = width - fixed + } return columnLayout{gap: gap, latency: latency, comm: comm, pidTid: pidTid, syscall: syscall, ret: ret, bytes: bytes, file: file} } - if width < 90 { - comm = 10 - syscall = 9 - } else { - comm = 12 - syscall = 10 - } + // Very narrow widths: compress further but keep file column readable. + comm = 8 + pidTid = 9 + syscall = 8 + ret = 4 + bytes = 7 fixed = gap + latency + comm + pidTid + syscall + ret + bytes + 7 file = width - fixed - if file < 8 { - file = 8 + if file < 12 { + file = 12 } return columnLayout{gap: gap, latency: latency, comm: comm, pidTid: pidTid, syscall: syscall, ret: ret, bytes: bytes, file: file} } diff --git a/internal/tui/eventstream/render_test.go b/internal/tui/eventstream/render_test.go index 89c2029..c7f32cd 100644 --- a/internal/tui/eventstream/render_test.go +++ b/internal/tui/eventstream/render_test.go @@ -93,3 +93,10 @@ func TestRenderEventRowIsSingleLineWithControlCharsAndLongValues(t *testing.T) { t.Fatalf("expected truncation ellipsis in narrow row, got %q", row) } } + +func TestComputeColumnLayoutGivesFileMoreSpace(t *testing.T) { + cols := computeColumnLayout(120) + if cols.file < 55 { + t.Fatalf("expected file column to get most width, got %d", cols.file) + } +} |
