You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
desktop: timeline rows lack containment, so scrolling a long session burns main-thread time in layout/paint #54291
Scrolling a long session in the desktop app drops to roughly 20 FPS. A CPU profile of the scroll window shows the cost is layout/paint rather than JS: (program) is the largest bucket (523ms of 1607ms sampled), and the app-side reads are small next to it.
The timeline's row container sets overflow: clip but no containment, so every mounted row stays in the layout and paint path for the whole list. packages/app/src has no content-visibility, contain, or will-change anywhere.
Adding contain: layout paint to the row container (packages/app/src/session/timeline/virtualizer.tsx, the row style) reduces cold-scroll long-task time by ~76ms median (5 of 7 paired rounds) and improves the settled frame-gap distribution in cumulative paired testing (11W-4L-3T, p=0.013), with no DOM cost. It is a one-line change.
Measured with the repo's own benchmark (packages/app/e2e/performance/timeline/session-timeline-scroll-benchmark.spec.ts) against a production build. Runs were paired and interleaved with the baseline: the frame-gap metric is bimodal (50ms / 66.6ms) and load-sensitive, so single-window readings are not reliable.
Plugins
none
OpenCode version
2.0.26
Steps to reproduce
Open a session with a long history (the benchmark uses 320 turns) in the desktop app.
Scroll the timeline.
Profile the renderer: the scroll window is dominated by layout/paint, with 3 long tasks totalling ~195ms.
Thanks for the detailed measurements. I tried to look at this here, but my sandbox is a headless, single-core Linux machine with no GPU compositor and no desktop app. Frame-rate and layout/paint timings from it wouldn't be comparable to yours, and with a metric this noisy they also wouldn't be stable enough to compare paired runs. A maintainer will need to run it on real desktop hardware.
What I could confirm from the code: the row style in packages/app/src/session/timeline/virtualizer.tsx sets only overflow: clip / overflow-clip-margin, and packages/app/src uses no contain or content-visibility, as you describe.
One thing that would help: I couldn't find session-timeline-scroll-benchmark.spec.ts in the repository (the closest is packages/app/e2e/performance/timeline/session-timeline-benchmark.spec.ts). If you wrote the scroll benchmark yourself, could you share it, or the exact command you ran, so maintainers can repeat your paired runs?
The benchmark is in #54293 — packages/app/e2e/performance/timeline/session-timeline-scroll-benchmark.spec.ts plus its probe. Run it from packages/app with PLAYWRIGHT_BUILD=1:
node_modules/.bin/playwright test --config e2e/performance/playwright.config.ts timeline/session-timeline-scroll-benchmark.spec.ts --repeat-each=20 --workers=1 --retries=0
Two notes for whoever runs it on desktop hardware:
The frame-gap metric is bimodal (50ms / 66.6ms) and the mode tracks machine load, so compare paired/interleaved runs inside the same window rather than absolute numbers across windows. That is what the numbers in the PR come from.
The two scenarios bracket the cold-tail settle: one scrolls as soon as the timeline mounts, the other waits for the settle to finish.
Thanks for confirming the row style from the code.
Activity
opencode-agent commentedon Oct 10, 2026
Thanks for the detailed measurements. I tried to look at this here, but my sandbox is a headless, single-core Linux machine with no GPU compositor and no desktop app. Frame-rate and layout/paint timings from it wouldn't be comparable to yours, and with a metric this noisy they also wouldn't be stable enough to compare paired runs. A maintainer will need to run it on real desktop hardware.
What I could confirm from the code: the row style in
packages/app/src/session/timeline/virtualizer.tsxsets onlyoverflow: clip/overflow-clip-margin, andpackages/app/srcuses nocontainorcontent-visibility, as you describe.One thing that would help: I couldn't find
session-timeline-scroll-benchmark.spec.tsin the repository (the closest ispackages/app/e2e/performance/timeline/session-timeline-benchmark.spec.ts). If you wrote the scroll benchmark yourself, could you share it, or the exact command you ran, so maintainers can repeat your paired runs?Related, but not the same problem:
Te-River commentedon Oct 10, 2026
The benchmark is in #54293 —
packages/app/e2e/performance/timeline/session-timeline-scroll-benchmark.spec.tsplus its probe. Run it frompackages/appwithPLAYWRIGHT_BUILD=1:Two notes for whoever runs it on desktop hardware:
Thanks for confirming the row style from the code.