Skip to content

feat(ui): add file explorer toolbar and file search modal#1408

Open
yashdev9274 wants to merge 3 commits intogeneralaction:mainfrom
yashdev9274:feat-yd-2-new
Open

feat(ui): add file explorer toolbar and file search modal#1408
yashdev9274 wants to merge 3 commits intogeneralaction:mainfrom
yashdev9274:feat-yd-2-new

Conversation

@yashdev9274
Copy link
Contributor

Summary

New PR for #1379 that contain feat of #1228 and pr of #1348

following changes:

  • Added FileExplorerToolbar component with Search, New File, New Folder, and Collapse buttons
  • Created FileSearchModal for dedicated file search functionality within projects
  • Added Shift+Cmd+P (Mac) / Ctrl+Shift+P (Windows) keyboard shortcut for file search
  • Integrated toolbar with existing FileTree component
  • Fixed folder collapse issue after create/delete operations by reloading expanded folders
  • Added folder selection on left-click for creating files/folders in selected directory

Fixes

Global Fixes : #1379
local fixe: #1228

Snapshot

Feat-1379.mp4

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (does not change functionality, e.g. code style improvements, linting)
  • This change requires a documentation update

Mandatory Tasks

  • I have self-reviewed the code
  • A decent size PR without self-review might be rejected

Checklist

  • I have read the contributing guide
  • My code follows the style guidelines of this project (pnpm run format)
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my PR needs changes to the documentation
  • I have checked if my changes generate no new warnings (pnpm run lint)
  • I have added tests that prove my fix is effective or that my feature works
  • I have checked if new and existing unit tests pass locally with my changes

… rmdir) to IPC and local/remote file systems
- Fixed Windows path separator bug in remote fs:rename, fs:mkdir, fs:rmdir handlers
- On Windows, path.join produces backslashes which break POSIX remote servers
- Minor cleanup: unused variable removal in LocalFileSystem
- UI: added padding to delete confirmation button in FileTree
- Added FileExplorerToolbar component with Search, New File, New Folder, and Collapse buttons
- Created FileSearchModal for dedicated file search functionality within projects
- Added Shift+Cmd+P (Mac) / Ctrl+Shift+P (Windows) keyboard shortcut for file search
- Integrated toolbar with existing FileTree component
- Fixed folder collapse issue after create/delete operations by reloading expanded folders
- Added folder selection on left-click for creating files/folders in selected directory
@vercel
Copy link

vercel bot commented Mar 11, 2026

@yashdev9274 is attempting to deploy a commit to the General Action Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR introduces a file explorer toolbar with Search, New File, New Folder, and Collapse All buttons, a dedicated FileSearchModal, and a full context-menu system (rename, delete, copy path, open in terminal/Finder) backed by three new IPC channels (fs:rename, fs:mkdir, fs:rmdir). It also adds a keyboard shortcut (Shift+Cmd/Ctrl+P) to open file search and includes null-safety hardening throughout the file tree.

Key issues found:

  • FileExplorerToolbar.tsx uses React.FC without importing React, which is a TypeScript compilation error.
  • FileSearchModal calls fsList without recursive: true, so the search only covers top-level files in the project root — files in subdirectories are invisible to search.
  • handleToolbarSearch in FileTree.tsx is dead code: the outer function is never called (the toolbar uses handleFileSearch), and the inner nested function it declares is also never invoked.
  • loadAllFiles (a useCallback) is missing expandedPaths and loadChildren from its dependency array; the reloadExpandedFolders inner closure captures stale values, meaning folder-reload after rename/delete may silently miss recently-expanded directories.
  • searchQuery and selectedIndex in FileSearchModal are not reset when the modal is reopened, leaving stale query text and cursor position visible to the user.

Confidence Score: 2/5

  • This PR has a compilation-blocking error and a functional regression that makes file search unusable for nested projects; not safe to merge as-is.
  • The missing React import in FileExplorerToolbar.tsx is a hard TypeScript compilation error. The non-recursive fsList in FileSearchModal makes the headline feature (project-wide file search) non-functional for any project with subdirectories. The stale-closure bug in loadAllFiles can silently break folder reload after file operations. These need to be fixed before merging.
  • FileExplorerToolbar.tsx (compilation error), FileSearchModal.tsx (broken search scope), and FileTree.tsx (dead code + stale closure in loadAllFiles).

Important Files Changed

