Skip to content

perf(review): incremental loading, virtualization, and query caching#162

Merged
stylessh merged 1 commit intomainfrom
stylessh/improve-review-page
Apr 21, 2026
Merged

perf(review): incremental loading, virtualization, and query caching#162
stylessh merged 1 commit intomainfrom
stylessh/improve-review-page

Conversation

@stylessh
Copy link
Copy Markdown
Owner

@stylessh stylessh commented Apr 21, 2026

Summary

Overhauls the PR review page to stay responsive on very large PRs (1000+ files). All changes are incremental and maintain existing UX.

Scroll & rendering

  • Fix the "diff blocks sometimes don't render on scroll" bug: the near-viewport IntersectionObserver was tearing itself down on every state update (its own output was in its deps). Observers are now held in refs and elements are observed imperatively as visibleFiles grows.
  • Single mount-only observer lifecycle also applied to the commit diff pane.
  • Eagerly kick off @pierre/diffs/react and the theme registration at module eval so Suspense boundaries don't queue a waterfall of lazy imports.
  • Memoize renderAnnotation, FileHeader, estimateDiffHeight, and split diffOptions theme CSS from structural options so theme toggles don't invalidate every block.

Data fetching

  • File summaries — moved from REST listFiles (returned full patches, ~50 MB for 3k files, failed KV cache writes) to GraphQL pullRequest.files requesting only path, changeType, additions, deletions. ~100× smaller payloads, fits in KV cache.
  • Incremental summariesgetPullFileSummaries now takes a cursor and returns one batch of ~500 files (5 GraphQL pages bundled server-side). Client uses useInfiniteQuery with a background auto-prefetch effect, so a 3k-file PR renders the first files in ~300 ms instead of a 9 s blank wait.
  • Incremental patches — same auto-prefetch pattern on getPullFiles so deeper diffs are in cache by the time the user scrolls to them.
  • Toolbar counts — read additions / deletions / changedFiles directly off the PR detail (one query) instead of summing streamed summaries. Chip is accurate on first paint.
  • Narrowed submitPullReview invalidateQueries to only this PR's keys instead of githubQueryKeys.all.

Virtualization

  • File tree in the sidebar is now virtualized with @tanstack/react-virtual (fixed 30 px rows). A 1200-node tree mounts ~32 DOM nodes instead of 1200; expand state hoisted to a central Set.

Caching

  • matchesTabQuery in query-client.tsx now handles "review" and "commit" tab types, so closing+reopening those tabs rehydrates from localStorage instead of reloading.
  • Review tab persists page / fileSummaries / reviewComments / reviewThreads. files (patches) deliberately excluded — too large for localStorage.

Shiki grammar fallback

  • At Pierre init, walk EXTENSION_TO_FILE_FORMAT and redirect any extension mapping to a grammar not in our Shiki bundle → text. No more "Language objective-cpp not found" errors; self-healing for any future Pierre additions.

Misc

  • Default diff style switched to split.
  • Right-aligned loading spinner in sidebar footer.

Test plan

  • Open a large PR (1000+ files) and confirm sidebar + toolbar chip appear in <1 s
  • Scroll through diffs quickly — no blank placeholders
  • Open a .h / .rb / Makefile diff — renders as plain text, no Shiki error
  • Close a review tab, reopen from history — sidebar + header hydrate instantly from localStorage
  • Submit a review — only this PR's queries invalidate
  • File tree with 1000+ nodes scrolls smoothly

Summary by CodeRabbit

  • New Features

    • Added virtual scrolling to file tree for improved performance with large pull requests
    • Implemented progressive loading of file summaries in the sidebar for faster initial load times
  • Performance

    • Optimized diff pane rendering with smarter viewport tracking to reduce re-renders
    • Enhanced memory efficiency through improved state management

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR implements virtualization and pagination for pull request review interfaces. It replaces REST-based file summaries with GraphQL batch pagination, introduces a virtualized file tree component, refactors viewport tracking to use refs and reducers instead of state, enhances diff block rendering with improved Pierre/Shiki integration, and switches query handling from single-page to infinite pagination with automatic background loading.

