perf(desktop): memoize PlatformAvatar + StatusDot (messaging icon churn) - #73774
Merged
Conversation
…hurn The sidebar's messaging section renders one PlatformAvatar (labelIcon) and StatusDot per platform group. The sidebar re-renders on every streaming tick ($sessions/$workingSessionIds/$messagingSessions churn), and both leaves were unmemoized — so every platform's avatar + dot re-rendered on each delta even though their props (platformId/tone/className) never changed. - PlatformAvatar: memo(forwardRef(...)) — pure fn of platformId/name/class/style - StatusDot: memo() — pure fn of tone/class Measured (Messaging open, settled, 2 sessions streaming, 3s window): before: 96 wasted (PlatformAvatar 32, StatusDot 32, + brand icons) after: 0 wasted Both are shared primitives; the memo also helps every other consumer (session rows, gateway menu, session tiles) that renders them under a hot parent.
Contributor
૮ >ﻌ< ა ci reviewran on d76c7f5 ℹ️ InfoDesktop E2E visual evidence · View test artifacts · View job1 visual diff. inline evidence upload failed. Failed to upload diff-665a0833239e-onboarding-overlay-diff.png with gh image (exit code 1): Error uploading /home/runner/work/_temp/e2e-evidence/diff-665a0833239e-onboarding-overlay-diff.png: step 0 (get upload token): uploadToken not found on repo page — do you have write access to NousResearch/hermes-agent? (or, if NousResearch enforces SAML SSO, authorize at https://github.com/orgs/NousResearch/sso) |
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…ssaging-icon-churn perf(desktop): memoize PlatformAvatar + StatusDot (messaging icon churn)
33hodl
pushed a commit
to 33hodl/hermes-agent
that referenced
this pull request
Aug 12, 2026
…ssaging-icon-churn perf(desktop): memoize PlatformAvatar + StatusDot (messaging icon churn)
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
Follow-up to #73698. That PR flagged three "not fixed" items; on re-measurement (settling each surface fully before starting the counter, instead of catching the mount cascade), only one was a real steady-state loop. This PR fixes it. The other two are documented below as non-issues so the record is straight.
The one real fix: messaging icon churn
The sidebar's messaging section renders, per platform group, one
PlatformAvatar(as the sectionlabelIcon) and aStatusDot. The wholeChatSidebarre-renders on every streaming tick — it subscribes to$sessions,$workingSessionIds,$messagingSessions,$messagingPlatformTotals, and ~16 other stores — and both icon components were unmemoized. So every platform's avatar and dot re-rendered on every stream delta despite their props (platformId,platformName,tone,className) never changing.messaging/platform-icon.tsx›PlatformAvatarmemo(forwardRef(...))— pure function ofplatformId/platformName/className/style.forwardRefpreserved (RadixTipasChildneeds the ref, #67500).components/status-dot.tsx›StatusDotmemo()— pure function oftone/className.Metrics (Messaging open, settled 4s after mount, 2 sessions streaming, 3s window)
PlatformAvatarStatusDotSiTelegram2, etc.) +SectionTitle/ExternalLinkBoth are shared primitives, so the memo also removes the same per-tick churn wherever else they render under a hot parent — session rows, the gateway menu panel, session tiles.
The two that were NOT real loops (measured, no code needed)
Documenting these because #73698's description originally listed them as open — they aren't.
cellCtx/cell memo already trims that mount cost. Nothing to fix here; perf(desktop): kill sidebar + overlay render churn from hot store subscriptions #73698's description has been corrected.Block/Ct/inlineCodechurn from the transcript behind the modal. Re-measured with perf(desktop): kill sidebar + overlay render churn from hot store subscriptions #73698'sPaletteRowmemo in place: those components no longer appear — typing "setting" now costs 64 wasted (was 5,519), all minor palette-item icons (Kbd/KbdCombo/Bolt). Already handled by perf(desktop): kill sidebar + overlay render churn from hot store subscriptions #73698.Verification
messaging/index.test.tsx,status-dotpassMethod note
The correction here is a measurement-discipline lesson worth recording: start the render counter only after a surface has settled. Measuring from the moment of navigation conflates the unavoidable one-time mount/data-load cascade with steady-state churn, and massively overstates the latter. The sidebar-bleed wins in #73698 are real (they reproduce on every stream tick, settled); the Artifacts "loop" was a mount-cascade artifact of measuring too early.