Skip to content

Conversation

@danhilse
Copy link

@danhilse danhilse commented Sep 1, 2025

Automated PR from fork branch 'feature/batch-copy-markdown'.

… '# Folder: <name>' headers); toasts and dedupe
Copy link

@greptile-apps greptile-apps bot left a 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.tsx lines 157-195 where multiple nested loops with await calls could create performance bottlenecks

3 files reviewed, 3 comments

Edit Code Review Bot Settings | Greptile

setLastSelectedFilePath(path)
return
}
const onlyFiles = vaultFilesFlattened
Copy link

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

Suggested change
const onlyFiles = vaultFilesFlattened
const flattenedItems = vaultFilesFlattened
Comment on lines +157 to +162
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)
}
Copy link

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.

Comment on lines +188 to +195
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 || '')
}
}
Copy link

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.

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

Labels

None yet

1 participant