Skip to content

Conversation

@spencrmartin
Copy link
Collaborator

@spencrmartin spencrmartin commented Jul 3, 2025

This PR implements a comprehensive fuzzy file search feature for the Goose desktop application.

Features

🔍 Fuzzy Search Algorithm

  • Custom fuzzy matching with scoring based on consecutive matches and word boundaries
  • Highlights matched characters in search results
  • Prioritizes matches at the beginning of words and file names

📁 File System Integration

  • Recursive directory scanning with depth limits for performance
  • Intelligent ignore patterns (node_modules, .git, pycache, etc.)
  • Distinguishes between files and directories with visual indicators

⌨️ Keyboard Navigation

  • Arrow keys for navigation
  • Enter to select
  • Escape to close
  • Full keyboard accessibility

🎨 UI/UX

  • Clean, responsive design matching the app's theme
  • Loading states during file scanning
  • Scrollable results with automatic scroll-to-selected
  • Shows current working directory and result count

Implementation Details

  • FuzzyFileSearch.tsx: Main component with fuzzy matching algorithm
  • ChatInput.tsx: Integration point for triggering the search
  • Performance optimized with result limiting and shallow directory scanning
  • Uses existing UI components (Card, ScrollArea) for consistency

Testing

  • Manual testing of search functionality
  • Keyboard navigation testing
  • Performance testing with large directories
  • UI responsiveness across different screen sizes

This is a draft PR to start the fuzzy search feature development. Ready for review and feedback!

@spencrmartin spencrmartin mentioned this pull request Jul 3, 2025
1 task
@spencrmartin
Copy link
Collaborator Author

Screen.Recording.2025-07-03.at.10.07.04.AM.mov

fuzzy search using electron IPC with individual icons for different filetypes

- Implement FuzzyFileSearch component with fuzzy matching algorithm
- Add file scanning with directory traversal and ignore patterns
- Include keyboard navigation (arrow keys, enter, escape)
- Support highlighting of matched characters in search results
- Integrate with ChatInput component for file selection
- Add loading states and error handling
- Limit results to 50 items for performance
- Add MentionPopover component for @ mention file selection
- Update FuzzyFileSearch to support computer-wide search with searchFromRoot prop
- Implement @ mention detection in ChatInput with cursor position tracking
- Add popover positioning logic based on cursor location
- Support both local directory and full computer file search
- Update placeholder text to indicate @ mention functionality
- Fix TypeScript errors and improve type safety
- Optimize directory scanning with depth limits and file type filtering
- Redesign MentionPopover to show file list above chat input
- Add file type icons for different file extensions and folders
- Display file name in bold with full path below in gray text
- Implement fuzzy search functionality directly in the popover
- Add keyboard navigation (arrow keys, enter, escape)
- Show 'Show X more...' when there are additional results
- Position popover above chat input with proper styling
- Optimize file scanning for better performance
- Add loading state while scanning files
- Remove unused imports and fix TypeScript errors
- Create FileIcon component with simple SVG icons for different file types
- Replace emoji icons with clean, consistent SVG icon set
- Fix focus stealing issue by removing auto-focus from popover
- Move keyboard navigation handling to ChatInput component
- Prevent popover from stealing focus while typing
- Update MentionPopover to receive filtered files from parent
- Improve keyboard navigation (arrow keys, enter, escape)
- Maintain chat input focus while popover is open
- Add proper TypeScript types for all components
- Remove unused React import to fix TypeScript warnings
- Increase directory scanning depth from 3 to 5 levels for better file discovery
- Add 'icons', 'src', 'components' to priority directories for scanning
- Reduce directory skip list at deeper levels to find more nested files
- Increase item limits per directory (50/40/30 based on depth)
- Enhance fuzzy matching algorithm with better scoring:
  - Higher bonus for consecutive matches (3x vs 2x)
  - Bonus for path separators and word boundaries (10 points)
  - Bonus for filename start matches (15 points)
  - Bonus for exact substring matches (20 points)
  - Bonus for filename-specific matches (25 points)
  - Reduced penalty for longer paths (0.05x vs 0.1x)
