npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@node-tree/explorer

v0.0.2

Published

File explorer service layer with VSCode-like architecture for virtual file systems

Readme

@node-tree/explorer

A 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.

🎯 Purpose & Philosophy

This package implements the service layer of a file explorer following VSCode's architecture patterns. It orchestrates between:

🔧 Note: This is a service-only package - it contains no UI components. Use @node-tree/react for React integration.

🚀 Features

📁 Complete File Operations

  • ✅ 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)

🔄 Full Undo/Redo System

  • ✅ Bounded operation history (configurable stack size)
  • ✅ Complete operation reversal (files, directories, content)
  • ✅ Branch handling - new operations clear future history
  • ✅ Metadata tracking with affected paths

⌨️ VSCode-like Navigation

  • ✅ 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)

🎯 Advanced Selection Management

  • ✅ 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

📋 Clipboard Operations

  • ✅ 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)

📡 Event-Driven Reactivity

  • ✅ 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

📦 Installation

npm install @node-tree/explorer @firesystem/core @node-tree/viewport
# or
yarn add @node-tree/explorer @firesystem/core @node-tree/viewport
# or
pnpm add @node-tree/explorer @firesystem/core @node-tree/viewport

🏗️ Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                    @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)         │
└─────────────────────┴───────────────────────────────────┘

🎮 VSCode Behavior Compatibility

Mouse Interactions

  • 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

Selection Behavior Updates (v0.0.2)

  • 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

🛠️ Core Services

ExplorerService - Main Orchestrator

Central service that coordinates all explorer functionality:

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,
});

FileOperationService - File Operations Engine

Handles all file system operations with undo/redo support:

// 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

NavigationService - Tree Navigation

VSCode-like keyboard navigation:

// 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

SelectionService - Multi-Selection

Advanced selection management:

// 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

ClipboardService - Cut/Copy/Paste

Full clipboard operations with memory management:

// 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)

HistoryManager - Undo/Redo System

Complete operation history:

// 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[]

🎮 Command System

The explorer provides a VSCode-like command system with categories, keybindings, and context awareness:

// 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}`);
  console.log(`  Category: ${cmd.metadata.category}`);
  console.log(`  Keybinding: ${cmd.metadata.keybinding}`);
});

// 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(),
});

Built-in Commands

File Operations

  • explorer.newFile - Create new file
  • explorer.newFolder - Create new directory
  • explorer.delete - Delete selected items
  • explorer.rename - Rename current item
  • explorer.refresh - Refresh tree view
  • explorer.reveal - Reveal file in tree

Navigation

  • explorer.navigateUp/Down - Arrow navigation
  • explorer.navigateLeft/Right - Horizontal navigation
  • explorer.navigateToFirst/Last - Jump navigation
  • explorer.pageUp/Down - Page navigation

Edit Operations

  • explorer.cut - Cut selected items
  • explorer.copy - Copy selected items
  • explorer.paste - Paste clipboard items
  • explorer.duplicate - Duplicate selected items
  • explorer.undo/redo - History operations

Selection

  • explorer.selectAll - Select all visible
  • explorer.clearSelection - Clear selection
  • explorer.toggleSelection - Toggle current item
  • explorer.invertSelection - Invert selection

📡 Event System

The explorer emits typed events for all state changes, enabling reactive UI updates:

// Listen to all explorer events
explorer.on("operation:start", (event) => {
  console.log(`Starting ${event.type} on:`, 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);
});

Event Types

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 };
}

Performance & Memory Management

Optimized Navigation

// 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

Memory-Safe Clipboard

// 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.");
  }
});

Event-Driven Error Handling

// Enhanced error events with detailed context
explorer.on("operation:error", (event) => {
  console.log(`Operation: ${event.type}`);
  console.log(`Error: ${event.error.message}`);
  // Handle specific error types
});

// Success events for better UX
explorer.on("operation:complete", (event) => {
  console.log(`${event.type} completed on ${event.paths.length} items`);
});

Performance Best Practices

// 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);
});

⚙️ Configuration

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
}

🔗 Integration with React

For React applications, use @node-tree/react which provides hooks that integrate seamlessly with this service layer:

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`);
    },
    'operation:error': (event) => {
      toast.error(`Error: ${event.error.message}`);
    }
  });

  return <YourTreeComponent />;
}

🧪 Testing

The package includes comprehensive tests with 90.34% coverage and 387 test cases:

npm test                # Run all tests
npm run test:coverage   # Run with coverage report
npm run test:watch      # Watch mode for development

Test Categories

  • 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

New Test Coverage

  • ✅ Clipboard TTL and auto-expiration
  • ✅ Memory limits and large selection handling
  • ✅ Optimized navigation performance
  • ✅ Enhanced error event handling
  • ✅ Success event emissions

📋 Requirements

  • Node.js: >=16.0.0
  • TypeScript: >=4.8.0
  • @firesystem/core: ^1.0.0
  • @node-tree/viewport: workspace:*

📄 License

MIT © Anderson D. Rosa

🤝 Contributing

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

📝 Changelog

v0.0.2 (Latest)

  • ✅ 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

v0.0.1 (Initial Release)

  • 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.