Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(web-shared): keep trace viewer span-select zoom smooth on large traces by mitul-s · Pull Request #2593 · vercel/workflow · GitHub
Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web-shared): keep trace viewer span-select zoom smooth on large traces by mitul-s · Pull Request #2593 · vercel/workflow · GitHub
Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web-shared): keep trace viewer span-select zoom smooth on large traces by mitul-s · Pull Request #2593 · vercel/workflow · GitHub
Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(web-shared): keep trace viewer span-select zoom smooth on large traces by mitul-s · Pull Request #2593 · vercel/workflow · GitHub
Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web-shared): keep trace viewer span-select zoom smooth on large traces by mitul-s · Pull Request #2593 · vercel/workflow · GitHub
Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(web-shared): keep trace viewer span-select zoom smooth on large traces by mitul-s · Pull Request #2593 · vercel/workflow · GitHub
Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(web-shared): keep trace viewer span-select zoom smooth on large traces by mitul-s · Pull Request #2593 · vercel/workflow · GitHub
Skip to content

fix(web-shared): keep trace viewer span-select zoom smooth on large traces - #2593

Closed
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121
Closed

fix(web-shared): keep trace viewer span-select zoom smooth on large traces#2593
mitul-s wants to merge 5 commits into
mainfrom
c+ms/fix-trace-viewer-zoom-perf-6121

Conversation

@mitul-s

@mitul-smitul-s commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the trace viewer click-to-zoom animation feeling broken / "like reduced motion is on" — it jumps to the end instead of easing. Reproduces on production (Vercel Front) but not locally, and only on click (wheel/scrollbar zoom is smooth).

Root cause

Two zoom paths behave differently:

  • Wheel/scrollbar zoom updates viewport directly, once per event → always smooth.
  • Click-to-zoom runs animateTo (a requestAnimationFrame easing loop) and mounts the detail panel in the same React commit.

animateTo captured its easing clock at call time (start: performance.now()), then each frame computed t = (performance.now() - start) / 150. On a production-sized trace, mounting/rendering the detail panel (real step input/output) blocks the main thread for tens-to-hundreds of ms before the first animation frame runs. Because the clock started at call time, by the first frame t is already most of (or all of) the 150ms — so the zoom skips straight to the end. That's the "animation broke / reduced motion" feel.

This is why it's invisible locally (panel data is tiny → first frame on time) and why wheel zoom is fine (never touches the panel). It's also independent of the #2434 JSON-viewer reskin, which isn't deployed yet.

Fixes (all @workflow/web-shared)

  1. Anchor the easing clock to the first delivered frame, not call time — a late first frame no longer makes the zoom skip ahead.
  2. Open the detail panel as a low-priority startTransition on span select, so its heavy first render can't block the animation's opening frames.

Supporting per-frame work reductions:
3. Memoize the event list and detail-panel aside so viewport-only re-renders skip them.
4. Share one ContextCardProvider for timeline marker tooltips instead of each tick self-mounting its own portal/observer/listener.
5. Gate computeSpanGaps on altHeld so that O(spans) pass stays off the animation path.

Narrowing

The wheel-vs-click contrast proved the per-frame timeline render (bars/markers/gaps) isn't the bottleneck — wheel re-renders all of that and stays smooth. The click-only cost is the panel, and the visible "jump" is the call-time easing clock. Fixes 1–2 target that directly; 3–5 are defensive.

How did you test your changes?

  • pnpm typecheck and pnpm vitest run test/new-trace-viewer-search.test.ts pass for @workflow/web-shared.
  • Mechanism verified against the easing math: with a call-time clock a >=150ms pre-first-frame block sets t=1 immediately (instant jump); anchoring to the first frame removes that.
  • Recommend a final check on a marker/payload-heavy production-scale run (or preview build) since the symptom is data-size dependent and doesn't show on local fixtures.

PR Checklist - Required to merge

  • pnpm changeset (patch for @workflow/web-shared)
  • DCO sign-off — commits not yet signed off; git rebase --signoff main (or amend) + force-push before merging.
  • Ping @vercel/workflow once ready

@changeset-bot

changeset-botBot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6d0b97b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

…+ deferred panel)
Co-authored-by: Cursor <cursoragent@cursor.com>
@mitul-smitul-s changed the title fix(web-shared): fix trace viewer lag when selecting/zooming a spanfix(web-shared): keep trace viewer span-select zoom smooth on large tracesJun 23, 2026
@mitul-smitul-s closed this Jun 25, 2026
Sign up for freeto 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.

2 participants

@mitul-s@cursoragent