perf(tui): lazily seed virtual history heights - #16523
Merged
kshitijk4poor merged 1 commit intoApr 27, 2026
Merged
Conversation
OutThisLife
approved these changes
Apr 27, 2026
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
prmartinow
pushed a commit
to prmartinow/hermes-agent
that referenced
this pull request
Aug 26, 2026
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
initialHeightsMapfor every transcript row on everyuseMainApp()renderuseVirtualHistory()from an estimate callbackProblem
The TUI already has transcript virtualization, markdown caching, and measured-height caching, but
useMainApp()was still doing one avoidable O(n) allocation-heavy step on every render:Map<string, number>covering every transcript rowestimatedMsgHeight()for missesuseVirtualHistory()That work happened even though
useVirtualHistory()already walks the full item list internally to build offsets. In long sessions, that meant each React render paid an extra full-transcript scan plus a large temporaryMapallocation before the hook could do its own work.Root cause
We were materializing the full initial-height snapshot in
useMainApp()instead of letting the virtualization layer seed missing heights on demand.Concretely, the old path did this on every render:
virtualRowsMapheightCacheorestimatedMsgHeight(...)The virtualization hook only needed:
What changed
1.
useMainApp()Replaced the eager
initialHeightsuseMemo()map build with:heightCachebucket passed directly asinitialHeightsestimateRowHeight(index)callback passed asestimateHeight2.
useVirtualHistory()Added
ensureVirtualItemHeight()and used it when:That helper:
estimateHeight(index, key)Why this is safe
This does not change the measured-height lifecycle:
onHeightsChangestill syncs measured heights back into the per-session bucketThis is a refactor of where missing estimates are materialized, not a behavior change to how row heights are ultimately measured.
Validation
Focused tests added
ui-tui/src/__tests__/useVirtualHistoryHeights.test.tsFull validation run
From
ui-tui/:Results:
npm run type-check: passedui-tuivitest suite: 45 files / 370 tests passedSynthetic microbenchmark
I also ran a small local Node microbenchmark to quantify the exact work removed: rebuilding a 10k-entry
initialHeightsmap 200 times versus reusing the existing cache bucket.Observed locally:
{"label":"old-build-initial-heights-map","ms":238.15,"heapDeltaKB":355,"lastSize":10000} {"label":"new-pass-existing-cache-only","ms":0.01,"heapDeltaKB":0,"lastSize":6666}This benchmark is synthetic and intentionally narrow: it measures only the removed eager map-build/allocation step, not whole-app frame time. But it does confirm that the deleted work was non-trivial and allocation-heavy.
Why this helps
For long transcripts, this removes a full-transcript allocation/scanning pass from the hot render path in
useMainApp(). That should reduce:MapallocationsestimatedMsgHeight()work for rows that never needed eager materializationScope
Focused perf cleanup only: