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
feat(render): Replace perpetual rAF loop with event-driven render scheduler
startRenderLoop kept a CPU core hot at ~60 Hz forever. Even on a
static screen each frame paid for a render() entry/exit (which calls
into WASM via update() and clearDirty()) and a getCursor() round-trip
into WASM. Browser tabs hosting an idle terminal pinned a core for as
long as the tab was open.
Replace the unconditional loop with requestRender(): an idempotent
single-rAF scheduler that's a no-op if a frame is already pending.
Wake points are placed on every event source that mutates renderable
state — writes from the PTY (writeInternal), each smooth-scroll
animateScroll tick, scroll API mutations (scrollLines, scrollToTop,
scrollToBottom, scrollToLine, smoothScrollTo immediate-jump),
selection changes, post-resize, and the cursor-blink interval (via a
new onRequestRender callback the renderer holds and the Terminal
sets). The renderer's setHoveredHyperlinkId / setHoveredLinkRange
also wake on actual state change, with identity dedupe.
After open()'s forced render, run one synchronous renderTick to
mirror the prior loop's first iteration: refreshRowMetaCache (used by
isRowWrapped) walks the WASM row iterator immediately after open()
and depends on the second update / clearDirty pair to settle WASM
state. Without this, the existing isRowWrapped test fails.
End state: idle terminal does zero JS work and zero WASM calls until
the next event. An equivalent design — fully event-driven with no
loop at all, where every state mutation calls requestRender directly
— would land in the same place; we kept the requestRender shape for
surgical scope.
Signed-off-by: Evan Wies <evan@neomantra.net>
0 commit comments