Skip to content

perf(desktop): stop re-rendering the app shell on every keystroke#1692

Open
noahjalex wants to merge 2 commits into
block:mainfrom
noahjalex:perf/thread-input-priority
Open

perf(desktop): stop re-rendering the app shell on every keystroke#1692
noahjalex wants to merge 2 commits into
block:mainfrom
noahjalex:perf/thread-input-priority

Conversation

@noahjalex

@noahjalex noahjalex commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Why

Noah: Typing in a thread with lots of agents working was painfully slow and building. I basically couldn't send messages, this fixes it for me.

AI: Every keystroke, anywhere in the app, re-rendered the entire app shell. usePresenceSession (mounted in AppShell) stored raw Date.now() activity timestamps in React state and updated them from capture-phase keydown/pointerdown listeners — new state at the app root per keystroke. This was the largest single contributor to typing feeling slow, on every surface.

What

  • Track last-activity in a ref; keep only the derived automatic status (online/away) in state.
  • State updates only when the status actually flips: activity while away, the 30s interval tick crossing the idle threshold, or visibility changes.
  • No behavior change: same idle timeout, same away/online semantics, same relay sync.

Repro / Verify

cd desktop && just desktop-dev   # open in Chrome with React DevTools

React DevTools → Profiler → record → type a sentence in any composer → stop.

  • On main: one AppShell-rooted commit per keystroke (the whole tree re-renders).
  • With this PR: no commits while typing.

Quantified (render-count probe + Event Timing benchmark, 4x CPU throttle):

main this PR
AppShell renders per 74 keystrokes 70 1
keystroke input-to-paint, channel median 32ms 16ms
keystroke input-to-paint, 68-reply thread median / >50ms count 48ms / 75 24ms / 1-3

Risk Assessment

Low — single hook, derived-state refactor with identical semantics. 2266 unit tests and messaging/thread/status e2e specs pass.

Related: #1652 (thread-panel render fixes; same investigation, independent change).

🤖 Generated with AI

usePresenceSession stored raw Date.now() activity timestamps in React
state and bumped them from capture-phase keydown/pointerdown listeners,
so every keystroke anywhere in the app re-rendered AppShell (the app
root) and swept context changes through the whole tree. Track activity
in a ref and keep only the derived automatic status in state, updated
solely when it actually flips (online <-> away, on activity, interval
tick, or visibility change).

Probe: AppShell renders during a 74-keystroke burst drop 70 -> 1.
Benchmark keystroke input-to-paint medians halve across every scenario
(32 -> 16ms channel, 48 -> 24ms in a 68-reply thread at 4x throttle);
thread keystrokes over the 50ms frame budget drop 76 -> 1-3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@noahjalex noahjalex marked this pull request as ready for review July 9, 2026 20:52
@noahjalex noahjalex requested a review from a team as a code owner July 9, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant