summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-05 22:50:32 +0200
committerPaul Buetow <paul@buetow.org>2026-03-05 22:50:32 +0200
commit4d69b8bf11b563d8ec6693447c09f35dec13b148 (patch)
tree4b7130a72318c5caf9fed68abebb27a7c09d6f3c /internal
parent476cfb1aa569b42e7bab6cbfd5e548c9e85ee07c (diff)
task 364: animate flamegraph zoom transitions
Diffstat (limited to 'internal')
-rw-r--r--internal/tui/flamegraph/model.go6
-rw-r--r--internal/tui/flamegraph/model_test.go29
2 files changed, 32 insertions, 3 deletions
diff --git a/internal/tui/flamegraph/model.go b/internal/tui/flamegraph/model.go
index c1693b1..8433fea 100644
--- a/internal/tui/flamegraph/model.go
+++ b/internal/tui/flamegraph/model.go
@@ -283,7 +283,7 @@ func (m Model) Paused() bool {
func (m *Model) SetViewport(width, height int) {
m.width = width
m.height = height
- m.rebuildFrames(false)
+ m.rebuildFrames(true)
}
// SetDarkMode sets the active color theme mode.
@@ -331,7 +331,7 @@ func (m *Model) zoomIn() {
m.zoomRoot = target
m.zoomPath = selectedPath
m.selectedIdx = 0
- m.rebuildFrames(false)
+ m.rebuildFrames(true)
}
func (m *Model) zoomUndo() {
@@ -347,7 +347,7 @@ func (m *Model) zoomUndo() {
m.zoomRoot = findNodeByPath(m.snapshot, m.zoomPath)
}
m.selectedIdx = last.previousSelectedIdx
- m.rebuildFrames(false)
+ m.rebuildFrames(true)
}
func (m *Model) zoomReset() {
diff --git a/internal/tui/flamegraph/model_test.go b/internal/tui/flamegraph/model_test.go
index 45e0c48..326c2d8 100644
--- a/internal/tui/flamegraph/model_test.go
+++ b/internal/tui/flamegraph/model_test.go
@@ -165,6 +165,35 @@ func TestZoomInUndoResetAndNestedZoom(t *testing.T) {
}
}
+func TestZoomTransitionAnimatesToNewLayout(t *testing.T) {
+ m := newZoomModel()
+ pathA := "root" + pathSeparator + "A"
+ preWidth := m.frames[mustFrameIndex(t, m.frames, pathA)].Width
+
+ m.selectedIdx = mustFrameIndex(t, m.frames, pathA)
+ m = pressFlameKey(t, m, tea.KeyPressMsg{Code: tea.KeyEnter})
+ if !m.animating {
+ t.Fatalf("expected zoom-in to start animation")
+ }
+ currentWidth := m.frames[mustFrameIndex(t, m.frames, pathA)].Width
+ targetWidth := m.targetFrames[mustFrameIndex(t, m.targetFrames, pathA)].Width
+ if currentWidth == targetWidth {
+ t.Fatalf("expected intermediate zoom frame width to differ from target (current=%d target=%d, pre=%d)", currentWidth, targetWidth, preWidth)
+ }
+
+ for i := 0; i < 180 && m.animating; i++ {
+ next, _ := m.Update(animTickMsg{})
+ m = next.(Model)
+ }
+ if m.animating {
+ t.Fatalf("expected zoom animation to settle within 180 ticks")
+ }
+ finalWidth := m.frames[mustFrameIndex(t, m.frames, pathA)].Width
+ if finalWidth != targetWidth {
+ t.Fatalf("expected final zoom width %d, got %d", targetWidth, finalWidth)
+ }
+}
+
func TestSearchLifecycleAndMatchNavigation(t *testing.T) {
m := NewModel(nil)
m.frames = []tuiFrame{