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.
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-editorPeer Dependencies
The package requires the following peer dependencies:
npm install react react-dom monaco-editor onigasmImportant: 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
- Workspace Structure: Define files with
id,path, andcontent - Theme Control: Set theme via the
themeprop and handle changes withonThemeChange - Code Execution: Implement
runCodeaction to handle code execution - Callbacks:
onLineSelectandonThemeChangeprovide integration points - WASM Configuration: Required for TextMate syntax highlighting
- 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
readOnlyorhiddenwill haveoperation: 'update' - Files with
origin: 'new'will haveoperation: 'create'(for future file creation feature) readOnlyandhiddenfiles will haveoperation: 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:
- Enable Sandbox: Set
workspace.config.sandbox: trueand provide asandboxURL - Sandbox Tab: A new "Sandbox" tab appears in the right panel
- Load Sandbox: Click "Load Sandbox" button to initialize the iframe
- Run Code: When you click Run, code is sent to the sandbox via
LOAD_CODEmessage - Output Streaming: stdout/stderr streams back to the terminal in real-time
- Completion: Sandbox sends
CODE_LOADEDwhen execution completes
Message Protocol (see .refs/workspace_host_guide.md for details):
READY_FOR_INIT← Sandbox ready for initializationINIT→ Initialize sandboxSETUP_STREAM← MessagePort for streaming outputINITIALIZED← Sandbox ready to accept codeLOAD_CODE→ Send files to sandboxCODE_LOADED← Code execution complete
Security Features:
- Origin Validation: Messages are validated against the
sandboxURLorigin 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
sandboxURLfrom your own domain in production - Avoid using
about:blankin 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.jsonAdvanced 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 themegithub-dark- GitHub dark themesolarized-light- Solarized light themesolarized-dark- Solarized dark themetealwave-light- Tealwave light theme (custom)tealwave-dark- Tealwave dark theme (custom)
UI Themes
light- Light UI modedark- 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-checkIntegration 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 devVisit 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 outputTesting
The project uses Jest and React Testing Library:
# Run all tests
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverageAPI 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 configurationeditorLabel?: string- Custom label for the editor (displayed next to sandbox icon, default: 'RunSpace')leftPanel?: LeftPanelConfig- Left panel configurationrightPanel?: RightPanelConfig- Right panel configurationeditorOptions?: object- Common editor options applied to all filesminimap?: { enabled: boolean }- Minimap configurationlineNumbers?: 'on' | 'off' | 'relative'- Line number displaywordWrap?: 'on' | 'off' | 'wordWrapColumn' | 'bounded'- Word wrap behaviorfontSize?: number- Font size in pixelstabSize?: number- Tab size
wasmPath?: string- Path to onigasm WASM file (required for Vite)className?: string- Custom CSS class for containerheight?: string- Container height (default: '100vh')onLineSelect?: (fileId: string, lineNumber: number) => void- Callback when a line is selectedonThemeChange?: (theme: string) => void- Callback when theme is changedterminalShowTimestamp?: 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 settingsuiTheme: UITheme- UI themefiles?: 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 workspaceactiveFileId: string- Currently active file IDuiTheme: UITheme- UI themeonFileSelect: (fileId: string) => void- Callback when file is selected
Hooks
useWorkspace
Main hook for workspace state management.
Returns:
files: FileConfig[]- List of filesactiveFileId: string- Currently active file IDtheme: EditorTheme- Current editor themeuiTheme: UITheme- Current UI themeterminalOutput: TerminalOutput[]- Terminal output linesisTerminalVisible: boolean- Terminal visibility stateisRunning: boolean- Code execution stateeditors: Map<string, EditorInstance>- Editor instances registrysetActiveFile: (fileId: string) => void- Switch active filesetTheme: (theme: EditorTheme) => void- Change editor themetoggleUITheme: () => void- Toggle light/dark UI modeupdateFileContent: (fileId: string, content: string) => void- Update file contentaddTerminalOutput: (output) => void- Add terminal outputclearTerminal: () => void- Clear terminalsetTerminalVisible: (visible: boolean) => void- Toggle terminalsetRunning: (running: boolean) => void- Set execution stateregisterEditor: (editor: EditorInstance) => void- Register editor instanceunregisterEditor: (fileId: string) => void- Unregister editorgetActiveEditor: () => EditorInstance | undefined- Get active editorgetEditorContent: (fileId: string) => string | undefined- Get file contentcloseFile: (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(): booleangetSupportedLanguages
Get list of supported languages.
function getSupportedLanguages(): string[]changeEditorTheme
Change the editor theme.
function changeEditorTheme(themeName: string): voiddetectThemeMode
Detect if an editor theme is light or dark.
function detectThemeMode(theme: EditorTheme): UIThemeisLightTheme / isDarkTheme
Check if a theme is light or dark.
function isLightTheme(theme: EditorTheme): boolean
function isDarkTheme(theme: EditorTheme): booleangetUIThemeForEditorTheme
Get the appropriate UI theme for an editor theme.
function getUIThemeForEditorTheme(editorTheme: EditorTheme): UIThemebuildFileTree
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): stringReturns: 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:
Import the WASM file correctly (Vite):
import onigasmWasm from 'onigasm/lib/onigasm.wasm?url';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:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Ensure all tests pass
- Submit a pull request
License
UNLICENSED - This project is not yet licensed for public distribution.
Acknowledgments
Built with:
- Monaco Editor - The code editor that powers VS Code
- monaco-ext - Extended Monaco Editor features
- React - UI library
- TailwindCSS - CSS framework
- React Icons - Icon library
Support
For issues and questions:
- GitHub Issues: https://github.com/duongtdn/runspace-editor/issues
- Documentation: https://github.com/duongtdn/runspace-editor#readme
Related Documentation
- Workspace Host Guide - Guide for implementing a Workspace Host that integrates with the Sandbox
Made with ❤️ by Duong Nguyen
