* 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
6.9 KiB
Frontend Performance Baseline and Delivery 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: Turn the current route-weight and delivery findings into repeatable gates, enable safe compression, eliminate eager case-study image downloads, and remove the mock route's whole-project trace.
Architecture: A production-server measurement script owns route asset accounting and compares it with a checked-in budget. Nginx owns textual response compression. Landing images become semantic lazy images. Static demo data is exposed through an explicit manifest so route handlers never derive filesystem paths from request input.
Tech Stack: Next.js 16, TypeScript, Rstest, Node.js, Nginx, pnpm.
Global Constraints: Work only in /Users/minimax/workspace/deer-flow/.worktrees/fix-frontend-performance. Preserve current API routes. Do not gzip SSE or already-compressed media. Every fix follows RED, GREEN, regression proof, then commit.
Task 1: Add a reproducible route-asset budget gate
Files:
-
Create:
frontend/scripts/measure-route-assets.mjs -
Create:
frontend/performance-budgets.json -
Create:
frontend/tests/unit/scripts/measure-route-assets.test.ts -
Modify:
frontend/package.json -
Modify:
frontend/AGENTS.md -
Write a failing unit test for exported
extractAssetPaths(html)andevaluateBudgets(measurements, budgets). Assert duplicate scripts are counted once and a one-byte overage fails with the route, asset class, actual bytes, and limit. -
Run
cd frontend && pnpm test -- tests/unit/scripts/measure-route-assets.test.tsand record the missing-module failure. -
Implement pure helpers in
measure-route-assets.mjs. The CLI must build withNEXT_PUBLIC_STATIC_WEBSITE_ONLY=true, spawnnext starton a free local port, fetch/,/workspace/chats, the canonical demo thread,/en/docs, and/blog/posts, resolve referenced/_next/static/files under.next/static, and emitperformance-results.json. -
Check in these initial ceilings, all below the measured baseline:
/JS 1,050,000 and CSS 150,000;/workspace/chatsJS 2,750,000 and CSS 150,000; demo chat JS 3,500,000 and CSS 150,000;/en/docsand/blog/postsJS 3,000,000 and CSS 230,000. -
Add
"perf:check": "node scripts/measure-route-assets.mjs --check"and document the gate and static-demo requirement infrontend/AGENTS.md. -
Run the focused test GREEN. Temporarily lower the
/JS budget to1, provepnpm perf:checkfails, restore the file, and defer the final passing gate until the bundle plan lands. -
Commit:
test(frontend): add route asset performance budgets.
Task 2: Enable safe Nginx compression
Files:
-
Modify:
docker/nginx/nginx.conf -
Modify:
docker/nginx/nginx.local.conf -
Create:
backend/tests/test_nginx_compression.py -
Modify:
AGENTS.md -
Write a failing test that parses both configs and asserts the same compression policy:
gzip on,gzip_vary on, minimum length 1024, compression level 5, and types limited to HTML default plus CSS, JavaScript, JSON, XML, and SVG. Asserttext/event-stream, fonts, images other than SVG, audio, and video are absent. -
Run
cd backend && uv run pytest tests/test_nginx_compression.py -qand capture RED. -
Add the identical directives to both
httpblocks. Includegzip_proxied any; do not add a wildcard content type. -
Update the service-topology note in root
AGENTS.mdto state that Nginx compresses textual responses but deliberately excludes SSE and pre-compressed media. -
Run the focused test GREEN. If local Nginx is available, start the configured service and verify
curl --compressed -IreturnsContent-Encoding: gzipfor HTML while an SSE response is uncompressed. -
Revert the directive block, prove the test fails, restore it, rerun GREEN.
-
Commit:
perf(nginx): compress textual responses safely.
Task 3: Lazy-load landing case-study images
Files:
-
Modify:
frontend/src/components/landing/sections/case-study-section.tsx -
Create:
frontend/tests/unit/components/landing/case-study-section.dom.test.tsx -
Write a DOM test that renders the section and asserts each card has an actual
imgwithloading="lazy",decoding="async", intrinsic dimensions, descriptive alt text, and no inline/background-image URL. -
Run the focused test and capture RED against the CSS backgrounds.
-
Replace background-image cards with a positioned
next/imageor native image. Keep the overlay and visual crop, set explicitsizes, and make only an above-the-fold image eager if measurement proves it is visible on initial viewport. -
Run the DOM test GREEN and verify in a browser/network trace that offscreen JPEG requests do not start before scrolling.
-
Revert the component change, prove RED, restore, rerun GREEN.
-
Commit:
perf(frontend): lazy load case study media.
Task 4: Replace request-derived mock filesystem traversal with a static manifest
Files:
-
Modify:
frontend/src/core/threads/static-demo.ts -
Modify:
frontend/src/app/mock/api/threads/[thread_id]/artifacts/[[...artifact_path]]/route.ts -
Modify:
frontend/src/app/mock/api/threads/[thread_id]/history/route.ts -
Modify:
frontend/src/app/mock/api/threads/search/route.ts -
Modify:
frontend/src/app/workspace/page.tsx -
Create:
frontend/tests/unit/core/threads/static-demo.test.ts -
Create:
frontend/tests/unit/app/mock/static-artifact-route.test.ts -
Add failing tests for
resolveStaticDemoArtifact(threadId, segments)covering a known artifact, unknown thread, traversal segments, and encoded traversal. Add a route test that asserts the response comes from the manifest-backed resolver. -
Run both focused tests and capture RED.
-
Define an explicit immutable demo manifest in
static-demo.ts; normalize and validate all path segments before a lookup. Route handlers may read only paths returned by that manifest. -
Replace request-time
readdirSync,readFileSync, andstatSyncin mock routes/workspace discovery with manifest imports orfs/promisesduring server execution. Keep response payloads byte-for-byte compatible. -
Run focused tests GREEN.
-
Run
NEXT_PUBLIC_STATIC_WEBSITE_ONLY=true pnpm buildand assert the prior Turbopack "whole project unintentionally traced" warning and mock artifact import trace are absent. -
Revert the resolver use, prove the route test RED, restore, rerun focused tests and build.
-
Commit:
perf(frontend): bound static demo file tracing.
Final verification
- Run
cd frontend && pnpm check && pnpm test. - Run
cd backend && uv run pytest tests/test_nginx_compression.py -q. - Run the static-demo production build and confirm zero unexpected-file/whole-project trace warnings.
- Record the new route measurements in the PR description; do not loosen a budget to make the gate pass.