Repository navigation
Description
Observed
In the Electron GUI's chat area, every streamed token re-renders the entire transcript.
The renderer keeps one global version counter that increments on any store change (getVersion() in emrg/gui/renderer/src/lib/transcript.ts), and the list component subscribes to it — TranscriptView.tsx reads useSyncExternalStore(store.subscribe, store.getVersion). A streaming delta therefore re-renders the component that owns the entry list, and every entry element is rebuilt with it. Entries are additionally keyed by array index, so the keys say nothing about identity either.
The cost consequently scales with transcript length: in a long session every token re-renders every message, tool row and tool group, and streaming gets slower the longer the conversation has grown.
Adding React.memo to each entry only trims the constant. The parent still produces a fresh element for every child on every token, so the work per token stays proportional to the number of entries.
Expected
When a token arrives, only the entry whose text changed is re-rendered; every other entry is not rendered at all. Per-frame cost is independent of how many entries the session holds.
What a fix must satisfy
- Stable identity per entry. Survives streaming deltas, tool boundaries and history replay, and is unique across stores — the replay path builds entries in a scratch store and hands them to the real one (
historyReplay.ts→prependEntries), so an id generator scoped to a single store would collide. - Per-entry version and per-entry subscription. A write to one entry notifies only that entry's subscribers.
- A separate structure signal for the list. It changes only when the entry set itself changes (add, prepend, clear, top history bar, session switch), so appending text to an existing entry does not re-render the list.
- Scroll-follow keeps working per token without being re-implemented by re-rendering the transcript.
- No regression for existing consumers:
getVersion()keeps meaning "increments on any change", tool-row and group interactions keep working, and entry keys stay stable across a delta. - A guard that fails if the behaviour regresses. With the per-entry subscription removed and the list back on the global version, the guard must go red. Counting renders is the measurement: a store whose
getEntryVersioncalls are counted shows one entry rendering per token when the fix is in place, and every entry rendering when it is not.
Activity
argszero commentedon Nov 3, 2026
Handled by #2680