- Search against file name, relative path, AND full path
- Prefer files over directories in results when scores are similar
- Increase displayed results from 5 to 8 files
- Increase total results from 10 to 20 files
- Add more file extensions including design files (ai, eps, sketch, fig, xd, psd)
- Include files without extensions (README, LICENSE, etc.)

Now searching '@iCons' will find files inside icon directories!
- Expand FileIcon component with specific icons for different file categories:
  📸 Image files: PNG, JPG, GIF, SVG, WebP, etc. - camera/image icon
  🎬 Video files: MP4, MOV, AVI, MKV, etc. - play button icon
  🎵 Audio files: MP3, WAV, FLAC, AAC, etc. - music note icon
  📦 Archive files: ZIP, TAR, GZ, RAR, 7Z, etc. - compressed folder icon
  📄 PDF files: Distinct PDF icon with document lines
  🎨 Design files: AI, EPS, Sketch, Figma, XD, PSD - design tool icon
  ⚡ JavaScript/TypeScript: JS, TS, JSX, TSX - code with checkmark
  🐍 Python files: PY, PYW, PYC - code with Python-specific elements
  🌐 HTML files: HTML, HTM, XHTML - structured markup icon
  🎨 CSS files: CSS, SCSS, SASS, LESS - styling icon
  📋 Data files: JSON, XML, YAML, CSV - structured data icon
  📝 Markdown files: MD, MDX - document with formatting
  🗄️ Database files: SQL, DB, SQLite - database cylinder icon
  ⚙️ Config files: ENV, INI, CFG, Dockerfile, etc. - settings gear icon
  📄 Text files: TXT, README, LICENSE, etc. - basic document icon
  🚀 Executable files: EXE, APP, DMG, etc. - executable with checkmark
  💻 Script files: SH, BAT, PS1, RB, PHP, etc. - terminal prompt icon
  📁 Directories: Folder icon
  📄 Default: Generic file icon for unknown types

- Support filename-based detection for files without extensions
- Include comprehensive file extension coverage
- Maintain consistent visual design with stroke-based SVG icons
- Improve user experience by making file types instantly recognizable
- Use extension-based detection first for files
- Check known file extensions before trying directory listing
- Add explicit handling for files without extensions (README, LICENSE, etc.)
- Add debug logging to track file detection
- More reliable file type identification
- Remove debug console.log statements
- Remove debug UI elements from file names
- Clean up file detection logic
- Finalize comprehensive file type icon system
- Ready for testing with proper file type detection
- Fix keyboard navigation by properly handling filtered files state
- Add callback to communicate filtered files from MentionPopover to ChatInput
- Ensure arrow keys work correctly when mention popover is open
- Improve bounds checking for navigation (prevent going out of range)
- Reset selected index when query changes
- Maintain proper state synchronization between components

Now arrow keys ↑↓ properly navigate the file list when searching with @
- Remove problematic callback approach that caused infinite re-renders
- Use forwardRef and useImperativeHandle to expose methods to parent
- Fix Maximum update depth exceeded error
- Restore proper arrow key navigation functionality
- Remove duplicate displayFiles definition
- Clean up component communication without state loops

The app should no longer be stuck in loading state and arrow keys should work!
- Remove font-medium class from file names in mention popover
- Use regular font weight for better visual hierarchy
- File names now appear less prominent, making the overall design cleaner
- Add text-textStandard class to file names for proper dark mode support
- Ensure file names use theme-aware colors instead of default black
- File names now properly adapt to light/dark mode themes
- Other text elements already use appropriate theme-aware classes
- Remove FuzzyFileSearch component and its usage
- Remove Cmd+P keyboard shortcut handler
- Remove isFuzzySearchOpen state and related functions
- Update placeholder text to remove ⌘P reference
- Focus entirely on @ mention functionality for file search

The @ mention system provides a more intuitive and integrated
file search experience directly in the chat input.
- Export FileItemWithMatch interface for proper typing
- Fix circular dependency issues with useCallback hooks
- Resolve TypeScript errors and ESLint warnings
- Add proper type annotations and dependency management
- Change #[warn(dead_code)] to #[allow(dead_code)] for test-only struct
- This resolves CI failure where clippy treats warnings as errors
@spencrmartin spencrmartin marked this pull request as ready for review July 3, 2025 15:14
@lily-de
Copy link
Contributor

