Skip to content

Commit 6fc1d1b

Browse files
committed
docs: add file tree virtualization design
1 parent f84d418 commit 6fc1d1b

1 file changed

Lines changed: 147 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# File Tree Virtualization Design
2+
3+
## Context
4+
5+
`components/FileTree.tsx` currently renders the full expanded tree recursively. For large repositories this creates thousands of mounted DOM nodes, repeated descendant counting, and expensive re-renders for expand/collapse, keyboard navigation, and selection changes. The first performance milestone is to virtualize only the file tree without changing parsing, search, or export behavior.
6+
7+
## Goals
8+
9+
- Keep the existing tree data model (`FileNode[]`) and `ProcessedFiles` shape unchanged.
10+
- Reduce mounted tree row count to a viewport-sized window instead of the full expanded tree.
11+
- Preserve current interactions:
12+
- expand/collapse
13+
- keyboard navigation
14+
- file selection
15+
- directory double click
16+
- file actions (copy path, exclude/include, delete)
17+
- selected and focused row styling
18+
- Keep the mobile and desktop layouts working with the current `MainContent.tsx` split-panel structure.
19+
20+
## Non-Goals
21+
22+
- No Web Worker work in this milestone.
23+
- No ZIP parsing changes in this milestone.
24+
- No search algorithm changes in this milestone.
25+
- No changes to `ProcessedFiles.fileContents`, `treeData`, or `structureString` synchronization rules.
26+
27+
## Chosen Approach
28+
29+
Use a virtualized flat list for the visible tree rows while keeping the source tree nested.
30+
31+
### Why this approach
32+
33+
- It isolates the change to the rendering layer.
34+
- It avoids risky changes to the repository data model.
35+
- It works with the current expand/collapse state by deriving a flat visible-row list from `nodes` and `expandedPaths`.
36+
- It gives an immediate DOM-count reduction even before any Worker work lands.
37+
38+
### Library choice
39+
40+
Prefer `react-virtuoso`.
41+
42+
Reasoning:
43+
44+
- The current tree rows can expand in height when hover actions appear, so a variable-height-friendly list is safer than a fixed-row abstraction.
45+
- It supports container-based virtualization cleanly for the existing scrollable panel.
46+
- It reduces the amount of manual measuring code we would need to maintain.
47+
48+
## Architecture
49+
50+
### 1. Flatten visible rows
51+
52+
Add a small tree-flattening helper inside `components/FileTree.tsx` or a nearby helper module.
53+
54+
Each flat row will contain:
55+
56+
- `node: FileNode`
57+
- `level: number`
58+
- `isOpen: boolean`
59+
- `isSelected: boolean`
60+
- `isFocused: boolean`
61+
62+
The flat list is recalculated from:
63+
64+
- `nodes`
65+
- `expandedPaths`
66+
- `selectedFilePath`
67+
- `focusedPath`
68+
69+
Only directory descendants under expanded paths are included.
70+
71+
### 2. Replace recursive render with row renderer
72+
73+
Split the current recursive `FileTreeNode` rendering into:
74+
75+
- a reusable row component that renders one node row
76+
- a virtualized list wrapper that renders the visible flat rows
77+
78+
Directory expansion state remains in `expandedPaths`.
79+
80+
### 3. Preserve keyboard navigation semantics
81+
82+
Keyboard navigation continues to operate on the visible path order, but the visible path list will now come from the flattened rows instead of a separate recursive walk.
83+
84+
On focus movement, the virtualized list should scroll the focused row into view.
85+
86+
### 4. Preserve action behavior
87+
88+
Row actions continue to call the existing callbacks:
89+
90+
- `onFileSelect`
91+
- `onDeleteFile`
92+
- `onCopyPath`
93+
- `onToggleExclude`
94+
- `onDirDoubleClick`
95+
96+
No business logic changes are required in `useInteraction.ts` or `useAppLogic.ts`.
97+
98+
## Files Expected To Change
99+
100+
- Modify `components/FileTree.tsx`
101+
- Modify `package.json`
102+
- Modify `package-lock.json`
103+
- Add `components/FileTree.test.tsx`
104+
105+
## Risks And Mitigations
106+
107+
### Risk: flattening breaks expand/collapse or selection behavior
108+
109+
Mitigation:
110+
111+
- keep the original `expandedPaths` state shape
112+
- derive rows from existing `FileNode.path`
113+
- cover expand/collapse and selection behavior with component tests
114+
115+
### Risk: keyboard navigation loses parity
116+
117+
Mitigation:
118+
119+
- derive visible order from the flattened rows
120+
- add tests for arrow navigation and Enter behavior
121+
122+
### Risk: variable-height rows produce jitter
123+
124+
Mitigation:
125+
126+
- use a virtualization library that supports dynamic row sizing
127+
- keep row structure close to the current DOM and CSS
128+
129+
## Testing Strategy
130+
131+
Add component tests focused on behavior instead of implementation details:
132+
133+
1. renders only a bounded number of visible rows for a large expanded tree
134+
2. collapses a directory and removes descendants from the visible row set
135+
3. supports keyboard navigation across visible rows and Enter activation
136+
4. preserves file action buttons for processed files
137+
138+
Manual verification:
139+
140+
- load a generated tree with thousands of files
141+
- confirm tree scrolling stays responsive
142+
- confirm DOM row count stays near viewport scale rather than total node count
143+
- confirm mobile and desktop tree panels still render and scroll correctly
144+
145+
## Rollout Notes
146+
147+
This milestone intentionally stops at render virtualization. If it lands cleanly, the next stage can address main-thread parsing and search with Workers without mixing concerns in one refactor.

0 commit comments

Comments
 (0)