@david.uhlir/ide
v1.0.14
Published
IDE Component
Readme
@david.uhlir/ide
A powerful Monaco Editor-based IDE component for React applications with multi-file support, syntax highlighting, diagnostics, and comparison features.
Features
- 🎨 Monaco Editor Integration - Full Monaco Editor functionality with TypeScript support
- 📁 Multi-file Management - Built-in file explorer with add, rename, delete operations
- 🔍 File Comparison - Side-by-side diff view for comparing files
- 🎯 Diagnostics Support - Display TypeScript/ESLint errors and warnings
- 🌓 Theme Support - Light, dark, and auto themes
- 🔄 Change Tracking - Visual indicators for modified, added, and deleted files
- 📝 TypeScript Support - Full IntelliSense and type checking
- 🎛️ Customizable - Configurable footer, confirmation dialogs, and more
Installation
npm install @david.uhlir/idePeer Dependencies
npm install react styled-components monaco-editor @monaco-editor/reactFor webpack-based projects, also install:
npm install monaco-editor-webpack-pluginWebpack Configuration
Add Monaco Editor Webpack Plugin to your webpack config:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.ttf$/,
type: 'asset/resource',
},
],
},
resolve: {
fallback: {
"path": false,
"fs": false,
"os": false,
"crypto": false,
"buffer": false,
"stream": false,
"util": false,
"assert": false,
"url": false,
"querystring": false,
}
},
output: {
globalObject: 'self',
},
plugins: [
new MonacoWebpackPlugin({
languages: ['javascript', 'typescript', 'json', 'html', 'css', 'markdown'],
features: [
'bracketMatching',
'clipboard',
'coreCommands',
'cursorUndo',
'find',
'folding',
'format',
'hover',
'multicursor',
'suggest'
]
}),
],
};Basic Usage
import React, { useState } from 'react';
import { IDE } from '@david.uhlir/ide';
const files = [
{
filename: '/index.ts',
content: 'console.log("Hello World!");'
},
{
filename: '/utils.ts',
content: 'export const utils = { greeting: "Hello" };'
}
];
function App() {
const [currentFiles, setCurrentFiles] = useState(files);
return (
<div style={{ height: '100vh' }}>
<IDE
files={files}
onChange={setCurrentFiles}
theme="auto"
title="My Code Editor"
/>
</div>
);
}API Reference
IDE Props
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| files | FileItemType[] | required | Array of files to display |
| onChange | (files: FileItemType[]) => void | required | Callback when files change |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | Editor theme |
| themeOverrides | Partial<ThemeType> | undefined | Overrides colors used by the IDE UI |
| monacoTheme | MonacoThemeOptions | undefined | Custom Monaco editor colors and token rules |
| title | React.ReactNode | '' | IDE title displayed in header |
| typings | string[] | [] | Eager TypeScript type definitions |
| lazyTypings | LazyTypingsOptions | undefined | Resolve only the declaration files needed by the active source file |
| diagnostics | MonacoDiagnostic[] | [] | Errors/warnings to display |
| compareWith | FileItemType[] | undefined | Files to compare against |
| onHasChangesChange | (hasChanges: boolean) => void | undefined | Callback when change status updates |
| onCompareDone | () => void | undefined | Callback when comparison is closed |
| onActionConfirm | (message: string) => Promise<boolean> | default confirm | Custom confirmation dialog |
| allowReinit | boolean | false | Allow reinitializing files prop |
| customFooter | React.ReactNode \| ((fullscreen: boolean) => React.ReactNode) | undefined | Custom footer content |
FileItemType
interface FileItemType {
filename: string;
content: string;
oldFilename?: string; // For renamed files
}MonacoDiagnostic
interface MonacoDiagnostic {
category: number;
code: number;
length: number;
messageText: string;
start: number;
}Advanced Examples
With TypeScript Support
import { IDE } from '@david.uhlir/ide';
const typings = [
`declare module "my-module" {
export function myFunction(): string;
}`
];
const files = [
{
filename: '/app.ts',
content: `import { myFunction } from "my-module";
console.log(myFunction());`
}
];
<IDE
files={files}
onChange={setFiles}
typings={typings}
diagnostics={diagnostics}
/>With Lazy TypeScript Typings
For larger projects, use lazyTypings to keep the initial declaration payload small. The resolver is supplied by the host application so it can attach the project session and authorization required by its typings endpoint.
const lazyTypings = {
revision: projectRevision,
bootstrap: [
{
filePath: 'file:///typings/project/global.d.ts',
content: globalDeclarations,
hash: globalDeclarationsHash,
},
],
resolve: async request => {
const response = await fetch('/api/typings/resolve', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(request),
})
if (!response.ok) {
throw new Error(`Typing resolution failed: ${response.status}`)
}
return response.json()
},
}
<IDE files={files} onChange={setFiles} lazyTypings={lazyTypings} />The endpoint must return stable virtual filePath values, content hash values, and the complete transitive declaration closure. Keep standard-library and global declarations in bootstrap; see the complete lazy typings contract for endpoint behavior, revisions, caching, security, and error handling.
With File Comparison
const originalFiles = [
{ filename: '/config.json', content: '{"version": "1.0.0"}' }
];
const compareFiles = [
{ filename: '/config.json', content: '{"version": "2.0.0"}' }
];
<IDE
files={originalFiles}
onChange={setFiles}
compareWith={compareFiles}
onCompareDone={() => console.log('Comparison closed')}
/>With Custom Footer
<IDE
files={files}
onChange={setFiles}
customFooter={(isFullscreen) => (
<div>
<button onClick={saveFiles}>Save All</button>
<span>Status: {isFullscreen ? 'Fullscreen' : 'Normal'}</span>
</div>
)}
/>With Diagnostics
const diagnostics = [
{
category: 1, // Error
code: 2304,
length: 9,
messageText: "Cannot find name 'undefined_var'.",
start: 45,
}
];
<IDE
files={files}
onChange={setFiles}
diagnostics={diagnostics}
/>Styling
The IDE uses styled-components and respects the system theme by default. You can customize the appearance by:
- Theme Selection: Use
themeprop to force light/dark mode - IDE Colors: Use
themeOverridesto override any field from the exportedThemeType - Monaco Themes: Use
monacoThemefor Monaco UI colors and syntax token rules
<IDE
files={files}
onChange={setFiles}
theme="dark"
themeOverrides={{
background: '#17191d',
backgroundFilesList: '#20242a',
colorFilesList: '#d7dae0',
border: '#303640',
}}
monacoTheme={{
base: 'vs-dark',
colors: {
'editor.background': '#17191d',
'editor.foreground': '#d7dae0',
'editorLineNumber.foreground': '#687080',
'editorLineNumber.activeForeground': '#d7dae0',
'editor.selectionBackground': '#35445c',
},
}}
/>monacoTheme is optional. Without it, Monaco continues using its built-in vs or vs-dark theme. MonacoThemeOptions accepts partial theme data; omitted fields use the selected light/dark base theme.
Development
Running the Example
# Clone the repository
git clone https://github.com/daviduhlir/ide.git
cd ide
# Install dependencies
npm install
# Build the package
npm run build
# Run the example
cd example
npm install
npm startBuilding
npm run build # Build for production
npm run build:watch # Build in watch mode
npm run build:release # Build optimized releaseTesting
npm test # Run all tests (18 tests pass)
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage reportTest Status: ✅ 18/18 tests passing
The project includes tests for:
- ✅ Utility Functions: Path resolution, file extension handling
- ✅ Business Logic: File operations, validation, change detection
- ✅ Conceptual Tests: File type detection, comparison algorithms
Monaco Editor Testing: Component-level tests are disabled (.disabled files) due to Monaco's complexity with Web Workers and browser APIs. This is a common pattern in Monaco-based projects.
Manual Testing: Use cd example && npm start for testing Monaco-specific features.
See Testing Documentation for detailed strategy.
Code Quality
npm run prettier-check # Check code formatting
npm run prettier-format # Format codeBrowser Support
- Chrome/Edge 88+
- Firefox 78+
- Safari 14+
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
ISC License - see LICENSE file for details.
Changelog
0.0.18
- Current stable release
- Full Monaco Editor integration
- Multi-file support with file explorer
- Comparison view functionality
- TypeScript diagnostics support