Changes

Cohort / File(s) Summary
Virtualization Dependencies
apps/dashboard/package.json
Added @tanstack/react-virtual dependency for virtualization support.
Viewport Tracking Refactoring
apps/dashboard/src/components/pulls/review/review-diff-pane.tsx, apps/dashboard/src/components/repo/commit-diff-pane.tsx
Replaced viewport state (useState<Set>) with mutable refs (useRef<Set> + useReducer) to avoid observer teardown on scroll updates. Split IntersectionObserver responsibilities into separate long-lived near-viewport and active-file observers with explicit refs and adjusted root margins.
Diff Block Enhancement
apps/dashboard/src/components/pulls/review/review-file-diff-block.tsx
Refactored Pierre/Shiki integration with module-level promise caching, added Shiki bundle-aware extension mapping override, introduced memoized unsafeCSS and placeholder height calculation, extracted stable memoized callbacks for renderAnnotation and collapse toggling.
File Tree Virtualization
apps/dashboard/src/components/pulls/review/review-file-tree.tsx
Replaced recursive ReviewFileTreeNode with new ReviewVirtualizedFileTree component using useVirtualizer for fixed-height rows. Centralized directory expansion state with explicit override set instead of per-node local state; added memoized DirectoryRow component.
GraphQL Pagination Implementation
apps/dashboard/src/lib/github.functions.ts, apps/dashboard/src/lib/github.query.ts, apps/dashboard/src/lib/github.types.ts
Migrated pull file summaries from REST single-call to GraphQL batch pagination; getPullFileSummaries now accepts cursor, returns PullFileSummariesPage with items and nextCursor, and batches multiple pages per fetch. Added pagination types and updated query options to use infiniteQueryOptions.
Query Client & Persistence
apps/dashboard/src/lib/query-client.tsx, apps/dashboard/src/routes/_protected/$owner/$repo/review.$pullId.tsx
Extended matchesTabQuery to recognize review/commit-specific query resources and validate alignment with tab context. Updated route prefetching from prefetchQuery to prefetchInfiniteQuery for file summaries.
Page-Level Integration
apps/dashboard/src/components/pulls/review/review-page.tsx
Switched file summaries and files queries to infinite queries with auto-pagination effects. Updated sidebar stats from streamed file counts to PR-level totals, changed diff style default to "split", improved cache invalidation predicate, and integrated ReviewVirtualizedFileTree with streaming state feedback.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Review Page
    participant QC as Query Client
    participant GraphQL as GraphQL API
    participant Cache as Cache Storage
    participant Tree as File Tree UI

    Client->>QC: Render with fileSummariesQuery
    QC->>Cache: Check cache for initial page
    Cache-->>QC: Cache miss or exists
    QC->>GraphQL: Fetch first 100 files (cursor: null)
    GraphQL-->>QC: Return items + nextCursor
    QC->>Cache: Store page 1 results
    QC-->>Client: Display initial files + loading indicator
    
    Note over Client,Tree: Background auto-pagination
    Client->>QC: Effect detects hasNextPage
    QC->>GraphQL: Fetch next batch (cursor: page1.nextCursor)
    GraphQL-->>QC: Return items + nextCursor
    QC->>Cache: Store page 2 results
    
    Client->>Tree: Render flattened + virtualized items
    Tree->>Tree: Show only visible rows at ROW_HEIGHT
    Note over Tree: IntersectionObserver tracks<br/>near-viewport and active files
    
    loop While hasNextPage
        Client->>QC: Continue auto-fetching background pages
        QC->>GraphQL: Fetch paginated results
        GraphQL-->>QC: Return page results
        QC->>Cache: Accumulate pages
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title precisely summarizes the main optimization changes: incremental loading, virtualization, and query caching improvements for the review page.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering all major changes across scroll & rendering, data fetching, virtualization, caching, and Shiki grammar fallback, with a detailed test plan, though it deviates from the repository template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stylessh/improve-review-page

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant