summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-26 22:14:49 +0200
committerPaul Buetow <paul@buetow.org>2026-02-26 22:14:49 +0200
commita5f53e8f5c90956f0aaed13aa79fc5ccb1d6bc15 (patch)
tree8b88dbb3b6a243b90c88882e4f4d2bc6f046657b
parentda547fb7a1654220a40d0b1dbed30d51b26bf485 (diff)
tui: align overview sparkline columns
-rw-r--r--internal/tui/dashboard/overview.go41
-rw-r--r--internal/tui/dashboard/overview_test.go35
2 files changed, 73 insertions, 3 deletions
diff --git a/internal/tui/dashboard/overview.go b/internal/tui/dashboard/overview.go
index 990e36d..5b8fab8 100644
--- a/internal/tui/dashboard/overview.go
+++ b/internal/tui/dashboard/overview.go
@@ -34,9 +34,10 @@ func renderOverview(snap *statsengine.Snapshot, width, height int) string {
)
panelInner := panelInnerWidth(width)
- latencySpark := renderOverviewSparkline("Latency:", snap.LatencySeriesNs(), panelInner)
- gapSpark := renderOverviewSparkline("Gap:", snap.GapSeriesNs(), panelInner)
- throughputSpark := renderOverviewSparkline("Throughput:", snap.ThroughputSeriesB(), panelInner)
+ labelWidth := maxLabelWidth("Latency:", "Gap:", "Throughput:")
+ latencySpark := renderOverviewSparklineAligned("Latency:", snap.LatencySeriesNs(), panelInner, labelWidth)
+ gapSpark := renderOverviewSparklineAligned("Gap:", snap.GapSeriesNs(), panelInner, labelWidth)
+ throughputSpark := renderOverviewSparklineAligned("Throughput:", snap.ThroughputSeriesB(), panelInner, labelWidth)
topSyscalls := "Top syscalls: " + summarizeTopSyscalls(snap)
topFiles := "Top files: " + summarizeTopFiles(snap)
topProcesses := "Top processes: " + summarizeTopProcesses(snap)
@@ -221,12 +222,46 @@ func summaryBoxInnerWidth(width int) int {
func renderOverviewSparkline(label string, data []float64, panelInner int) string {
w := panelInner - utf8.RuneCountInString(label) - 1 - sparklineSafetyMargin
+ if w > sparklineMaxWidth {
+ w = sparklineMaxWidth
+ }
if w < 8 {
w = 8
}
return renderLabeledSparkline(label, data, w)
}
+func renderOverviewSparklineAligned(label string, data []float64, panelInner int, labelWidth int) string {
+ paddedLabel := padLabelRight(label, labelWidth)
+ w := panelInner - labelWidth - 1 - sparklineSafetyMargin
+ if w > sparklineMaxWidth {
+ w = sparklineMaxWidth
+ }
+ if w < 8 {
+ w = 8
+ }
+ return renderLabeledSparkline(paddedLabel, data, w)
+}
+
+func maxLabelWidth(labels ...string) int {
+ max := 0
+ for _, label := range labels {
+ w := utf8.RuneCountInString(label)
+ if w > max {
+ max = w
+ }
+ }
+ return max
+}
+
+func padLabelRight(label string, width int) string {
+ pad := width - utf8.RuneCountInString(label)
+ if pad <= 0 {
+ return label
+ }
+ return label + strings.Repeat(" ", pad)
+}
+
func panelInnerWidth(width int) int {
if width <= 0 {
width = 80
diff --git a/internal/tui/dashboard/overview_test.go b/internal/tui/dashboard/overview_test.go
index 706661e..9895490 100644
--- a/internal/tui/dashboard/overview_test.go
+++ b/internal/tui/dashboard/overview_test.go
@@ -129,3 +129,38 @@ func TestRenderOverviewSparklineHasSafetyMargin(t *testing.T) {
t.Fatalf("expected sparkline width <= %d with safety margin, got %d", max, got)
}
}
+
+func TestRenderOverviewSparklineCapsWidth(t *testing.T) {
+ out := renderOverviewSparkline("Latency:", make([]float64, 120), 400)
+ lines := strings.Split(out, "\n")
+ if len(lines) != 2 {
+ t.Fatalf("expected 2-line sparkline, got %q", out)
+ }
+ if got := lipgloss.Width(lines[0]) - len("Latency: "); got > sparklineMaxWidth {
+ t.Fatalf("expected capped sparkline width <= %d, got %d", sparklineMaxWidth, got)
+ }
+}
+
+func TestRenderOverviewSparklineAlignedUsesSameSparkStartColumn(t *testing.T) {
+ const panelInner = 80
+ labelWidth := maxLabelWidth("Latency:", "Gap:", "Throughput:")
+ lat := renderOverviewSparklineAligned("Latency:", []float64{1, 2, 3}, panelInner, labelWidth)
+ gap := renderOverviewSparklineAligned("Gap:", []float64{1, 2, 3}, panelInner, labelWidth)
+ thr := renderOverviewSparklineAligned("Throughput:", []float64{1, 2, 3}, panelInner, labelWidth)
+
+ latTop := strings.Split(lat, "\n")[0]
+ gapTop := strings.Split(gap, "\n")[0]
+ thrTop := strings.Split(thr, "\n")[0]
+
+ prefix := strings.Repeat(" ", labelWidth-len("Latency:"))
+ if !strings.HasPrefix(latTop, "Latency:"+prefix+" ") {
+ t.Fatalf("unexpected latency prefix: %q", latTop)
+ }
+ prefix = strings.Repeat(" ", labelWidth-len("Gap:"))
+ if !strings.HasPrefix(gapTop, "Gap:"+prefix+" ") {
+ t.Fatalf("unexpected gap prefix: %q", gapTop)
+ }
+ if !strings.HasPrefix(thrTop, "Throughput: ") {
+ t.Fatalf("unexpected throughput prefix: %q", thrTop)
+ }
+}