File explorer service layer with VSCode-like architecture for virtual file systems
npm install @node-tree/explorerA VSCode-inspired file explorer service layer that provides comprehensive file operations, navigation, and state management for tree-based file explorers. Built with TypeScript and designed for high-performance applications with large directory structures.
This package implements the service layer of a file explorer following VSCode's architecture patterns. It orchestrates between:
- @firesystem/core: Virtual file system abstraction
- @node-tree/viewport: High-performance tree virtualization
- @node-tree/react: React hooks for UI integration
> ๐ง Note: This is a service-only package - it contains no UI components. Use @node-tree/react for React integration.
- โ
Create files/directories with smart naming
- โ
Delete with confirmation and bulk support
- โ
Rename with validation and conflict resolution
- โ
Move/Copy with overwrite handling
- โ
Duplicate with intelligent naming (file-copy.ext)
- โ
Bounded operation history (configurable stack size)
- โ
Complete operation reversal (files, directories, content)
- โ
Branch handling - new operations clear future history
- โ
Metadata tracking with affected paths
- โ
Arrow key navigation (Up/Down, Left/Right)
- โ
Page navigation (PageUp/PageDown)
- โ
Jump navigation (Home/End, Parent/Child)
- โ
Smart expansion (Right arrow expands directories)
- โ
OPTIMIZED: High-performance End navigation (O(1) complexity)
- โ
Single-click selection (VSCode behavior)
- โ
Multi-select with Ctrl+Click (maintains previous selection)
- โ
Range selection with Shift+Click
- โ
Select all/clear/invert operations
- โ
Type-specific selection (files only, directories only)
- โ
NEW: selectSingle() method for exclusive selection
- โ
Cut/Copy/Paste with proper semantics
- โ
Multi-paste support (copy once, paste multiple times)
- โ
Auto-clear on cut operations
- โ
Cross-operation clipboard persistence
- โ
NEW: Memory-safe clipboard with 10,000 item limit
- โ
NEW: Auto-expiring clipboard (30-minute TTL)
- โ
Typed event system for all state changes
- โ
Progress tracking for long operations
- โ
User confirmation hooks (delete, overwrite)
- โ
Real-time UI synchronization
- โ
ENHANCED: Comprehensive error events with detailed context
- โ
NEW: Operation success events for better UX feedback
``bash`
npm install @node-tree/explorer @firesystem/core @node-tree/viewportor
yarn add @node-tree/explorer @firesystem/core @node-tree/viewportor
pnpm add @node-tree/explorer @firesystem/core @node-tree/viewport
``
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ @node-tree/react โ
โ (React Hooks & UI) โ
โโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ @node-tree/explorer โ
โ (Service Layer & Logic) โ
โ โโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโ โ
โ โ ExplorerService โ NavigationSvc โ SelectionSvc โ โ
โ โ FileOpService โ ClipboardSvc โ HistoryManager โ โ
โ โโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ @firesystem/core โ @node-tree/viewport โ
โ (Virtual FS) โ (Tree Virtualization) โ
โโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Single Click on File โ Selects file exclusively (clears previous selection)
- Single Click on Directory โ Selects directory and toggles expansion
- Double Click on File โ Opens file in permanent mode
- Double Click on Directory โ No action (VSCode ignores double-click on directories)
- Ctrl+Click โ Toggles selection of clicked item (maintains previous selection)
- Shift+Click โ Selects range from cursor to clicked item
- โ
Fixed: Single click now properly selects the clicked item
- โ
Fixed: Ctrl+Click maintains previous selection instead of clearing it
- โ
Added: selectSingle() method for exclusive selection (VSCode behavior)
- โ
Fixed: Double-click on directories is now properly ignored
Central service that coordinates all explorer functionality:
`typescript
import { ExplorerService } from "@node-tree/explorer";
import { MemoryFileSystem } from "@firesystem/memory";
import { TreeViewport } from "@node-tree/viewport";
const fs = new MemoryFileSystem();
const viewport = new TreeViewport(provider, { itemHeight: 24 });
const explorer = new ExplorerService(fs, viewport, {
confirmDelete: true,
confirmOverwrite: true,
maxHistorySize: 50,
});
`
Handles all file system operations with undo/redo support:
`typescript`
// All operations return promises and emit events
await explorer.createFile("/src", "component.tsx");
await explorer.createDirectory("/src", "components");
await explorer.rename("/old-name.txt", "new-name.txt");
await explorer.delete(["/file1.txt", "/file2.txt"]);
await explorer.move(["/source.txt"], "/target-folder");
await explorer.copy(["/template.txt"], "/new-folder");
await explorer.duplicate(["/important.txt"]); // Creates important-copy.txt
VSCode-like keyboard navigation:
`typescript`
// Navigate tree structure
await explorer.navigateUp(); // Move cursor up
await explorer.navigateDown(); // Move cursor down
await explorer.navigateLeft(); // Collapse or go to parent
await explorer.navigateRight(); // Expand or enter directory
await explorer.navigateToFirst(); // Jump to first item
await explorer.navigateToLast(); // Jump to last item
Advanced selection management:
`typescript`
// Selection operations
await explorer.selectAll(); // Select all visible items
explorer.clearSelection(); // Clear all selections
explorer.selectSingle("/file.txt"); // Select only this item (clear others)
explorer.toggleSelection("/file.txt"); // Toggle single item (keep others)
await explorer.selectRange("/start", "/end"); // Select range
await explorer.invertSelection(); // Invert current selection
Full clipboard operations with memory management:
`typescript
// Clipboard operations
explorer.cut(["/file1.txt", "/file2.txt"]); // Cut files
explorer.copy(["/template.tsx"]); // Copy files
await explorer.paste("/destination"); // Paste to destination
// Check clipboard state
const clipboardState = explorer.getClipboardState();
console.log(clipboardState.hasItems); // boolean
console.log(clipboardState.operation); // 'cut' | 'copy'
// Memory-safe operations
try {
explorer.copy(largeFileList); // Automatically limited to 10,000 items
} catch (error) {
// Handle excessive clipboard usage
}
// Auto-expiring clipboard (30-minute TTL)
explorer.copy(["/file1.txt"]);
// ... 31 minutes later ...
console.log(explorer.canPaste()); // false (auto-cleared)
`
Complete operation history:
`typescript
// History operations
await explorer.undo(); // Undo last operation
await explorer.redo(); // Redo next operation
// Check history state
const historyState = explorer.getHistoryState();
console.log(historyState.canUndo); // boolean
console.log(historyState.canRedo); // boolean
console.log(historyState.operations); // IOperation[]
`
The explorer provides a VSCode-like command system with categories, keybindings, and context awareness:
`typescript
// Execute commands by ID
await explorer.executeCommand("explorer.newFile");
await explorer.executeCommand("explorer.delete");
await explorer.executeCommand("explorer.selectAll");
// Get all available commands
const commands = explorer.getCommands();
commands.forEach((cmd) => {
console.log(${cmd.metadata.id}: ${cmd.metadata.label}); Category: ${cmd.metadata.category}
console.log(); Keybinding: ${cmd.metadata.keybinding}
console.log();
});
// Register custom commands
explorer.registerCommand({
metadata: {
id: "custom.myCommand",
label: "My Custom Command",
category: "Custom",
keybinding: "Ctrl+Alt+M",
},
handler: async (context) => {
// Custom logic here
},
enabled: (context) => context.explorer.hasSelection(),
});
`
#### File Operations
- explorer.newFile - Create new fileexplorer.newFolder
- - Create new directoryexplorer.delete
- - Delete selected itemsexplorer.rename
- - Rename current itemexplorer.refresh
- - Refresh tree viewexplorer.reveal
- - Reveal file in tree
#### Navigation
- explorer.navigateUp/Down - Arrow navigationexplorer.navigateLeft/Right
- - Horizontal navigationexplorer.navigateToFirst/Last
- - Jump navigationexplorer.pageUp/Down
- - Page navigation
#### Edit Operations
- explorer.cut - Cut selected itemsexplorer.copy
- - Copy selected itemsexplorer.paste
- - Paste clipboard itemsexplorer.duplicate
- - Duplicate selected itemsexplorer.undo/redo
- - History operations
#### Selection
- explorer.selectAll - Select all visibleexplorer.clearSelection
- - Clear selectionexplorer.toggleSelection
- - Toggle current itemexplorer.invertSelection
- - Invert selection
The explorer emits typed events for all state changes, enabling reactive UI updates:
`typescriptStarting ${event.type} on:
// Listen to all explorer events
explorer.on("operation:start", (event) => {
console.log(, event.paths);
});
explorer.on("operation:complete", (event) => {
console.log(Completed ${event.type} on:, event.paths);
});
explorer.on("operation:error", (event) => {
console.error(Error in ${event.type}:, event.error);
});
// Progress tracking for long operations
explorer.on("progress:start", (event) => {
console.log(Starting operation (${event.total} items));
});
explorer.on("progress:update", (event) => {
const percent = (event.current / event.total) * 100;
console.log(Progress: ${percent.toFixed(1)}%);
});
// State change notifications
explorer.on("clipboard:change", (state) => {
updateClipboardUI(state);
});
explorer.on("history:change", (state) => {
updateUndoRedoButtons(state);
});
// User confirmation hooks
explorer.on("confirm:delete", (event) => {
const result = window.confirm(Delete ${event.paths.length} items?);
event.confirm(result);
});
explorer.on("confirm:overwrite", (event) => {
const result = window.confirm(Overwrite ${event.path}?);`
event.confirm(result);
});
`typescript
interface IExplorerEvents {
// Operation lifecycle
"operation:start": { type: OperationType; paths: string[] };
"operation:complete": { type: OperationType; paths: string[] };
"operation:error": { type: OperationType; error: Error };
// Progress tracking
"progress:start": { total: number };
"progress:update": { current: number; total: number };
"progress:complete": void;
// State changes
"clipboard:change": IClipboardState;
"history:change": { canUndo: boolean; canRedo: boolean };
// User confirmations
"confirm:delete": { paths: string[]; confirm: (result: boolean) => void };
"confirm:overwrite": { path: string; confirm: (result: boolean) => void };
}
`
`typescript
// High-performance End navigation - O(1) complexity
await explorer.navigateToLast(); // Uses viewport.scrollToIndex() for efficiency
// Smart fallback for large trees
// If scrollToIndex fails, gracefully falls back to visible window
`
`typescript
// Automatic limits prevent memory issues
const MAX_CLIPBOARD_ITEMS = 10000; // Built-in limit
const CLIPBOARD_TTL_MS = 30 60 1000; // 30-minute auto-expire
// Error handling for large selections
explorer.on("operation:error", (event) => {
if (event.error.message.includes("Maximum allowed")) {
showToast("Selection too large. Maximum 10,000 items allowed.");
}
});
`
`typescriptOperation: ${event.type}
// Enhanced error events with detailed context
explorer.on("operation:error", (event) => {
console.log();Error: ${event.error.message}
console.log();
// Handle specific error types
});
// Success events for better UX
explorer.on("operation:complete", (event) => {
console.log(${event.type} completed on ${event.paths.length} items);`
});
`typescript
// 1. Use bulk operations instead of loops
await explorer.delete(["/file1", "/file2", "/file3"]); // โ
Good
// NOT: for (const file of files) await explorer.delete([file]); // โ Bad
// 2. Configure appropriate history size
const explorer = new ExplorerService(fs, viewport, {
maxHistorySize: 100, // Adjust based on memory constraints
});
// 3. Listen to progress events for long operations
explorer.on("progress:update", (event) => {
const percent = (event.current / event.total) * 100;
updateProgressBar(percent);
});
`
`typescript
interface IExplorerConfig {
// User confirmations
confirmDelete?: boolean; // Default: false
confirmOverwrite?: boolean; // Default: false
// History settings
maxHistorySize?: number; // Default: 50
// File operation settings
autoExpandOnCreate?: boolean; // Default: true
autoSelectOnCreate?: boolean; // Default: true
// Navigation settings
pageNavigationSize?: number; // Default: 10
wrapNavigation?: boolean; // Default: false
}
`
For React applications, use @node-tree/react which provides hooks that integrate seamlessly with this service layer:
`typescript
import { useExplorerService, useKeyboardShortcuts, useExplorerEvents } from '@node-tree/react';
function FileExplorer() {
// Create explorer service
const explorer = useExplorerService(fs, viewport, config);
// Setup keyboard shortcuts
const shortcuts = useKeyboardShortcuts(explorer);
// Listen to events
useExplorerEvents(explorer, {
'operation:complete': (event) => {
toast.success(${event.type} completed successfully);Error: ${event.error.message}
},
'operation:error': (event) => {
toast.error();
}
});
return
}
`
The package includes comprehensive tests with 90.34% coverage and 387 test cases:
`bash`
npm test # Run all tests
npm run test:coverage # Run with coverage report
npm run test:watch # Watch mode for development
- Unit Tests: Individual service testing with mocks
- Integration Tests: Full FileSystem + Viewport + Explorer integration
- Performance Tests: Memory limits, TTL expiration, large datasets
- Error Handling: Comprehensive error scenarios and edge cases
- โ
Clipboard TTL and auto-expiration
- โ
Memory limits and large selection handling
- โ
Optimized navigation performance
- โ
Enhanced error event handling
- โ
Success event emissions
- Node.js: >=16.0.0
- TypeScript: >=4.8.0
- @firesystem/core: ^1.0.0
- @node-tree/viewport: workspace:\*
MIT ยฉ Anderson D. Rosa
This package follows VSCode architecture patterns and maintains high code quality standards:
- ๐ง TypeScript: Fully typed with strict mode
- ๐งช Testing: Comprehensive test suite with high coverage
- ๐ Linting: ESLint + Prettier configuration
- ๐๏ธ Architecture: Service-oriented with clear separation of concerns
- ๐ก Events: Reactive architecture with typed events
- โป๏ธ Operations: Full undo/redo support for all operations
- โ
Added selectSingle()` method for exclusive selection
- โ
Fixed single-click selection behavior to match VSCode
- โ
Fixed Ctrl+Click to maintain previous selection
- โ
Fixed double-click on directories (now properly ignored)
- โ
Improved VSCode behavior compatibility
- Core explorer service implementation
- File operations with undo/redo
- Navigation and selection services
- Clipboard operations
- Event system
- Command registry
---
Built for high-performance applications that need VSCode-level file explorer functionality with complete TypeScript support and reactive architecture.