Filename Overview
src/renderer/components/FileExplorer/FileExplorerToolbar.tsx New toolbar component with Search, New File, New Folder, and Collapse buttons — missing React import (compilation error) and has an unused ChevronDown import.
src/renderer/components/FileExplorer/FileSearchModal.tsx New file search modal — only indexes top-level files (missing recursive: true in fsList call) and does not reset search state between modal openings.
src/renderer/components/FileExplorer/FileTree.tsx Major additions: context menu with rename/delete/new file/new folder, toolbar integration, and file search wiring — contains dead code in handleToolbarSearch and a stale closure bug in loadAllFiles where expandedPaths and loadChildren are missing from the useCallback dependency array.
src/main/services/fsIpc.ts Adds IPC handlers for fs:rename, fs:mkdir, and fs:rmdir — includes proper path traversal guards (startsWith(normRoot)) and delegates correctly to local/remote filesystem implementations.
src/main/services/fs/LocalFileSystem.ts Adds rename (with source/destination existence checks) and mkdir (recursive) methods — straightforward and well-guarded implementations.
src/renderer/hooks/useKeyboardShortcuts.ts Adds FILE_SEARCH shortcut (Shift+Cmd/Ctrl+P) that dispatches emdash:openFileSearch custom event to open the search modal.

Sequence Diagram

sequenceDiagram
    participant User
    participant KB as useKeyboardShortcuts
    participant FT as FileTree
    participant FSM as FileSearchModal
    participant IPC as Electron IPC
    participant FS as LocalFileSystem / RemoteFileSystem

    Note over User, FS: File Search Flow
    User->>KB: Shift+Cmd/Ctrl+P
    KB->>FT: dispatchEvent("emdash:openFileSearch")
    FT->>FSM: isOpen=true
    FSM->>IPC: fsList(rootPath, {includeDirs:true})
    IPC->>FS: list()
    FS-->>IPC: items[]
    IPC-->>FSM: {success, items}
    User->>FSM: types query / arrows
    FSM-->>User: filtered results
    User->>FSM: Enter / click
    FSM->>FT: onSelectFile(path) + onClose()
    FT->>FT: onOpenFile(path)

    Note over User, FS: Context Menu File Operations
    User->>FT: right-click node
    FT-->>User: ContextMenu (Rename/Delete/New File/New Folder)
    User->>FT: select action → dialog input
    FT->>IPC: fsRename / fsRemove / fsMkdir / fsRmdir / fsWriteFile
    IPC->>FS: rename() / remove() / mkdir()
    FS-->>IPC: {success}
    IPC-->>FT: {success}
    FT->>FT: loadAllFiles() → reloadExpandedFolders()
Loading

Last reviewed commit: 543578b

onCollapse: () => void;
}

export const FileExplorerToolbar: React.FC<FileExplorerToolbarProps> = ({
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing React import

React.FC is referenced on this line but React is never imported. Unlike JSX usage, type-level references to React.FC require React to be explicitly in scope. This will cause a TypeScript compilation error.

Suggested change
export const FileExplorerToolbar: React.FC<FileExplorerToolbarProps> = ({
import React from 'react';
export const FileExplorerToolbar: React.FC<FileExplorerToolbarProps> = ({

Or add the import at the top of the file alongside the existing imports:

Suggested change
export const FileExplorerToolbar: React.FC<FileExplorerToolbarProps> = ({
import React from 'react';
import { ChevronDown, CopyMinus, FilePlus, FolderPlus, Search } from 'lucide-react';
import { Button } from '../ui/button';

Comment on lines +27 to +34
const fetchFiles = async () => {
const opts: any = { includeDirs: true };
if (connectionId && remotePath) {
opts.connectionId = connectionId;
opts.remotePath = remotePath;
}

const result = await window.electronAPI.fsList(rootPath, opts);
Copy link
Contributor

Choose a reason for hiding this comment

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

File search only indexes top-level files

fsList is called without recursive: true, so it only returns files directly in the root directory. Nested files in subdirectories will never appear in search results, making this essentially a root-level file picker rather than a project-wide file search.

Suggested change
const fetchFiles = async () => {
const opts: any = { includeDirs: true };
if (connectionId && remotePath) {
opts.connectionId = connectionId;
opts.remotePath = remotePath;
}
const result = await window.electronAPI.fsList(rootPath, opts);
const opts: any = { includeDirs: false, recursive: true };

Setting includeDirs: false (or keeping it true if directories should also be searchable) combined with recursive: true ensures the full project tree is indexed.

Comment on lines +20 to +22
const [searchQuery, setSearchQuery] = useState('');
const [files, setFiles] = useState<Array<{ name: string; path: string; type: string }>>([]);
const [selectedIndex, setSelectedIndex] = useState(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

State not reset between modal openings

searchQuery and selectedIndex are component state that persist between opens/closes. If a user types a query, closes the modal, and reopens it, the stale search text and cursor position remain. The file list is re-fetched (via the isOpen effect) but the input and selection are not cleared.

Consider resetting these when isOpen becomes true:

useEffect(() => {
  if (isOpen) {
    setSearchQuery('');
    setSelectedIndex(0);
  }
}, [isOpen]);

Comment on lines +670 to +689
const handleToolbarSearch = () => {
// Focus the search input
const handleToolbarSearch = () => {
// Detect platform
const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);

// Create keyboard event for Shift+Cmd+P (Mac) or Ctrl+Shift+P (Windows)
const key = isMac ? 'p' : 'p';
const event = new KeyboardEvent('keydown', {
key: key,
metaKey: isMac,
ctrlKey: !isMac,
shiftKey: true,
bubbles: true,
});

// Dispatch the event to trigger CommandPalette
document.dispatchEvent(event);
};
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Dead code: handleToolbarSearch outer function is never called and contains an unreachable inner function

The outer handleToolbarSearch function declares an inner handleToolbarSearch function but never invokes it — the body is effectively a no-op. Additionally, the outer function itself is never used; the toolbar's onSearch prop is wired to handleFileSearch (line 914), not handleToolbarSearch.

Also, const key = isMac ? 'p' : 'p' inside the inner function is always 'p' regardless of platform — the ternary has no effect.

This entire block should be removed:

Comment on lines +529 to +554
const reloadExpandedFolders = async () => {
const expandedArray = Array.from(expandedPaths);

for (const path of expandedArray) {
// Find the node in the new file list
const item = validItems.find((f: any) => f.path === path);
if (item) {
// Construct a proper FileNode with required properties
const node: FileNode = {
id: item.path,
name: item.path.split('/').pop() || item.path,
path: item.path,
type: item.type === 'dir' ? 'directory' : item.type,
};
await loadChildren(node);
}
}
};
// Trigger reload of expanded folders
setTimeout(reloadExpandedFolders, 0);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load files');
} finally {
setLoading(false);
}
}, [rootPath, connectionId, remotePath]); // Reload when rootPath or remote info changes
Copy link
Contributor

Choose a reason for hiding this comment

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

loadAllFiles captures stale expandedPaths and loadChildren

The useCallback dependency array only includes [rootPath, connectionId, remotePath] (line 554), but the reloadExpandedFolders closure references both expandedPaths and loadChildren. Because these are omitted from the deps, loadAllFiles is never recreated when expandedPaths changes — meaning calls to loadAllFiles after rename/delete operations (lines 704, 724, 741, 758) will use whatever expandedPaths was captured at the last rootPath/remote-info change, silently skipping any folders the user has since expanded.

loadChildren should be added to the dependency array. For expandedPaths, since it's a Set stored as state, it can also be added; React's ESLint plugin (react-hooks/exhaustive-deps) would flag this as a violation.

  }, [rootPath, connectionId, remotePath, expandedPaths, loadChildren]);

Note: if this creates a circular dependency (since loadChildren is defined after loadAllFiles), consider extracting reloadExpandedFolders into its own useCallback that depends on expandedPaths and loadChildren, and call it separately after loadAllFiles resolves.

@arnestrickmann
Copy link
Contributor

CleanShot 2026-03-11 at 10 42 45@2x

Thank you!

Could we also use the same icons for each file in the search component that we are using in the left sidebar, where the user can see the files?

@yashdev9274
Copy link
Contributor Author

yashdev9274 commented Mar 11, 2026

CleanShot 2026-03-11 at 10 42 45@2x Thank you!

Could we also use the same icons for each file in the search component that we are using in the left sidebar, where the user can see the files?

you mean to use same icons for these files similar to one in file trees?
i'll do it after this pr merge, cause this editor feature still needs multiple update.

@arnestrickmann
Copy link
Contributor

Yes,
Please fix it before

@yashdev9274
Copy link
Contributor Author

Yes,
Please fix it before

this pr is ready to merge you can also review it if you want, there is no issue in this pr.

I'll add more explorer features after merging so that this pr doesn't get bulkier

@yashdev9274
Copy link
Contributor Author

hey @arnestrickmann any update on this PR

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.

Feature: File/folder creation in UI and enhanced code editor capabilities

2 participants