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

runspace-editor

v0.1.1

Published

Multi-file code editor component with workspace support, built on Monaco Editor and React

Readme

RunSpace Editor

A modern, feature-rich multi-file code editor component built with React, TypeScript, and Monaco Editor. Designed for building interactive coding environments with workspace support, read-only files/lines, terminal output, and customizable themes.

Version License React TypeScript

Features

Multi-File Workspace

  • Support for multiple files with tab-based navigation
  • Individual file configuration (language, read-only, highlighting)
  • File switching with preserved editor state
  • Optional file paths: Organize files in directories (e.g., src/components, assets/latest)

📁 File Explorer

  • Hierarchical directory tree view
  • Collapsible folders with visual tree lines
  • File selection indicators with checkbox-style UI
  • Auto-expands to reveal active file
  • Configurable via side panel settings

🔒 Read-Only Control

  • Entire file read-only mode
  • Granular line-level read-only control
  • Visual indicators for read-only content

🎨 Theme Support

  • Built-in themes: GitHub (light/dark), Solarized (light/dark), Tealwave (light/dark)
  • UI theme toggle (light/dark mode)
  • Auto-sync UI theme: Entire editor UI automatically adapts when editor theme changes
  • Theme detection utilities for custom integrations
  • Customizable editor themes

📟 Terminal Output

  • Real-time terminal output display
  • Multiple output types (stdout, stderr, info, error, success)
  • Auto-scroll and clear functionality

⚙️ Configurable Controls

  • Theme selector
  • Settings panel
  • Run button with custom actions
  • File tabs with close functionality

🎯 Advanced Editor Features

  • Syntax highlighting via TextMate grammars
  • Line selection events
  • Line highlighting
  • Auto-resize to content
  • Monaco Editor powered

🔧 Developer Friendly

  • TypeScript support with full type definitions
  • React hooks for state management
  • Extensible action system for code execution
  • Well-documented API

🏖️ Sandbox Mode

  • Secure code execution in isolated iframe
  • Message-based communication protocol
  • Real-time output streaming
  • Configurable sandbox URL
  • Automatic workspace integration

Installation

npm install runspace-editor
# or
yarn add runspace-editor
# or
pnpm add runspace-editor

Peer Dependencies

The package requires the following peer dependencies:

npm install react react-dom monaco-editor onigasm

Important: These dependencies must be installed by your application, not by the package. This ensures version compatibility and prevents duplication.

Quick Start

import React from 'react';
import { RunSpaceEditor } from 'runspace-editor';
import type { Workspace, RunSpaceActions, EditorTheme } from 'runspace-editor';
import onigasmWasm from 'onigasm/lib/onigasm.wasm?url'; // Vite specific
import 'runspace-editor/dist/index.css';

const workspace: Workspace = {
  files: [
    {
      id: 'main.js',
      path: 'main.js',
      language: 'javascript',
      content: `console.log('Hello, World!');`,
    },
  ],
  activeFileId: 'main.js',
};

const actions: RunSpaceActions = {
  runCode: async (files) => {
    // Your code execution logic
    // files is an array of FileOperation objects with path, content, and operation type
    console.log('Running:', files);
    return {
      status: 'completed',
      tests: [],
      summary: { total: 0, passed: 0, failed: 0, timeout: 0, error: 0 },
    };
  },
};

function App() {
  const [theme, setTheme] = React.useState<EditorTheme>('github-dark');

  const handleLineSelect = (fileId: string, lineNumber: number) => {
    console.log(`Line ${lineNumber} selected in ${fileId}`);
  };

  const handleThemeChange = (newTheme: string) => {
    console.log(`Theme changed to: ${newTheme}`);
    setTheme(newTheme as EditorTheme);
  };

  return (
    <RunSpaceEditor
      workspace={workspace}
      actions={actions}
      theme={theme}
      wasmPath={onigasmWasm}
      height="100vh"
      onLineSelect={handleLineSelect}
      onThemeChange={handleThemeChange}
      terminalShowTimestamp={true}
    />
  );
}

export default App;

