Skip to content

[Bug] Session UI typing lag and scroll stuttering #615

@Spongeacer

Description

@Spongeacer

What happened?

The PawWork session UI exhibits visible performance regressions:

  1. Typing lag — characters appear with perceptible delay after each keystroke.
  2. Scroll stutter — the message list drops frames when scrolled.
  3. 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

  1. Open a session with 50+ messages.
  2. Type quickly in the composer — observe character latency.
  3. Scroll the message list rapidly — observe dropped frames.
  4. 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):

  1. 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>
  2. 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.

  3. 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()
      // ...
    }}
  4. 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.

  5. 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:

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High priorityappApplication behavior and product flowsbugSomething isn't workinguiDesign system and user interface

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions