Background
The #430 converge series (motion / typography / spacing) is fully merged and token governance is complete. The React architecture cleanup surfaced by that work is tracked here; the CSS-side cleanup is tracked separately in #476.
Verified against main at 2791ea4c on 2026-07-04, after #489, #497, #502, #508, and #512 merged.
Scope
This issue covers React component structure, AppShell state ownership, async UI polish, effect callback stability, and evidence-based render performance. It does not cover token values, visual redesign, or CSS architecture debt.
The high-priority React cleanup is now effectively complete. The remaining question is whether a very small evidence-based memo pass is worth doing. Do not add broad memoization without prop-stability or render-count evidence.
Completed
PR4 — Async and lazy boundaries are done
Done in #489fix/ui-react): harden async and lazy boundaries.
What changed:
- Made chat model switching and session row actions rejection-safe while preserving owner-side toast feedback.
- Added compact Suspense fallbacks only where the target surface is already known to be visible.
- Kept
ArtifactPane fallback intentionally null unless AppShell owns an explicit artifact/list-error visibility gate. - Added contracts for async pending cleanup, lazy fallback scope, and startup fail-soft behavior.
PR5 — Tool activity preview split is done
Done in #497refactor(ui-react): split tool activity previews.
What changed:
- Reduced
tool-activity.tsx from the original 1494-line catch-all into a smaller shell plus focused preview owners. - Preserved result rendering behavior, redaction, truncation, and fallback ordering through behavior contracts.
- Removed topology-heavy tests during review and kept coverage on user-visible preview contracts.
Current snapshot: packages/ui/src/tool-activity.tsx is 326 lines on main at 2791ea4c.
PR6 — Session list module split is done
Done in #502refactor(ui-react): split session list modules.
What changed:
- Reduced
session-list-panel.tsx from the original 1103-line mixed owner into a small shell plus session-history-list.tsx and session-sidebar-nav.tsx owners. - Removed stale module-panel props and dead
sessions nav branches. - Kept behavior contracts for row actions, busy state, scroll area, and time bucket copy.
Current snapshot:
packages/ui/src/session-list-panel.tsx: 48 lines.packages/ui/src/session-history-list.tsx: 854 lines.
PR7 — AppShell session UI state is done
Done in #508refactor(ui-react): consolidate app shell session state.
What changed:
- Consolidated the per-session UI map state behind a local hook/reducer boundary.
- Preserved same-session cleanup and active-session behavior.
- Fixed the nested thinking/truncated update risk during review by making map updates merge against latest state.
PR8 — AppShell effect callback stability is done
Done in #512refactor(ui-react): stabilize app shell effect callbacks.
What changed:
- Replaced the local latest-ref pattern with React 19
useEffectEvent for long-lived subscription, timer, and listener callbacks. - Preserved mount-once bootstrap subscriptions and
activeId-keyed active-session subscriptions. - Kept ordinary UI callbacks, such as the plan reminder toast action, outside the Effect Event boundary.
- Added behavior tests proving captured old subscription callbacks dispatch to the latest handler without resubscribing.
Current Size Snapshot
Measured on main at 2791ea4c:
| File | Lines | Status |
|---|
apps/desktop/src/renderer/app-shell.tsx | 1464 | Still large, but per-session state and effect callback boundaries are now separated. |
packages/ui/src/tool-activity.tsx | 326 | Split target addressed. |
packages/ui/src/chat-view.tsx | 1506 | Still large; hot path already has memoized materialization and TurnView. |
packages/ui/src/session-list-panel.tsx | 48 | Split target addressed. |
packages/ui/src/session-history-list.tsx | 854 | New focused owner; revisit only if it becomes hard to review. |
app-shell.tsx and chat-view.tsx are still large, but the original highest-risk seams have been reduced. Further splitting should require a concrete review or maintenance problem, not line count alone.
Remaining
Optional PR9 — Evidence-based memo pass
Candidate title: perf(ui-react): assess targeted memo boundaries.
Recommended handling:
- First prove prop stability or repeated render cost for a narrow boundary, likely
SessionRow, ToolActivity, or an extracted tool preview component. - Add
memo only where the test or measurement shows it avoids unrelated rerenders without hiding unstable props. - Do not touch
TurnView as a first move; it is already memoized. - If there is no useful evidence, leave the code alone and close this issue with a note that memo work is not justified right now.
Close criteria
This issue can be closed once we either:
- decide to skip PR9 because no render evidence justifies it, or
- land one small PR9 that adds only proven memo boundaries.
Out of scope
Background
The #430 converge series (motion / typography / spacing) is fully merged and token governance is complete. The React architecture cleanup surfaced by that work is tracked here; the CSS-side cleanup is tracked separately in #476.
Verified against
mainat2791ea4con 2026-07-04, after #489, #497, #502, #508, and #512 merged.Scope
This issue covers React component structure, AppShell state ownership, async UI polish, effect callback stability, and evidence-based render performance. It does not cover token values, visual redesign, or CSS architecture debt.
The high-priority React cleanup is now effectively complete. The remaining question is whether a very small evidence-based memo pass is worth doing. Do not add broad memoization without prop-stability or render-count evidence.
Completed
PR4 — Async and lazy boundaries are done
Done in #489
fix/ui-react): harden async and lazy boundaries.What changed:
ArtifactPanefallback intentionallynullunless AppShell owns an explicit artifact/list-error visibility gate.PR5 — Tool activity preview split is done
Done in #497
refactor(ui-react): split tool activity previews.What changed:
tool-activity.tsxfrom the original 1494-line catch-all into a smaller shell plus focused preview owners.Current snapshot:
packages/ui/src/tool-activity.tsxis 326 lines onmainat2791ea4c.PR6 — Session list module split is done
Done in #502
refactor(ui-react): split session list modules.What changed:
session-list-panel.tsxfrom the original 1103-line mixed owner into a small shell plussession-history-list.tsxandsession-sidebar-nav.tsxowners.sessionsnav branches.Current snapshot:
packages/ui/src/session-list-panel.tsx: 48 lines.packages/ui/src/session-history-list.tsx: 854 lines.PR7 — AppShell session UI state is done
Done in #508
refactor(ui-react): consolidate app shell session state.What changed:
PR8 — AppShell effect callback stability is done
Done in #512
refactor(ui-react): stabilize app shell effect callbacks.What changed:
useEffectEventfor long-lived subscription, timer, and listener callbacks.activeId-keyed active-session subscriptions.Current Size Snapshot
Measured on
mainat2791ea4c:apps/desktop/src/renderer/app-shell.tsxpackages/ui/src/tool-activity.tsxpackages/ui/src/chat-view.tsxTurnView.packages/ui/src/session-list-panel.tsxpackages/ui/src/session-history-list.tsxapp-shell.tsxandchat-view.tsxare still large, but the original highest-risk seams have been reduced. Further splitting should require a concrete review or maintenance problem, not line count alone.Remaining
Optional PR9 — Evidence-based memo pass
Candidate title:
perf(ui-react): assess targeted memo boundaries.Recommended handling:
SessionRow,ToolActivity, or an extracted tool preview component.memoonly where the test or measurement shows it avoids unrelated rerenders without hiding unstable props.TurnViewas a first move; it is already memoized.Close criteria
This issue can be closed once we either:
Out of scope