Key Features Demonstrated

  1. Workspace Structure: Define files with id, path, and content
  2. Theme Control: Set theme via the theme prop and handle changes with onThemeChange
  3. Code Execution: Implement runCode action to handle code execution
  4. Callbacks: onLineSelect and onThemeChange provide integration points
  5. WASM Configuration: Required for TextMate syntax highlighting
  6. Terminal Timestamps: Control timestamp display in terminal output

Configuration

Workspace

interface Workspace {
  /** List of files in the workspace */
  files: FileConfig[];
  /** Initial active file ID */
  activeFileId?: string;
  /** Workspace configuration */
  config?: WorkspaceConfig;
}

WorkspaceConfig

interface WorkspaceConfig {
  /** Enable sandbox mode */
  sandbox?: boolean;
  /** URL of the sandbox iframe */
  sandboxURL?: string;
  /** Build pipeline steps */
  build?: PipelineStep[];
  /** Run pipeline steps */
  run?: PipelineStep[];
  /** Entry point files */
  entryPoints?: string[];
  /** Artifact paths */
  artifacts?: string[];
}

FileConfig

interface FileConfig {
  /** Unique identifier for the file */
  id: string;
  /** Display name shown in tabs */
  name: string;
  /** Programming language for syntax highlighting */
  language: string;
  /** Initial content of the file */
  content: string;
  /** Optional relative path from workspace root (e.g., 'src/components', 'assets/latest'). If not set, file is at root. */
  path?: string;
  /** Whether the entire file is read-only */
  readOnly?: boolean;
  /** Line numbers that should be read-only (1-indexed) */
  readOnlyLines?: number[];
  /** Line numbers to highlight (1-indexed) */
  highlightLines?: number[];
  /** Whether to enable line selection events */
  enableLineSelection?: boolean;
  /** Whether the file is hidden from the editor UI (not shown in file explorer or tabs, but included in workspace actions) */
  hidden?: boolean;
  /** Origin of the file - 'workspace' for files from initial workspace, 'new' for user-added files */
  origin?: 'workspace' | 'new';
}

ControlConfig

interface ControlConfig {
  /** Show theme selector dropdown */
  showThemeSelector?: boolean;
  /** Show settings button */
  showSettings?: boolean;
  /** Show run button */
  showRunButton?: boolean;
  /** Show file tabs */
  showFileTabs?: boolean;
  /** Custom label for run button */
  runButtonLabel?: string;
}

SidePanelConfig

interface SidePanelConfig {
  /** Whether the entire sidebar is visible (default: true) */
  visible?: boolean;
  /** Initial collapsed state (default: false) */
  collapsed?: boolean;
  /** Width in pixels when expanded */
  width?: number;
  /** Tab visibility configuration */
  tabs?: {
    /** Show file explorer tab (default: true) */
    explorer?: boolean;
    /** Show test results tab (default: true) */
    testResults?: boolean;
  };
}

Note: The sidebar is always positioned on the left side of the editor.

RightPanelConfig

interface RightPanelConfig {
  /** Whether the entire panel is visible (default: false) */
  visible?: boolean;
  /** Width in pixels (default: 400) */
  width?: number;
  /** Tab visibility configuration */
  tabs?: {
    /** Show sandbox tab (default: true if workspace.config.sandbox is true) */
    sandbox?: boolean;
    /** Show test results tab (default: true) */
    testResults?: boolean;
  };
}

Note: The right panel contains the Sandbox tab (when enabled) and Test Results tab. The Sandbox tab only appears when workspace.config.sandbox is set to true.

RunSpaceActions

interface RunSpaceActions {
  /** Execute code - receives array of all files with operation metadata, returns execution result */
  runCode?: (files: FileOperation[]) => Promise<ExecutionResult>;
  /** Save file - for persistence */
  saveFile?: (fileId: string, content: string) => Promise<void>;
  /** Load workspace - for initialization */
  loadWorkspace?: () => Promise<Workspace>;
}

ExecutionResult

interface ExecutionResult {
  status: 'completed' | 'failed' | 'timeout';
  build?: BuildResult;
  run?: RunResult;
  tests?: TestResult[];
  summary?: TestSummary;
  error?: string;
}

interface BuildResult {
  status: 'success' | 'failed';
  stdout: string;
  stderr: string;
  artifacts: string[];
  timeMs: number;
}

interface RunResult {
  status: 'success' | 'failed';
  stdout: string;
  stderr: string;
  exitCode: number;
  timeMs: number;
}

interface TestResult {
  id: string;
  name?: string;
  status: 'passed' | 'failed' | 'timeout' | 'error';
  stdout?: string;
  stderr?: string;
  exitCode?: number;
  expectations?: Record<string, any>;
  timedOut: boolean;
  timeMs: number;
  memoryKb?: number;
  validationReason?: string;
  validationDetails?: Record<string, any>;
  hidden?: boolean;
}

interface TestSummary {
  total: number;
  passed: number;
  failed: number;
  timeout: number;
  error: number;
}

FileOperation

interface FileOperation {
  /** File path */
  path: string;
  /** File content */
  content: string;
  /** Operation type - 'create', 'update', or 'delete'. Undefined for readonly/hidden files */
  operation?: 'create' | 'update' | 'delete';
}

Important Notes:

  • ALL files are passed to runCode, including readonly and hidden files
  • Files from the workspace that are not readOnly or hidden will have operation: 'update'
  • Files with origin: 'new' will have operation: 'create' (for future file creation feature)
  • readOnly and hidden files will have operation: undefined (no operation, but content is provided for context)

Examples

Read-Only Lines

const workspace: Workspace = {
  files: [
    {
      id: 'helper.js',
      name: 'helper.js',
      language: 'javascript',
      content: `function add(a, b) {
  return a + b;
}

function multiply(a, b) {
  return a * b;
}`,
      readOnlyLines: [1, 2, 3], // Lines 1-3 are read-only
    },
  ],
};

Entire File Read-Only

const workspace: Workspace = {
  files: [
    {
      id: 'config.json',
      name: 'config.json',
      language: 'json',
      content: `{"key": "value"}`,
      readOnly: true, // Entire file is read-only
    },
  ],
};

Line Highlighting

const workspace: Workspace = {
  files: [
    {
      id: 'example.js',
      name: 'example.js',
      language: 'javascript',
      content: `// Important line\nconsole.log('Hello');\n// Another line`,
      highlightLines: [2], // Highlight line 2
    },
  ],
};

Hidden Files

Hidden files are included in the workspace and available during code execution, but they are not displayed in the file explorer or file tabs. This is useful for configuration files, helper utilities, or dependencies that should exist in the workspace but don't need to be edited by users.

const workspace: Workspace = {
  files: [
    {
      id: 'app.js',
      name: 'app.js',
      language: 'javascript',
      content: `import { config } from './config.js';\nconsole.log('API:', config.apiUrl);`,
    },
    {
      id: 'config.js',
      name: 'config.js',
      language: 'javascript',
      content: `export const config = { apiUrl: 'https://api.example.com' };`,
      hidden: true, // This file won't appear in the UI but is available when running code
    },
  ],
  activeFileId: 'app.js',
};

Sandbox Mode

Sandbox mode enables secure code execution in an isolated iframe. When enabled, the Run button will send code to the sandbox instead of calling the runCode action. The sandbox communicates with the editor via the Message Passing protocol.

const workspace: Workspace = {
  files: [
    {
      id: 'main.js',
      path: 'main.js',
      language: 'javascript',
      content: 'console.log("Hello from sandbox!");',
    },
  ],
  config: {
    sandbox: true,
    sandboxURL: 'https://your-sandbox-domain.com/sandbox',
    entryPoints: ['main.js'],
  },
};

<RunSpaceEditor
  workspace={workspace}
  rightPanel={{
    visible: true,
    tabs: {
      sandbox: true, // Enable sandbox tab
    },
  }}
/>

How it works:

  1. Enable Sandbox: Set workspace.config.sandbox: true and provide a sandboxURL
  2. Sandbox Tab: A new "Sandbox" tab appears in the right panel
  3. Load Sandbox: Click "Load Sandbox" button to initialize the iframe
  4. Run Code: When you click Run, code is sent to the sandbox via LOAD_CODE message
  5. Output Streaming: stdout/stderr streams back to the terminal in real-time
  6. Completion: Sandbox sends CODE_LOADED when execution completes

