Skip to content

fix(traces): rework Timeline tab — clamping/NaN/perf fixes + navigation UX#252

Merged
Makisuo merged 1 commit into
mainfrom
fix/trace-timeline-rework
Jul 23, 2026
Merged

fix(traces): rework Timeline tab — clamping/NaN/perf fixes + navigation UX#252
Makisuo merged 1 commit into
mainfrom
fix/trace-timeline-rework

Conversation

@Makisuo

@Makisuo Makisuo commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

What

Two passes over the trace Timeline tab (packages/ui/src/components/traces/), prompted by it being buggy and rough to use.

Bug fixes

  • clampViewport rewritten — right-then-left edge clamping so an oversized window can't re-violate the opposite edge; windows wider than the padded trace get centered; NaN/Infinity inputs recover to a sane window.
  • SET_VIEWPORT clamps inside the reducer — the minimap could previously drag/resize the viewport completely off the trace (negative / >100% rects, empty body). All viewport paths now share one clamp.
  • Zero-duration traces — a 1 ms synthetic window at the bounds source kills every downstream division-by-zero (minimap %, axis %, ticks); the default viewport is built through clampViewport so the min-width floor applies at first paint.
  • Hover perf — tooltip and crosshair positions are ref + rAF-driven; mousemove within a span no longer re-renders the whole timeline (one render only on span-boundary crossing).
  • Hotkey isolation — timeline keys stopPropagation so app-global hotkeys (D time picker, F filter, ? help, T theme) don't also fire while the timeline is focused; keys typed into the span search no longer drive the timeline.

Navigation & orientation UX

Patterns adopted from Jaeger/Grafana, Sentry's trace-view rewrite, Perfetto, and Chrome DevTools:

  • Animated viewport — easeOutSine tween on keyboard/programmatic zooms (double-click span, F, Fit); wheel/drag gestures stay instant and cancel in-flight animations.
  • Keyboard clusterW/S cursor-anchored zoom, A/D pan, fast variants, F fit focused/selected span (else whole trace), ? shortcut sheet.
  • Span bars/ chevrons when a bar continues off-view; clipped-left bars keep their label pinned to the visible edge (px indent — CSS % padding resolves against the parent cell, which was silently hiding labels); small bars get an outside name · duration label on the roomier side; label gating uses visible width, not total bar width.
  • Minimap — drag on empty area reframes to that range (preview-then-commit, either direction); click jumps; double-click fits; hover guide line with +offset time readout; error spans brighter and painted on top; depth pitch scales with trace depth instead of piling deep spans on the bottom row.
  • SearchEnter/⇧Enter cycles matches with a k/n counter, focusing + scrolling each match into view.
  • Misc — crosshair time readout, gesture cursor states (grabbing/crosshair), pointer-events: none on rows during scroll bursts, dead sticky removed from the time axis.

Tests

The reducer suite was already failing on main (2/8 — stale expectations from a removed proportional zoom floor). Fixed those and added coverage for SET_VIEWPORT clamping, degenerate/zero-duration traces, NaN/Infinity inputs, and the oversized-window invariant — 15/15 pass (packages/ui, bunx vitest run src/components/traces/__tests__/timeline-reducer.test.ts).

Verification

  • bun typecheck clean across the repo (32/32 turbo tasks).
  • Browser-verified against a live 25-span trace: minimap drag past both edges clamps at ±5% padding with the body never emptying; 25× zoom-out spam caps at the 1.1× window; zero NaN/Infinity inline styles; wheel zoom anchors under the cursor; d/f/? no longer trigger the global hotkeys; search cycles 1/6→2/6→1/6 with focus ring.

Reviewer notes

  • useTimelineInteractions now returns crosshairRef/dragMode/getCursorTimeMs instead of crosshairX/isDragging-only; the crosshair div is always mounted and driven imperatively.
  • The animation dispatches clamped SET_VIEWPORT per frame — visible-row re-renders during a 120–220 ms tween, same cost profile as an ordinary drag-pan.
  • Researched-but-deferred follow-ups (critical-path sub-bar, span-event ticks, autogrouped repeated siblings, own-vs-hidden-child error icons) are intentionally out of scope.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

…er perf + Tier1/2 UX

Bug fixes:
- clampViewport rewritten: right-then-left edge clamping (no re-violation on
  oversized windows), centered branch when the window exceeds the padded trace,
  NaN/Infinity input guards
- SET_VIEWPORT now clamps inside the reducer, so minimap pan/resize/jump can no
  longer drag the viewport off the trace and blank the body
- zero-duration traces get a 1ms synthetic window (kills minimap/axis NaN styles);
  default viewport built through clampViewport so the min-width floor holds at
  first paint
- tooltip + crosshair positions are ref/rAF-driven — mousemove inside one span no
  longer re-renders the whole timeline
- timeline keys stopPropagation so app-global hotkeys (D/F/T/?) don't also fire;
  keys typed into the search input no longer drive the timeline

UX (patterns from Jaeger/Grafana, Sentry, Perfetto, Chrome DevTools):
- animated viewport: easeOutSine tween on keyboard + programmatic zooms; direct
  gestures stay instant and cancel in-flight animations
- W/S cursor-anchored zoom, A/D pan, shift-fast variants, F fit span/trace,
  ? shortcut sheet
- span bars: clipping chevrons when a bar extends off-view, viewport-pinned
  labels for clipped-left bars (px indent), outside name·duration labels for
  small bars, label gating by visible width
- minimap: drag-on-empty reframes with preview-then-commit, click jumps,
  double-click fits, hover guide with time readout, errors brighter and on top,
  depth pitch scales with trace depth, resize-edge cursor affordances
- search: Enter/⇧Enter cycles matches with k/n counter + focus ring
- crosshair time readout; pointer-events disabled on rows during scroll bursts

Tests: reducer suite was already failing (stale proportional-floor expectations);
fixed and extended with boundary/degenerate/NaN coverage (15 pass).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pullfrog

pullfrog Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Your Pullfrog Router balance is exhausted.

You have a card on file but auto-reload is disabled, so runs paused once your balance went past the overdraft buffer.

Top up balance → · Enable auto-reload →

Pullfrog  | ⚠️ this action is pinned to a commit SHA, which freezes the cleanup step — switch to @v0 or keep the SHA fresh with Dependabot | Rerun failed job ➔View workflow run | via Pullfrog | Using Claude Opus𝕏

@Makisuo
Makisuo merged commit 5578b5a into main Jul 23, 2026
7 of 8 checks passed
@Makisuo
Makisuo deleted the fix/trace-timeline-rework branch July 23, 2026 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant