deer-flow/docs/superpowers/plans/2026-07-31-frontend-performance-chat-runtime.md
DanielWalnut 459dd78707
perf(frontend): bound delivery, bundles, and long-running UI work (#4622)
* docs: design frontend performance remediation

* docs: plan frontend performance remediation

* test(frontend): add route asset performance budgets

* perf(nginx): compress textual responses safely

* perf(frontend): lazy load case study media

* perf(frontend): bound static demo file tracing

* perf(frontend): restore static locale boundaries

* perf(frontend): defer closed workspace panels

* perf(frontend): split editors and deduplicate highlighting

* perf(frontend): index incremental message derivation

* perf(frontend): stabilize paged history cache policy

* perf(frontend): bound streaming markdown renders

* perf(frontend): virtualize message history

* perf(frontend): bound and virtualize chat lists

* perf(frontend): suspend inactive decorative animation

* perf(browser): stream latest frames as binary

* perf(artifacts): stream bounded text previews

* docs: finalize performance runtime boundaries

* style(backend): apply test formatting

* fix(frontend): keep translation functions client-side

* perf(frontend): defer decorative animation bundles

* test(frontend): lock optimized route budgets

* fix: harden frontend performance boundaries

* test(frontend): update i18n provider fixture

* fix(frontend): preserve sidebar pagination position

* style(backend): format artifact range test
2026-08-01 22:19:59 +08:00

7.1 KiB

Frontend Chat Runtime Performance Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Keep streaming chat, long histories, Markdown, and chat navigation responsive with work proportional to the changed or visible data rather than the entire history.

Architecture: Pure indexed derivation functions update only affected turns. Query policy avoids surprise focus refetches. TanStack Virtual owns visible message/chat windows with measured rows and anchored scrolling. Streaming Markdown commits bounded chunks at a fixed cadence.

Tech Stack: React 19, TanStack Query, @tanstack/react-virtual, Rstest/happy-dom, TypeScript.

Global Constraints: Preserve message ordering, tool-result association, human-input cards, usage totals, stick-to-bottom behavior, pagination anchors, and accessibility. Add the virtualization dependency only after its first RED test.

Task 1: Index message-derived state once per update

Files:

  • Modify: frontend/src/components/workspace/messages/message-list.tsx

  • Modify: frontend/src/components/workspace/messages/message-group.tsx

  • Modify: frontend/src/core/messages/utils.ts

  • Create: frontend/src/core/messages/derived-state.ts

  • Create: frontend/tests/unit/core/messages/derived-state.test.ts

  • Modify: frontend/tests/unit/components/workspace/messages/message-group.test.ts

  • Write failing pure tests for deriveMessageState(messages, previous?): assistant-turn usage, tool-call/result lookup, workspace-change anchors, and stable object identity for unchanged completed turns when only the streaming tail changes.

  • Add a 5,000-message operation-count fixture that fails if derivation revisits every completed item on a tail-only update.

  • Run focused tests and capture RED.

  • Implement maps keyed by message ID/tool-call ID and a step-index map keyed by step identity. Replace steps.indexOf(step) and repeated full-history scans in render paths with precomputed indices.

  • Memoize the derived state at the message-list boundary and pass precise slices to groups/items.

  • Run tests GREEN; revert the incremental reuse branch to prove the tail-update test RED, then restore.

  • Commit: perf(frontend): index incremental message derivation.

Task 2: Make history pagination focus-stable

Files:

  • Modify: frontend/src/core/threads/hooks.ts

  • Create: frontend/tests/unit/core/threads/thread-history-options.test.ts

  • Write a failing query-options test asserting refetchOnWindowFocus: false, explicit staleTime, and unchanged next-page cursor behavior.

  • Run the focused test and capture RED.

  • Set refetchOnWindowFocus: false for paged immutable history while leaving active-run streaming/cache writes authoritative. Use a concrete five-minute staleTime; explicit refresh/invalidation remains available.

  • Run GREEN; revert the option, prove RED, restore.

  • Commit: perf(frontend): stabilize paged history cache policy.

Task 3: Bound streaming Markdown work

Files:

  • Modify: frontend/src/components/workspace/messages/markdown-content.tsx

  • Modify: frontend/tests/unit/components/workspace/messages/markdown-content.dom.test.tsx

  • Modify: frontend/tests/unit/components/workspace/messages/markdown-content.test.ts

  • Add fake-timer tests asserting a long streaming append causes no more than one rendered-content update per 50 ms, the final content becomes exact within 300 ms after stream completion, unmount cancels work, and reduced-motion renders immediately.

  • Run focused tests and capture RED against requestAnimationFrame-per-growth behavior.

  • Replace per-frame growing-string state with a bounded scheduler: retain the latest target in a ref, commit at most every 50 ms, reveal at least 64 new characters per commit, and flush the exact target on completion. Memoize parsed output by the committed string.

  • Run GREEN. Restore the old RAF scheduler temporarily and confirm the update-count assertion RED, then restore the fix.

  • Commit: perf(frontend): bound streaming markdown renders.

Task 4: Virtualize long message histories

Files:

  • Modify: frontend/package.json

  • Modify: pnpm-lock.yaml

  • Create: frontend/src/components/workspace/messages/virtual-message-list.tsx

  • Modify: frontend/src/components/workspace/messages/message-list.tsx

  • Create: frontend/tests/unit/components/workspace/messages/virtual-message-list.dom.test.tsx

  • Modify: relevant Playwright chat history tests under frontend/tests/e2e/

  • Write a DOM test with 2,000 variable-height rows and assert fewer than 80 message groups are mounted, the first/last items become reachable, and appending while pinned keeps the bottom anchored.

  • Add a pagination-anchor test: prepending older rows keeps the previously visible message at the same visual offset.

  • Run the tests and capture RED.

  • Add @tanstack/react-virtual with pnpm. Implement measured rows with stable message-group IDs, overscan 8, explicit scroll-margin handling, and pin-to-bottom state integrated with use-stick-to-bottom.

  • Keep live assistant/tool rows mounted while active even if measurement changes; announce newly arrived assistant content through the existing accessible live region.

  • Run DOM and focused E2E tests GREEN. Temporarily replace the virtual items with the full array and prove the mount-count test RED, then restore.

  • Commit: perf(frontend): virtualize message history.

Task 5: Normalize, bound, and virtualize chat navigation

Files:

  • Modify: frontend/src/components/workspace/recent-chat-list.tsx

  • Modify: frontend/src/app/workspace/chats/page.tsx

  • Create: frontend/src/core/threads/thread-list-model.ts

  • Create: frontend/tests/unit/core/threads/thread-list-model.test.ts

  • Create: frontend/tests/unit/components/workspace/recent-chat-list.dom.test.tsx

  • Write failing tests proving sidebar and page share one normalized Map<threadId, ThreadSummary>, sorting happens only when page data changes, retained pages are capped at 200 threads, and a 1,000-row fixture mounts fewer than 60 rows.

  • Run focused tests and capture RED.

  • Move dedupe/sort into one memoized selector. Use bounded infinite-query retention and virtual rows for both consumers. Keep sentinel pagination based on the virtualizer's final item instead of a permanently rendered DOM tail.

  • Preserve active-thread visibility and keyboard/focus semantics.

  • Run tests GREEN and relevant chats E2E. Reintroduce full mapping to prove mount-count RED, restore.

  • Commit: perf(frontend): bound and virtualize chat lists.

Final verification

  • Run cd frontend && pnpm check && pnpm test.
  • Run Playwright coverage for long history, pagination, active streaming, and chat navigation.
  • Profile a 5,000-message synthetic thread: record commit count and maximum rendered rows before/after in the PR description.
  • Run pnpm perf:check and confirm virtualization does not breach route budgets.