Message Protocol (see .refs/workspace_host_guide.md for details):

  • READY_FOR_INIT ← Sandbox ready for initialization
  • INIT → Initialize sandbox
  • SETUP_STREAM ← MessagePort for streaming output
  • INITIALIZED ← Sandbox ready to accept code
  • LOAD_CODE → Send files to sandbox
  • CODE_LOADED ← Code execution complete

Security Features:

  • Origin Validation: Messages are validated against the sandboxURL origin to prevent unauthorized communication
  • Structured Messages: Only properly formatted messages with valid types are processed
  • Isolated Execution: Sandbox runs in an iframe with sandbox="allow-scripts allow-same-origin"
  • Secure Communication: Messages are sent with the correct target origin instead of wildcards
  • Error Handling: Comprehensive error states with user feedback and recovery options

Best Practices:

  • Always specify a sandboxURL from your own domain in production
  • Avoid using about:blank in production environments
  • The origin validation automatically extracts and validates the origin from sandboxURL
  • Messages from unauthorized origins are logged as warnings and rejected

Headless Sandbox (Console Applications):

For console-only applications where no sandbox UI is needed, you can enable sandbox execution while keeping the right panel hidden. The sandbox component stays mounted in the background to handle code execution:

const workspace: Workspace = {
  files: [
    {
      id: 'app.py',
      path: 'app.py',
      language: 'python',
      content: 'print("Hello from Python!")',
    },
  ],
  config: {
    sandbox: true,
    sandboxURL: 'https://your-sandbox-domain.com/sandbox',
    entryPoints: ['app.py'],
  },
};

<RunSpaceEditor
  workspace={workspace}
  rightPanel={{
    visible: false, // Panel hidden, but sandbox component is mounted
    tabs: {
      sandbox: true, // Sandbox enabled in config
    },
  }}
/>

In this mode:

  • Right panel remains hidden (visible: false)
  • Sandbox component is mounted and functional
  • Code execution works normally via the sandbox
  • All output appears in the terminal
  • No manual "Load Sandbox" step required (auto-initializes)

Custom Run Action

const actions: RunSpaceActions = {
  runCode: async (files) => {
    try {
      // files is an array of ALL files including readonly and hidden
      // Each has: { path: string, content: string, operation?: 'create' | 'update' | 'delete' }

      // Separate files by operation type
      const filesToUpdate = files.filter(f => f.operation === 'update');
      const filesToCreate = files.filter(f => f.operation === 'create');
      const contextFiles = files.filter(f => !f.operation); // readonly/hidden files

      console.log(`Updating ${filesToUpdate.length} files`);
      console.log(`Creating ${filesToCreate.length} files`);
      console.log(`Context files: ${contextFiles.length} (readonly/hidden)`);

      // Process operations
      for (const file of files) {
        if (file.operation === 'update') {
          await updateFile(file.path, file.content);
        } else if (file.operation === 'create') {
          await createFile(file.path, file.content);
        }
        // Files without operation are readonly/hidden - used for context only
      }

      // Execute code with all files available
      const result = await executeInSandbox(files);

      return {
        status: 'completed',
        run: {
          status: 'success',
          stdout: result.stdout,
          stderr: result.stderr || '',
          exitCode: 0,
          timeMs: result.timeMs,
        },
        tests: [],
        summary: { total: 0, passed: 0, failed: 0, timeout: 0, error: 0 },
      };
    } catch (error) {
      return {
        status: 'failed',
        tests: [],
        summary: { total: 0, passed: 0, failed: 0, timeout: 0, error: 0 },
        error: error.message,
      };
    }
  },
  saveFile: async (fileId, content) => {
    // Save to backend/localStorage
    await fetch(`/api/files/${fileId}`, {
      method: 'PUT',
      body: JSON.stringify({ content }),
    });
  },
};

Multiple Files with Different Languages

const workspace: Workspace = {
  files: [
    {
      id: 'script.js',
      name: 'script.js',
      language: 'javascript',
      content: 'console.log("JavaScript");',
    },
    {
      id: 'styles.css',
      name: 'styles.css',
      language: 'css',
      content: 'body { margin: 0; }',
    },
    {
      id: 'index.html',
      name: 'index.html',
      language: 'html',
      content: '<!DOCTYPE html><html></html>',
    },
  ],
  activeFileId: 'script.js',
};

File Explorer with Directory Structure

const workspace: Workspace = {
  files: [
    {
      id: 'main.js',
      name: 'main.js',
      path: 'src',
      language: 'javascript',
      content: `import { helper } from './utils/helper.js';\nconsole.log(helper());`,
    },
    {
      id: 'helper.js',
      name: 'helper.js',
      path: 'src/utils',
      language: 'javascript',
      content: `export function helper() {\n  return 'Hello from helper!';\n}`,
    },
    {
      id: 'data.json',
      name: 'data.json',
      path: 'assets/latest',
      language: 'json',
      content: `{"key": "value"}`,
    },
    {
      id: 'config.js',
      name: 'config.js',
      language: 'javascript',
      content: `export default { apiUrl: 'http://localhost:3000' };`,
    },
  ],
  activeFileId: 'main.js',
};

// Enable file explorer in side panel
const sidePanelConfig: SidePanelConfig = {
  visible: true,
  collapsed: false,
  width: 280,
  tabs: {
    explorer: true,  // Enable file explorer
    testResults: true, // Enable test results
  },
};

<RunSpaceEditor
  workspace={workspace}
  sidePanel={sidePanelConfig}
  // ... other props
/>

The explorer will display the directory tree with collapsible folders:

Workspace
├── [ ] config.js
├── [×] src/
│   ├── [×] main.js         (× = selected)
│   └── [ ] utils/
│       └── [ ] helper.js
└── [ ] assets/
    └── [ ] latest/
        └── [ ] data.json

Advanced Usage

Using the useWorkspace Hook

For advanced control, you can use the useWorkspace hook directly:

import { useWorkspace } from 'runspace-editor';

function MyCustomEditor() {
  const workspace = useWorkspace(config);

  // Access workspace state
  console.log(workspace.files);
  console.log(workspace.activeFileId);
  console.log(workspace.terminalOutput);

  // Perform actions
  workspace.setActiveFile('file2.js');
  workspace.updateFileContent('file1.js', 'new content');
  workspace.addTerminalOutput({ type: 'info', content: 'Message' });
  workspace.clearTerminal();

  // Use in your custom UI
  return <div>{/* Your custom implementation */}</div>;
}

Custom Component Composition

You can use individual components for more control:

import {
  FileEditor,
  Terminal,
  ControlPanel,
  SidePanel,
  useWorkspace,
} from 'runspace-editor';

function CustomEditor() {
  const workspace = useWorkspace(config);

  return (
    <div className="editor-layout">
      <ControlPanel {...workspace} />
      <div className="editor-content">
        {workspace.files.map((file) => (
          <FileEditor key={file.id} file={file} {...otherProps} />
        ))}
      </div>
      <Terminal output={workspace.terminalOutput} />
    </div>
  );
}

Supported Languages

The editor supports syntax highlighting for all languages supported by Monaco Editor and TextMate grammars, including:

  • JavaScript / TypeScript / JSX / TSX
  • Python
  • Java / C / C++ / C#
  • Go / Rust
  • HTML / CSS / SCSS
  • JSON / YAML / XML
  • Markdown
  • Shell / Bash
  • SQL
  • PHP / Ruby
  • And many more...

Themes

Built-in Editor Themes

  • github-light - GitHub light theme
  • github-dark - GitHub dark theme
  • solarized-light - Solarized light theme
  • solarized-dark - Solarized dark theme
  • tealwave-light - Tealwave light theme (custom)
  • tealwave-dark - Tealwave dark theme (custom)

UI Themes

  • light - Light UI mode
  • dark - Dark UI mode

Browser Support

  • Chrome/Edge: Latest 2 versions
  • Firefox: Latest 2 versions
  • Safari: Latest 2 versions

Development

Setup

# Install dependencies
npm install

# Run development server (examples)
npm run dev

# Run tests
npm test

# Build for production
npm run build

# Type checking
npm run type-check

Integration Example

A complete integration example is available in the integration-example/ directory. This demonstrates:

  • How to consume RunSpaceEditor as an external package
  • Proper peer dependency management
  • WASM file configuration
  • Callback handlers (onLineSelect, onThemeChange)
  • Code execution and auto-save
cd integration-example
npm install
npm run dev

Visit http://localhost:3901 to see the integration example in action.

Project Structure

runspace-editor/
├── src/
│   ├── components/       # React components
│   │   ├── RunSpaceEditor.tsx
│   │   ├── FileEditor.tsx
│   │   ├── Terminal.tsx
│   │   ├── ControlPanel.tsx
│   │   └── SidePanel.tsx
│   ├── hooks/            # Custom React hooks
│   │   ├── useWorkspace.ts
│   │   └── __tests__/
│   ├── types/            # TypeScript definitions
│   │   ├── workspace.ts
│   │   └── monaco-ext.d.ts
│   ├── utils/            # Utility functions
│   │   └── monaco-setup.ts
│   └── index.ts          # Main exports
├── examples/             # Example applications
│   ├── App.tsx
│   └── main.tsx
└── dist/                 # Build output

Testing

The project uses Jest and React Testing Library:

# Run all tests
npm test

# Watch mode
npm run test:watch

# Coverage report
npm run test:coverage

API Reference

Components

RunSpaceEditor

Main component that integrates all functionality.

Props:

  • workspace: Workspace - Workspace with code files (required)
  • actions?: RunSpaceActions - Injected actions for code execution, file saving, etc.
  • theme?: EditorTheme - Default theme for all editors (default: 'github-dark')
  • controls?: ControlConfig - Control panel configuration
  • editorLabel?: string - Custom label for the editor (displayed next to sandbox icon, default: 'RunSpace')
  • leftPanel?: LeftPanelConfig - Left panel configuration
  • rightPanel?: RightPanelConfig - Right panel configuration
  • editorOptions?: object - Common editor options applied to all files
    • minimap?: { enabled: boolean } - Minimap configuration
    • lineNumbers?: 'on' | 'off' | 'relative' - Line number display
    • wordWrap?: 'on' | 'off' | 'wordWrapColumn' | 'bounded' - Word wrap behavior
    • fontSize?: number - Font size in pixels
    • tabSize?: number - Tab size
  • wasmPath?: string - Path to onigasm WASM file (required for Vite)
  • className?: string - Custom CSS class for container
  • height?: string - Container height (default: '100vh')
  • onLineSelect?: (fileId: string, lineNumber: number) => void - Callback when a line is selected
  • onThemeChange?: (theme: string) => void - Callback when theme is changed
  • terminalShowTimestamp?: boolean - Whether to show timestamps in terminal output (default: true)

FileEditor

Individual file editor component.

Terminal

Terminal output display component.

ControlPanel

Top control bar with theme selector and controls.

SidePanel

Collapsible side panel with optional file explorer.

Props:

  • config: SidePanelConfig - Panel configuration including explorer settings
  • uiTheme: UITheme - UI theme
  • files?: FileConfig[] - Workspace files (for explorer)
  • activeFileId?: string - Active file ID (for explorer)
  • onFileSelect?: (fileId: string) => void - Callback when file is selected in explorer

FileExplorer

Displays workspace files in a hierarchical tree structure with collapsible folders.

Props:

  • files: FileConfig[] - List of files in workspace
  • activeFileId: string - Currently active file ID
  • uiTheme: UITheme - UI theme
  • onFileSelect: (fileId: string) => void - Callback when file is selected

Hooks

useWorkspace

Main hook for workspace state management.

Returns:

  • files: FileConfig[] - List of files
  • activeFileId: string - Currently active file ID
  • theme: EditorTheme - Current editor theme
  • uiTheme: UITheme - Current UI theme
  • terminalOutput: TerminalOutput[] - Terminal output lines
  • isTerminalVisible: boolean - Terminal visibility state
  • isRunning: boolean - Code execution state
  • editors: Map<string, EditorInstance> - Editor instances registry
  • setActiveFile: (fileId: string) => void - Switch active file
  • setTheme: (theme: EditorTheme) => void - Change editor theme
  • toggleUITheme: () => void - Toggle light/dark UI mode
  • updateFileContent: (fileId: string, content: string) => void - Update file content
  • addTerminalOutput: (output) => void - Add terminal output
  • clearTerminal: () => void - Clear terminal
  • setTerminalVisible: (visible: boolean) => void - Toggle terminal
  • setRunning: (running: boolean) => void - Set execution state
  • registerEditor: (editor: EditorInstance) => void - Register editor instance
  • unregisterEditor: (fileId: string) => void - Unregister editor
  • getActiveEditor: () => EditorInstance | undefined - Get active editor
  • getEditorContent: (fileId: string) => string | undefined - Get file content
  • closeFile: (fileId: string) => void - Close a file

Utilities

initializeMonaco

Initialize Monaco Editor with TextMate support.

async function initializeMonaco(wasmPath?: string): Promise<void>

Note: TextMate grammars are used for syntax highlighting. Monaco workers are not required.

isMonacoInitialized

Check if Monaco is initialized.

function isMonacoInitialized(): boolean

getSupportedLanguages

Get list of supported languages.

function getSupportedLanguages(): string[]

changeEditorTheme

Change the editor theme.

function changeEditorTheme(themeName: string): void

detectThemeMode

Detect if an editor theme is light or dark.

function detectThemeMode(theme: EditorTheme): UITheme

isLightTheme / isDarkTheme

Check if a theme is light or dark.

function isLightTheme(theme: EditorTheme): boolean
function isDarkTheme(theme: EditorTheme): boolean

getUIThemeForEditorTheme

Get the appropriate UI theme for an editor theme.

function getUIThemeForEditorTheme(editorTheme: EditorTheme): UITheme

buildFileTree

Build a hierarchical tree structure from flat file list.

function buildFileTree(files: FileConfig[]): TreeNode[]

Returns: Array of TreeNode objects representing the directory structure.

getFullPath

Get the full path for a file (path + name).

function getFullPath(file: FileConfig): string

Returns: Full path string (e.g., 'src/components/App.tsx' or 'main.js' for root files).

Troubleshooting

WASM Loading Issues

If you encounter WASM loading errors, make sure to:

  1. Import the WASM file correctly (Vite):

    import onigasmWasm from 'onigasm/lib/onigasm.wasm?url';
  2. Pass it to the editor:

    <RunSpaceEditor wasmPath={onigasmWasm} {...otherProps} />

Monaco Dedupe Error

If you see module duplication errors, ensure your vite.config.ts includes:

export default defineConfig({
  resolve: {
    dedupe: ['monaco-editor'],
  },
});

Monaco Worker Errors

Monaco workers are not needed in this package because we use TextMate grammars for syntax highlighting instead of Monaco's built-in language services. This provides:

  • ✅ Better syntax highlighting via TextMate
  • ✅ No worker configuration needed
  • ✅ No worker-related errors
  • ✅ Smaller bundle size
  • ✅ Faster initialization

If you see any worker-related warnings in the console, they can be safely ignored. The editor will function perfectly using TextMate grammars loaded via SyntaxLoader.loadAll().

Note: Language features like autocomplete and diagnostics are not included as this package focuses on code display and editing with syntax highlighting.

React StrictMode Issues

The editor is compatible with React StrictMode. Container cleanup is handled automatically.

Theme Synchronization

The UI theme now automatically syncs with the editor theme. Use detectThemeMode() utility:

import { detectThemeMode } from 'runspace-editor';

const uiTheme = detectThemeMode('github-dark'); // Returns 'dark'
const lightUITheme = detectThemeMode('github-light'); // Returns 'light'

This ensures the entire editor UI (panels, terminal, buttons) matches the editor theme.

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Ensure all tests pass
  5. Submit a pull request

License

UNLICENSED - This project is not yet licensed for public distribution.

Acknowledgments

Built with:

Support

For issues and questions:

Related Documentation


Made with ❤️ by Duong Nguyen