Repository navigation
tui: Windows TUI lags on mouse-wheel scroll and window resize (regression vs v1; desktop GUI unaffected) #54239
Description
Activity
opencode-agent commented
on Oct 10, 2026 on Oct 10, 2026 – with OpenCode AgentContributorMore actionsThanks for the detailed report. This lag appears specific to native Windows (Windows Terminal / ConPTY), and our automated environment only runs Linux terminals. We couldn't reproduce it meaningfully here, so it needs a maintainer with a Windows setup.
Things that would help them:
- Roughly how long the transcript is when the lag starts (number of messages, or whether there are large tool outputs or diffs), and whether short sessions are smooth.
- Whether the same happens in another Windows terminal, e.g. WezTerm or Alacritty, or in plain conhost.
- Whether it gets better if you make the terminal window smaller.
Related (older) reports, for context: #14479 (scroll lag in long sessions), #7893 (scroll lag on WSL2), #38435 (TUI lag with very large sessions).
Thanks for the triage notes. Ran the three suggested experiments (v2.0.26, WT 1.24.12741.0 vs WezTerm 20240203-110809-5046fc22, same machine, same sessions,
animations: false):Results
- Short sessions are not immune. A fresh session scrolls smoothly only while it is very short; after just a few exchanges the same wheel-scroll lag appears. So this is not a long-transcript-only problem — there is a high per-frame fixed cost that kicks in early.
- Window size has little effect. Halving the terminal window does not noticeably reduce the lag, so per-frame cell count does not seem to be the dominant factor either.
- WezTerm is dramatically smoother — roughly on par with the v1 TUI. Opening the same session in WezTerm on the same machine removes most of the scroll and resize lag.
Session metrics (read-only query of the local DB)
The heaviest local session: ~5,970 messages / ~24,000 parts / ~78 MB of part payload, including multi-MB
readtool outputs and an 8 MB PDF file part. Its tail (what the TUI loads first): last 100 messages = ~360 parts / ~1.6 MB; last 500 = ~1,900 parts / ~8.5 MB.But given result (1) and (2), transcript size does not look like the main driver: lag appears in short sessions too, is insensitive to window size, and largely disappears in WezTerm.
Interpretation
Since the byte stream produced by opentui is handled fine by WezTerm (also ConPTY-based on Windows), the stream itself seems workable. That points at the interaction between the TUI's frame-write pattern (frequent small full/partial frame writes) and Windows Terminal's ConPTY read/throttle/render pipeline, rather than opentui's frame construction cost alone. The lag also affects window dragging (a resize storm of full repaints), which fits a throughput/latency problem on the ConPTY→WT path.
Happy to capture anything else that would help a Windows-equipped maintainer (e.g. specific WT settings, or a side-by-side recording of WT vs WezTerm).
Follow-up with more data points that narrow the regression window further.
The regression is within the opentui-based TUI line
To our knowledge the 1.x TUI already used opentui (per this repo's
opentuilabel: "relates to changes in v1.0, now that opencode uses opentui"). The v1 TUI (current 1.18.x, before the recent v2 upgrade) was smooth in Windows Terminal on this same machine with the same sessions. So the delta that introduced this lag is something that changed in the TUI app/render pipeline between 1.18.x and 2.0.x, not a renderer-framework boundary.Ruled out so far
Hypothesis Test Result Long-transcript O(n) render cost Fresh short session, scroll Lags after a few exchanges Per-frame cell count Half-size terminal window No meaningful change Animation frame churn animations: falseSlight improvement; lag persists opentui frame-construction cost alone Same session in WezTerm (also ConPTY on Windows) Mostly smooth, ≈ v1 TUI Server / session payload Same session in Desktop GUI No lag at all Remaining hypotheses, for a Windows-equipped maintainer
- Changed frame-write pattern in v2 (write frequency, chunk sizing, or flush behavior on win32) interacting badly with Windows Terminal's ConPTY read/throttle pipeline. The byte stream opentui produces is handled fine by WezTerm, so the stream itself is workable — WT's reader side may be the sensitive half. Resize is the worst case (a storm of full repaints), which fits a throughput/latency problem on the ConPTY→WT path.
- v2 UI chrome (persistent tab strip, sidebar, scrollbar, status indicators) increasing per-frame layout work or dirty-region churn even for short transcripts. We have not yet tested
tabs.mode: "off"/tabs.indicators: "numbers"/session.sidebar: "hide"in isolation; can run those on request. - Bun / opentui native version bump between the lines changing the number of write syscalls per frame.
Misc
- We checked the
devbranch today: the ~40 TUI PRs merged since v2.0.26 are refactors/cleanup — no render-path perf fix merged, and the only related open PR we found is perf(tui): open a session with its latest messages and load the rest once it stays open #53429 (session lazy-load), which targets long sessions and would not explain the short-session lag. - Plain conhost A/B is still pending; we can add it if useful.
- Happy to capture a side-by-side recording (WT vs WezTerm, same session, same actions) if that helps.
Update: the lag disappeared after a machine reboot
Same opencode 2.0.26, same Windows Terminal 1.24.12741.0, same sessions — after rebooting the machine today the TUI lag is gone (the user reports it feels normal again; we have not re-run the full A/B, but regular use is smooth).
Host context around the lag period
- The machine had been up continuously for ~24 days (Sep 14 → Oct 8) while the lag was observed, then ~2 days more, then a reboot today (Oct 10).
- No display-driver TDR resets during the lag window (
nvlddmkm, last Event ID 4101 was in May), and no DWM errors — so no explicit GPU-driver crash explains it. Whatever degraded, it degraded silently (plausibly GPU scheduler / compositor / memory state under long uptime, though that is speculation). - Earlier A/B from the degraded state stands: WezTerm on the same degraded host was smooth, and WT + v1 was smooth (from the user's experience before the upgrade).
How this changes the framing
The trigger appears to be transient host-side render-pipeline state, not a deterministic v2↔WT incompatibility — which may be why a Linux CI could not reproduce it. But the differential sensitivity still looks v2-specific: under the same degraded host state, WT + v2 TUI janks badly while WezTerm + v2 TUI and (per the user's past experience) WT + v1 TUI stay smooth. So v2's frame output path on Windows Terminal seems far less tolerant of host rendering degradation than v1's was.
Suggestions for whoever picks this up on Windows hardware:
- Reproduce or reason about the write/flush pattern per frame in v2 vs 1.18.x (frequency, chunking, synchronized-output usage). Something in it appears to fall off a cliff when the host render pipeline is degraded.
- If it helps anyone else: affected users can confirm the environmental nature by trying the same session in WezTerm, and recover by rebooting.
We'll reopen/update here if the lag returns after the machine has been up for a while again.
Correction + environment detail: uptime was not the variable; the display stack likely is
A follow-up to correct our earlier framing, prompted by re-checking the timeline.
Correction on the "long uptime" implication
The machine was also rebooted on Oct 8 (after a ~24-day uptime period from Sep 14), and all of our A/B tests today were run on that fresh ~2-day boot — with the lag fully present. Today's reboot then cleared it. So "weeks of accumulated state" is not the right variable: whatever reboot resets can be triggered within days (or faster), without logging anything. We over-weighted uptime; treat the reboot correlation as "reboot clears a volatile host state", nothing more about duration.
For the record: no display-driver TDRs, no DWM errors, no pending Windows updates (last KB is from Sep 9), and no relevant app crashes in the Oct 8–10 window.
Environment detail that may matter for reproduction
This machine's display stack is far from a clean single-GPU setup, which may explain why clean Linux/CI environments don't reproduce:
- Three currently active display adapters, two of them third-party Indirect Display Drivers:
OrayIddDriver Device(Sunlogin remote-desktop virtual display) andGameViewer Virtual Display Adapter(remote-play virtual display), plus the physical GPU. - Physical GPU is an old NVIDIA GeForce GT 730 (Kepler-era, driver 30.0.14.7514 / 2024-06), with a phantom GT 710 instance (the card was swapped at some point) and a phantom Microsoft Remote Display Adapter (prior RDP use) still in the device tree.
- IDD virtual displays wake/deactivate with remote sessions and display-topology changes — events that change composition state without leaving any event-log trace, and that only a reboot fully resets.
Refined hypothesis
The flaky component is plausibly the multi-adapter composition path (third-party IDDs + old NVIDIA + DWM), which Windows Terminal's GPU renderer depends on. WT + v2 TUI appears to fall off a cliff when that path is in a degraded state, while WT + v1 TUI and WezTerm + v2 TUI tolerated the same state — so the v2 frame-output change remains the sensitivity delta, but this host environment is what makes it visible.
Cheap next experiments (for us or anyone with a similar setup)
- Next time the jank appears: disable the two third-party IDD adapters in Device Manager (no reboot needed) and see whether WT recovers — this would directly implicate the IDDs instead of the NVIDIA driver or Windows itself.
- Longer term: run WT + v2 for a while with the IDDs disabled entirely and see whether the jank ever returns.
- Three currently active display adapters, two of them third-party Indirect Display Drivers:
Root cause narrowed down: periodic ~2 s stalls in Windows Terminal's CJK font fallback (not opencode, not the GPU path)
Before the reboot we measured the degraded state directly with a small synthetic TUI (alt screen, full-screen repaint per frame, timing each
write+flush), so this supersedes the display-stack hypothesis above.What triggered it
WT 1.24.12741.0, 140x36, 600 frames stalls > 50 ms total ASCII only (256-color / truecolor / bg / box-drawing) 0 ~0.01 s per 60 frames CJK text in the frame (default font Cascadia Mono) 19, each 2.0–2.2 s 39.6 s CJK, rendering.graphicsAPI: direct2d7 per 300 frames, ~2.06 s each 14.5 s CJK, non-elevated WT instance same ~2 s stalls — CJK, font list "Cascadia Mono, NSimSun"same ~2 s stalls 16.5 s CJK, primary font NSimSun(has CJK glyphs, no fallback needed)0 0.09 s CJK, WezTerm (same host, same moment) 0 0.2 s CJK, inbox conhost 0 0.46 s During the stalls WT CPU stayed ~10–14 % of one core and GPU 3D utilization near 0, so the renderer was waiting rather than working. While WT was blocked, the ConPTY output pipe backed up and the TUI couldn't process wheel input, which is the "lag" users feel. Mouse-input latency into the app was 0.3–0.4 ms whenever no stall was in progress.
Conclusion
- The trigger was Windows Terminal's DirectWrite font-fallback path for CJK glyphs intermittently blocking for ~2 s (the very regular duration looks like a timeout). Anything that avoids fallback (a primary font that contains CJK, or a terminal with its own fallback cache like WezTerm) was unaffected.
- It is not opencode-v2-specific: Claude Code in fullscreen TUI mode was equally affected in the same WT during the same period. The v1 comparison was most likely just from before the host entered this state.
- After the reboot, the exact same benchmark on the same WT version with the default font shows 0 stalls in 2×600 frames (~0.25 s total). So the faulty state lives in the host's font-fallback stack (DirectWrite / Font Cache service), and a reboot clears it. No Font Cache errors were logged, so we couldn't pin down the exact component.
Workarounds if it happens to anyone else: reboot; or set a WT primary font that contains CJK glyphs; or use another terminal. As far as we can tell, nothing needs fixing in opencode, so feel free to close this as environmental. We'll reopen with data if it comes back.
Summary
On native Windows, the v2 TUI has severe input-to-frame latency: mouse-wheel scrolling visibly lags behind wheel ticks, and dragging the terminal window edge (interactive resize) stutters the UI while dragging, catching up after release. The same session opened in the Desktop GUI is perfectly smooth, and the v1 (1.x) TUI on the same machine + terminal was smooth. This appears to be a v2 TUI regression.
Environment
Reproduction
opencodein Windows Terminal on native Windows (not WSL).Expected Behavior
Wheel scrolling and interactive resize re-render smoothly, comparable to the v1 TUI on the same setup.
Actual Behavior
Additional Context
cli.json(animations: false,diffs.wrap: "none",session.scrollbar: false): helps slightly, but the lag is still clearly present and far from v1 smoothness.