What happened?
The PawWork session UI exhibits visible performance regressions:
- Typing lag — characters appear with perceptible delay after each keystroke.
- Scroll stutter — the message list drops frames when scrolled.
- Both regressions worsen as the session accumulates messages.
Which area seems affected?
Session timeline rendering (packages/app/src/pages/session/) and the ScrollView primitive in packages/ui.
How much does this affect you?
User-visible regression on the core conversation surface. Long sessions become uncomfortable to read or interact with.
Steps to reproduce
- Open a session with 50+ messages.
- Type quickly in the composer — observe character latency.
- Scroll the message list rapidly — observe dropped frames.
- Open Chrome DevTools Performance and record during typing and scroll to inspect main-thread activity.
What did you expect to happen?
Typing and scrolling stay smooth (no perceptible latency, no dropped frames) for sessions up to at least 200 messages.
PawWork version
dev branch, reproduced across multiple desktop builds. Most recent observation: hand-test feedback on PR #631 surfaced INP ~200ms on trackpad scrolling.
OS version
macOS (darwin). Not OS-specific based on the rendering pipeline; likely affects Windows similarly.
Can you reproduce it again?
Yes, consistently in long sessions.
Diagnostics
Likely root causes (severity rating from the original analysis):
-
No virtualization on the message list (high) — packages/app/src/pages/session/message-timeline.tsx:1106 renders every message directly. DOM nodes and reactive computations grow linearly with message count:
<For each={rendered()}>
{(messageID) => {
const active = createMemo(() => activeMessageID() === messageID)
const comments = createMemo(() => messageComments(...))
// ...
}}
</For>
-
createMemo overuse (high) — packages/ui/src/components/message-part.tsx carries ~276 reactive calls (createMemo, createEffect, For, Show, etc.). Every state update fans out widely.
-
ScrollView scroll handler not throttled (high) — packages/ui/src/components/scroll-view.tsx:111-139 and packages/ui/src/hooks/create-auto-scroll.tsx. Each scroll event recomputes thumb position and feeds createResizeObserver:
onScroll={(e) => {
updateThumb()
// ...
}}
-
content-visibility sizing inaccurate (medium) — message-timeline.tsx:1130 uses content-visibility: auto with contain-intrinsic-size: auto 500px. The intrinsic-size estimate may be too imprecise, causing layout jitter when items enter the viewport.
-
Scroll state machine too deep (medium) — packages/app/src/pages/session/session-timeline-scroll-controller.ts (595 lines). Each scroll event traverses several state transitions, adding latency.
Estimated impact by session size (from manual observation):
| Messages |
Approx DOM nodes |
Reactive calls |
UX |
| 10 |
~100 |
~50 |
Smooth |
| 50 |
~500 |
~250 |
Mild stutter |
| 100 |
~1000 |
~500 |
Visible stutter |
| 200+ |
~2000+ |
~1000+ |
Heavy stutter |
Recommended optimization order:
- High priority: introduce virtualization (the
virtua dependency is already on the tree); hoist per-message memos to list level or use createSelector instead of per-row createMemo; throttle scroll handling and split position calculation from UI update.
- Medium priority: tighten
contain-intrinsic-size (consider contain: strict); flatten the scroll state machine.
Related work:
What happened?
The PawWork session UI exhibits visible performance regressions:
Which area seems affected?
Session timeline rendering (
packages/app/src/pages/session/) and theScrollViewprimitive inpackages/ui.How much does this affect you?
User-visible regression on the core conversation surface. Long sessions become uncomfortable to read or interact with.
Steps to reproduce
What did you expect to happen?
Typing and scrolling stay smooth (no perceptible latency, no dropped frames) for sessions up to at least 200 messages.
PawWork version
dev branch, reproduced across multiple desktop builds. Most recent observation: hand-test feedback on PR #631 surfaced INP ~200ms on trackpad scrolling.
OS version
macOS (darwin). Not OS-specific based on the rendering pipeline; likely affects Windows similarly.
Can you reproduce it again?
Yes, consistently in long sessions.
Diagnostics
Likely root causes (severity rating from the original analysis):
No virtualization on the message list (high) —
packages/app/src/pages/session/message-timeline.tsx:1106renders every message directly. DOM nodes and reactive computations grow linearly with message count:createMemooveruse (high) —packages/ui/src/components/message-part.tsxcarries ~276 reactive calls (createMemo,createEffect,For,Show, etc.). Every state update fans out widely.ScrollViewscroll handler not throttled (high) —packages/ui/src/components/scroll-view.tsx:111-139andpackages/ui/src/hooks/create-auto-scroll.tsx. Each scroll event recomputes thumb position and feedscreateResizeObserver:content-visibilitysizing inaccurate (medium) —message-timeline.tsx:1130usescontent-visibility: autowithcontain-intrinsic-size: auto 500px. The intrinsic-size estimate may be too imprecise, causing layout jitter when items enter the viewport.Scroll state machine too deep (medium) —
packages/app/src/pages/session/session-timeline-scroll-controller.ts(595 lines). Each scroll event traverses several state transitions, adding latency.Estimated impact by session size (from manual observation):
Recommended optimization order:
virtuadependency is already on the tree); hoist per-message memos to list level or usecreateSelectorinstead of per-rowcreateMemo; throttle scroll handling and split position calculation from UI update.contain-intrinsic-size(considercontain: strict); flatten the scroll state machine.Related work: