Skip to content

Conversation

@weilirs
Copy link
Collaborator

@weilirs weilirs commented Nov 4, 2024

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.

PR Summary

Added @ syntax functionality for referencing files in chat context, enabling users to search and include files in conversations through an autocomplete interface.

  • Potential security risk in searchFiles function as it exposes file paths without proper validation or sanitization in electron/main/filesystem/filesystem.ts
  • File search implementation in searchFiles loads all files into memory before filtering, which could cause performance issues with large directories
  • Regex pattern /@([^@]+?\.md)/g in extractFileReferences is fragile and only handles .md files without validating existence
  • Caret position calculation in getCaretCoordinates creates/removes DOM elements frequently without debouncing
  • Missing error handling for file operations and IPC calls across multiple components

7 file(s) reviewed, 17 comment(s)
Edit PR Review Bot Settings | Greptile

return { localModelPath, repoName }
}

export const searchFiles = async (store: Store<StoreSchema>, searchTerm: string): Promise<string[]> => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Function marked as async but contains no await operations

}

export const searchFiles = async (store: Store<StoreSchema>, searchTerm: string): Promise<string[]> => {
const vaultDirectory = store.get(StoreKeys.DirectoryFromPreviousSession)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Using DirectoryFromPreviousSession instead of current window's vault directory could cause inconsistencies

Comment on lines 229 to 233
const allFiles = GetFilesInfoList(vaultDirectory)

const searchTermLower = searchTerm.toLowerCase()

const matchingFiles = allFiles.filter((file) => file.name.toLowerCase().startsWith(searchTermLower))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Loading all files into memory before filtering could be inefficient for large vaults. Consider using a streaming/iterative approach

Comment on lines +225 to +227
if (!vaultDirectory || typeof vaultDirectory !== 'string') {
throw new Error('No valid vault directory found')
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Error handling could provide more context about why the vault directory is invalid

Comment on lines +198 to +200
ipcMain.handle('search-files', async (_event, searchTerm: string) => {
return searchFiles(store, searchTerm)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: No error handling for searchFiles - could throw unhandled exceptions if store is invalid or searchTerm is malformed. Consider wrapping in try/catch.

>
{files.map((file) => (
<div key={file} className="cursor-pointer px-4 py-2 hover:bg-neutral-700" onClick={() => onSelect(file)}>
{file.split('/').pop()}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: file.split('/').pop() could return undefined if path ends in /

Comment on lines 176 to 180
const handleFileSelect = (filePath: string) => {
const lastAtIndex = userTextFieldInput.lastIndexOf('@')
const newValue = `${userTextFieldInput.slice(0, lastAtIndex)}@${filePath} ${userTextFieldInput.slice(
lastAtIndex + searchTerm.length + 1,
)}`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Potential XSS vulnerability - filePath needs to be sanitized before being inserted into the text input

Comment on lines 114 to 121
// Create a hidden div with the same styling as textarea
const mirror = document.createElement('div')
mirror.style.cssText = window.getComputedStyle(element).cssText
mirror.style.height = 'auto'
mirror.style.position = 'absolute'
mirror.style.visibility = 'hidden'
mirror.style.whiteSpace = 'pre-wrap'
document.body.appendChild(mirror)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Memory leak risk - mirror element needs cleanup if component unmounts while calculating coordinates

Comment on lines +20 to +21
const regex = /@([^@]+?\.md)/g
const matches = message.match(regex)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: regex pattern only matches .md files and doesn't handle spaces or special characters in filenames

Comment on lines 60 to 61
const fileRefs = extractFileReferences(userTextFieldInput || '')
// console.log('fileRefs', fileRefs)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: no validation that referenced files exist before adding to chat context

@milaiwi
Copy link
Collaborator

milaiwi commented Nov 4, 2024

Will take a look tonight

Edit: Delayed due to comment below.

@samlhuillier
Copy link
Collaborator

I'm afraid we'll need to hold off on merging this PR. I'm doing a bit of a revamp of chat to get it working nicely in the sidebar #472 so this feature can only be merged after that's done

@weilirs weilirs marked this pull request as draft November 5, 2024 01:54
@weilirs
Copy link
Collaborator Author

weilirs commented Nov 5, 2024

I'm afraid we'll need to hold off on merging this PR. I'm doing a bit of a revamp of chat to get it working nicely in the sidebar #472 so this feature can only be merged after that's done

Okay, this PR needs more work as well, just want some opinions from you guys.

@weilirs weilirs marked this pull request as ready for review November 20, 2024 08:59
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.

PR Summary

(updates since last review)

This PR continues to evolve the @ syntax feature for file references in chat context, with several new changes and components. Here's what's new:

  • Added FileAutocomplete component in /src/components/Chat/FileAutocomplete.tsx for handling file selection UI with hover previews
  • Implemented ChatSources component in /src/components/Chat/MessageComponents/ChatSources.tsx to display referenced files with preview functionality
  • Added vault path resolution in extractFileReferences function to convert relative paths to absolute paths
  • Introduced analytics tracking for LLM-related actions in DefaultLLMSelector.tsx

The implementation still needs attention in these areas:

  • File path handling in FileAutocomplete doesn't account for different operating systems' path separators
  • Autocomplete positioning logic may break with scrolling/resizing
  • Missing loading/empty states in FileAutocomplete component
  • No debouncing on file search operations which could impact performance

9 file(s) reviewed, 14 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines +214 to +215
const matchingFiles = allFiles
.filter((file) => file.name.toLowerCase().startsWith(searchTermLower))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Case-sensitive path handling could cause issues on Windows. Consider using case-insensitive comparison with localeCompare

.filter((file) => file.name.toLowerCase().startsWith(searchTermLower))
.map((file) => ({
absolutePath: file.path,
relativePath: file.path.replace(vaultDirectory, '').replace(/^[/\\]+/, ''),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Path separator handling with replace(/^[/\]+/, '') may not work correctly on Windows. Use path.normalize() instead

Comment on lines +164 to +166
ipcMain.handle('get-vault-path', async () => {
return getVaultPath(store)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: No error handling for getVaultPath - could expose system errors to renderer process

Comment on lines +95 to +98
searchFiles:
createIPCHandler<(searchTerm: string) => Promise<Array<{ absolutePath: string; relativePath: string }>>>(
'search-files',
),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Consider adding input validation/sanitization for searchTerm to prevent path traversal attacks

getAllFilenamesInDirectory: createIPCHandler<(dirName: string) => Promise<string[]>>('get-files-in-directory'),
getFiles: createIPCHandler<(filePaths: string[]) => Promise<FileInfoWithContent[]>>('get-files'),
searchFiles:
createIPCHandler<(searchTerm: string) => Promise<Array<{ absolutePath: string; relativePath: string }>>>(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: The return type should be more specific - consider creating an interface for the path object structure

Comment on lines +39 to +44
className="absolute z-50 max-h-48 w-64 overflow-y-auto rounded-md border border-neutral-700 bg-background shadow-lg"
style={{
top: 'auto',
bottom: `calc(100% + 10px)`,
left: position.left,
}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: absolute positioning with fixed width could cause overflow issues on small screens. Consider making width responsive or adding overflow handling

left: position.left,
}}
>
{files.map((file) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider limiting number of results shown to prevent performance issues with large file lists

Comment on lines +25 to +30
const vaultPath = await window.fileSystem.getVaultPath()
return matches.map((match) => {
const relativePath = match.slice(1) // Remove @ symbol

return `${vaultPath}/${relativePath}`
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: path concatenation with string interpolation is unsafe and may cause path traversal vulnerabilities. Use path.join() instead

return
}

const fileRefs = await extractFileReferences(userTextFieldInput || '')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider adding error handling for when getVaultPath() fails

Comment on lines 30 to 33
const handleDeleteLLM = async (modelName: string) => {
// eslint-disable-next-line no-alert
const confirmDelete = window.confirm(`Are you sure you want to delete the model ${modelName}?`)
if (!confirmDelete) return
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using window.confirm is not ideal for modern UX. Consider using a modal dialog component instead.

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

Labels

None yet

3 participants