-
Notifications
You must be signed in to change notification settings - Fork 507
feat(files): multi-select + bulk Copy Markdown; support folders (with '# Folder: <name>' headers); toasts and dedupe #525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
… '# Folder: <name>' headers); toasts and dedupe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Summary
This PR introduces multi-file selection capabilities to the file sidebar and implements a bulk "Copy Markdown" feature. The changes add comprehensive file selection functionality with support for single-click selection, Ctrl/Cmd+click for multi-select, and Shift+click for range selection - standard file management UX patterns.
The implementation centers around new state management in FileContext.tsx that tracks selected files through a selectedFilePaths array and lastSelectedFilePath for range selection anchoring. Four new methods handle different selection modes: selectSingleFile() for replacing current selection, toggleSelectFile() for adding/removing individual files, selectRangeTo() for shift-click range selection, and clearSelection() for clearing all selections.
The bulk copy feature reads content from multiple selected files asynchronously and formats the output with folder headers ("# Folder: ") when directories are selected. This allows users to aggregate content from multiple notes into a single markdown document, maintaining organizational structure through the folder headers.
The range selection logic uses findIndex() on the flattened file list to calculate proper ordering regardless of the file tree structure, ensuring bidirectional ranges work correctly. Visual selection indicators are updated to reflect the new multi-select state, and the context menu now includes the "Copy Markdown" option when files are selected.
The .gitignore addition excludes an "AGENTS.md" file that appears to be generated during development or testing of the new functionality.
Confidence score: 3/5
- This PR requires careful review due to performance implications from nested async loops in the bulk copy feature
- Score reflects concerns about sequential await calls in loops which could cause UI blocking and poor user experience with large file selections
- Pay close attention to
FileItemRows.tsxlines 157-195 where multiple nested loops with await calls could create performance bottlenecks
3 files reviewed, 3 comments
| setLastSelectedFilePath(path) | ||
| return | ||
| } | ||
| const onlyFiles = vaultFilesFlattened |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Variable name 'onlyFiles' is misleading - this array contains both files and directories based on vaultFilesFlattened
| const onlyFiles = vaultFilesFlattened | |
| const flattenedItems = vaultFilesFlattened |
| for (const p of baseSelection) { | ||
| // eslint-disable-next-line no-await-in-loop | ||
| const isDir = await window.fileSystem.isDirectory(p) | ||
| if (isDir) dirPaths.push(p) | ||
| else filePaths.push(p) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Multiple await calls in loop can cause performance issues with many selected files. Consider using Promise.all() to parallelize these operations.
| for (const p of filePaths) { | ||
| if (!seenFiles.has(p)) { | ||
| seenFiles.add(p) | ||
| // eslint-disable-next-line no-await-in-loop | ||
| const c = await window.fileSystem.readFile(p, 'utf-8') | ||
| parts.push(c || '') | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Another sequential await loop that could benefit from Promise.all() for better performance.
Automated PR from fork branch 'feature/batch-copy-markdown'.