Files
Codewalkers/apps/web/package.json
Lukas May 0323b42667 feat: virtualize ReviewSidebar file list for >50 items with scroll preservation
Adds windowed rendering to FilesView in ReviewSidebar.tsx using
react-window 2.x (List component). File lists with more than 50 rows
render only visible items, keeping the DOM lean for large diffs.

- Install react-window 2.x and @types/react-window in apps/web
- Flatten directory-grouped file tree into a typed Row[] array via useMemo
- Use VariableSizeList-equivalent react-window 2.x List with rowHeight fn
  (32px for dir-headers, 40px for file rows); falls back to plain DOM
  render for ≤50 rows to avoid overhead on small diffs
- Directories are collapsible: clicking the dir-header toggles collapse,
  removing its file rows from the Row[] and from the virtual list
- Preserve sidebar scroll offset across Files ↔ Commits tab switches via
  filesScrollOffsetRef passed from ReviewSidebar into FilesView
- Clicking a file calls listRef.scrollToRow({ index, align: "smart" })
  to keep the clicked row visible in the virtual list
- Root-level files (directory === "") render without a dir-header,
  preserving existing behavior
- Add resolve.dedupe for react/react-dom in vitest.config.ts to prevent
  duplicate-React errors after local workspace package installation
- Add 6 Vitest + RTL tests covering: large-list DOM count, small-list
  fallback, collapse, re-expand, tab-switch smoke, root-level files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 19:50:53 +01:00

59 lines
1.7 KiB
JSON

{
"name": "@codewalk-district/web",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"lint": "tsc --noEmit"
},
"dependencies": {
"@codewalk-district/shared": "*",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-query": "^5.75.0",
"@tanstack/react-router": "^1.158.0",
"@tiptap/extension-link": "^3.19.0",
"@tiptap/extension-placeholder": "^3.19.0",
"@tiptap/extension-table": "^3.19.0",
"@tiptap/html": "^3.19.0",
"@tiptap/pm": "^3.19.0",
"@tiptap/react": "^3.19.0",
"@tiptap/starter-kit": "^3.19.0",
"@tiptap/suggestion": "^3.19.0",
"@trpc/client": "^11.9.0",
"@trpc/react-query": "^11.9.0",
"@types/react-window": "^1.8.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"geist": "^1.7.0",
"lucide-react": "^0.563.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-window": "^2.2.7",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tippy.js": "^6.3.7"
},
"devDependencies": {
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-slot": "^1.2.4",
"@tailwindcss/typography": "^0.5.19",
"@tanstack/router-plugin": "^1.158.0",
"@types/node": "^25.2.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.24",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.19",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.7.3",
"vite": "^6.1.0"
}
}