lily-de commented Jul 3, 2025

🔥

@DOsinga
Copy link
Collaborator

DOsinga commented Jul 3, 2025

nice!

@DOsinga DOsinga merged commit 63774ef into main Jul 3, 2025
8 checks passed
@DOsinga DOsinga deleted the fuzzy-search branch July 3, 2025 21:09
@towry
Copy link

towry commented Jul 4, 2025

截屏2025-07-04 09 01 32 截屏2025-07-04 09 01 41

Doesn't respect the current working dir, and seems too fuzzy.

lifeizhou-ap added a commit that referenced this pull request Jul 4, 2025
* main:
  feat(goose-cli): theme persistence & selection (#1693)
  chore(release): release version 1.0.32 (#3248)
  Add fuzzy file search functionality (#3240)
  update styling of user messages (#3247)
  Add support for escape key to dismiss settings menu (#3225)
  fix: Refactor string truncation logic into reusable utility function to avoid panic (#2818) (#2819)
  fix: Prevent modal from closing on text select. (#3127)
  fix: Add back lazy_static (#3243)
  chore: remove unused dependencies (#3049)
  feat: Add close button (X) to toast notifications (#3197)
  Adds json schema validation to goose recipe validate cli (#3234)
Copy link
Collaborator

@DOsinga DOsinga left a comment

Choose a reason for hiding this comment

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

Glad we could get this in since the functionality is great, but I looked the code over again and spotted some opportunities for clean up

use std::env;

#[warn(dead_code)]
#[allow(dead_code)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

why this?

"file_type": "demonstration"
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is this file?

File modification operations include:
- String replacement
- Line insertion
- Content appending
Copy link
Collaborator

Choose a reason for hiding this comment

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

and this one?

}
});

console.log('Floating button script loaded'); No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

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

are we using this script?

className?: string;
}

export const FileIcon: React.FC<FileIconProps> = ({ fileName, isDirectory, className = "w-4 h-4" }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

simplify this function by having a list structure that has the filetypes and the inner elements of the svg


if (lastAtIndex === -1) {
// No @ found, close mention popover
setMentionPopover(prev => ({ ...prev, isOpen: false }));
Copy link
Collaborator

Choose a reason for hiding this comment

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

we could extract this thing into a named function for clarity and code dedupe:

const closeMentionPopover = () => {
setMentionPopover(prev => ({ ...prev, isOpen: false }));
};

and then just return that here and below

query: afterAt,
mentionStart: lastAtIndex,
selectedIndex: 0, // Reset selection when query changes
// filteredFiles will be populated by the MentionPopover component
Copy link
Collaborator

Choose a reason for hiding this comment

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

most of these comments don't add much I feel

}
}

// Handle history navigation first
Copy link
Collaborator

Choose a reason for hiding this comment

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

this comment doesn't seem true anymore

const handleKeyDown = (evt: React.KeyboardEvent<HTMLTextAreaElement>) => {
// If mention popover is open, handle arrow keys and enter
if (mentionPopover.isOpen && mentionPopoverRef.current) {
if (evt.key === 'ArrowDown') {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this could be simplified by using a case statement and extracting an updateSelectedIndex function

textAreaRef.current?.focus();

// Set cursor position after the inserted file path
setTimeout(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

consider using requestAnimationFrame(() => {

atarantino pushed a commit to atarantino/goose that referenced this pull request Jul 14, 2025
Co-authored-by: spencrmartin <[email protected]>
Signed-off-by: Adam Tarantino <[email protected]>
s-soroosh pushed a commit to s-soroosh/goose that referenced this pull request Jul 18, 2025
Co-authored-by: spencrmartin <[email protected]>
Signed-off-by: Soroosh <[email protected]>
kwsantiago pushed a commit to kwsantiago/goose that referenced this pull request Jul 19, 2025
Co-authored-by: spencrmartin <[email protected]>
Signed-off-by: Kyle Santiago <[email protected]>
cbruyndoncx pushed a commit to cbruyndoncx/goose that referenced this pull request Jul 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants