@ydb-platform/monaco-ghost v1.0.0
@ydb-platform/monaco-ghost
š A lightweight adapter for integrating completion services with Monaco Editor's inline completion system.
Installation ⢠Quick Start ⢠Documentation ⢠Contributing
⨠Features at a Glance
- š» Ghost Text Display - Inline suggestions with keyboard navigation
- ā” High Performance - Debouncing, caching, and optimized for large files
- šÆ Type Safety - Comprehensive TypeScript support
- šØ Theme Support - Dark and light themes with customization options
- š Event System - Rich analytics and error tracking
- š§© React Integration - Pre-built components and hooks
š¦ Installation
npm install @ydb-platform/monaco-ghost monaco-editorš Quick Start
The library provides multiple ways to integrate with React applications. Here are the main approaches:
import React, { useCallback } from 'react';
import MonacoEditor from 'react-monaco-editor';
import * as monaco from 'monaco-editor';
import { useMonacoGhost } from '@ydb-platform/monaco-ghost';
function MyCustomEditor() {
// Java-specific API implementation
const javaApi = {
getCodeAssistSuggestions: async () => ({
Suggests: [{ Text: 'System.out.println("Hello, World!");' }],
RequestId: 'demo-request',
}),
};
// Java-specific configuration
const javaConfig = {
debounceTime: 200,
suggestionCache: {
enabled: true,
},
};
const eventHandlers = {
onCompletionAccept: text => console.log('Accepted:', text),
onCompletionDecline: (text, reason, otherSuggestions) =>
console.log('Declined:', text, reason, otherSuggestions),
onCompletionIgnore: (text, otherSuggestions) => console.log('Ignored:', text, otherSuggestions),
onCompletionError: error => console.error('Error:', error),
};
const { register } = useMonacoGhost({
api: javaApi,
eventHandlers,
config: javaConfig,
});
const editorDidMount = useCallback(
(editor: monaco.editor.IStandaloneCodeEditor) => {
register(editor);
},
[register]
);
const options = {
selectOnLineNumbers: true,
minimap: { enabled: false },
automaticLayout: true,
fontSize: 14,
lineNumbers: 'on',
scrollBeyondLastLine: false,
roundedSelection: false,
padding: { top: 10 },
};
return (
<MonacoEditor
width="800"
height="600"
language="java"
theme="vs-dark" // or "vs-light"
value="// Your Java code here"
options={options}
editorDidMount={editorDidMount}
/>
);
}For more control over the ghost instance lifecycle, you can use createMonacoGhostInstance instead of the hook:
import React from 'react';
import MonacoEditor from 'react-monaco-editor';
import * as monaco from 'monaco-editor';
import { createMonacoGhostInstance } from '@ydb-platform/monaco-ghost';
function MyCustomEditor() {
const [monacoGhostInstance, setMonacoGhostInstance] =
React.useState<ReturnType<typeof createMonacoGhostInstance>>();
// Configuration and helpers
const monacoGhostConfig = {
api: {
getCodeAssistSuggestions: async () => ({
Suggests: [{ Text: 'System.out.println("Hello, World!");' }],
RequestId: 'demo-request',
}),
},
eventHandlers: {
onCompletionAccept: text => console.log('Accepted:', text),
onCompletionDecline: (text, reason, otherSuggestions) =>
console.log('Declined:', text, reason, otherSuggestions),
onCompletionIgnore: (text, otherSuggestions) =>
console.log('Ignored:', text, otherSuggestions),
onCompletionError: error => console.error('Error:', error),
},
config: {
language: 'java',
debounceTime: 200,
suggestionCache: {
enabled: true,
},
},
};
// Initialize ghost instance when enabled
React.useEffect(() => {
if (monacoGhostInstance && isCodeAssistEnabled) {
monacoGhostInstance.register(monacoGhostConfig);
}
return () => {
monacoGhostInstance?.unregister();
};
}, [isCodeAssistEnabled, monacoGhostConfig, monacoGhostInstance]);
const editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor) => {
setMonacoGhostInstance(createMonacoGhostInstance(editor));
};
return (
<MonacoEditor
width="800"
height="600"
language="java"
theme="vs-dark"
value="// Your Java code here"
options={{
selectOnLineNumbers: true,
minimap: { enabled: false },
automaticLayout: true,
}}
editorDidMount={editorDidMount}
/>
);
}This approach gives you more control over when the ghost instance is created and destroyed, and allows for more complex initialization logic if needed.
// Using the pre-built editor component
import { MonacoEditor } from '@ydb-platform/monaco-ghost';
function MyApp() {
// SQL-specific API implementation
const sqlApi = {
getCodeAssistSuggestions: async () => ({
Suggests: [{ Text: 'SELECT * FROM users;' }],
RequestId: 'demo-request',
}),
};
// SQL-specific configuration
const sqlConfig = {
debounceTime: 200,
suggestionCache: {
enabled: true,
},
};
return (
<MonacoEditor
initialValue="-- Your SQL code here"
language="sql"
theme="vs-dark" // or "vs-light"
api={sqlApi}
config={sqlConfig}
onCompletionAccept={text => console.log('Accepted:', text)}
onCompletionDecline={(text, reason, otherSuggestions) =>
console.log('Declined:', text, reason, otherSuggestions)
}
onCompletionIgnore={(text, otherSuggestions) =>
console.log('Ignored:', text, otherSuggestions)
}
onCompletionError={error => console.error('Error:', error)}
editorOptions={{
minimap: { enabled: false },
fontSize: 14,
}}
/>
);
}Vanilla JavaScript
import * as monaco from 'monaco-editor';
import {
createCodeCompletionService,
registerCompletionCommands,
} from '@ydb-platform/monaco-ghost';
// Create language-specific API implementation
const sqlApi = {
getCodeAssistSuggestions: async data => {
// Call your completion service
// Return suggestions in the expected format
return {
Suggests: [{ Text: 'SELECT * FROM users;' }],
RequestId: 'request-id',
};
},
};
// Configure the adapter with language-specific settings
const sqlConfig = {
debounceTime: 200,
suggestionCache: {
enabled: true,
},
};
// Create provider for SQL
const sqlCompletionProvider = createCodeCompletionService(sqlApi, sqlConfig);
// Subscribe to completion events with type safety
sqlCompletionProvider.events.on('completion:accept', data => {
console.log('Completion accepted:', data.acceptedText);
});
sqlCompletionProvider.events.on('completion:decline', data => {
console.log(
'Completion declined:',
data.suggestionText,
'reason:',
data.reason,
'other suggestions:',
data.otherSuggestions
);
});
sqlCompletionProvider.events.on('completion:ignore', data => {
console.log(
'Completion ignored:',
data.suggestionText,
'other suggestions:',
data.otherSuggestions
);
});
sqlCompletionProvider.events.on('completion:error', error => {
console.error('Completion error:', error);
});
// Register with Monaco for SQL
monaco.languages.registerInlineCompletionsProvider(['sql'], sqlCompletionProvider);
// Register commands (assuming you have an editor instance)
registerCompletionCommands(monaco, sqlCompletionProvider, editor);š Documentation
š® Keyboard Shortcuts
| Key | Action |
|---|---|
Tab | Accept current suggestion |
Escape | Decline current suggestion |
Alt+] | Cycle to next suggestion |
Alt+[ | Cycle to previous suggestion |
āļø Configuration
interface CodeCompletionConfig {
// Required when using hooks
language?: string; // The language this configuration applies to (e.g., 'sql', 'java')
// Performance settings
debounceTime?: number; // Time in ms to debounce API calls (default: 200)
// Cache settings
suggestionCache?: {
enabled?: boolean; // Whether to enable suggestion caching (default: true)
};
}š API Interface
interface ICodeCompletionAPI {
getCodeAssistSuggestions(data: PromptFile[]): Promise<Suggestions>;
}
export interface PromptPosition {
lineNumber: number;
column: number;
}
export interface PromptFragment {
text: string;
start: PromptPosition;
end: PromptPosition;
}
export interface PromptFile {
path: string;
fragments: PromptFragment[];
cursorPosition: PromptPosition;
}
export interface Suggestions {
items: string[];
requestId?: string;
}š Events
The completion service emits four types of events with rich data:
1. Acceptance Events
interface CompletionAcceptEvent {
requestId: string;
acceptedText: string;
}
completionProvider.events.on('completion:accept', (data: CompletionAcceptEvent) => {
console.log('Accepted:', data.acceptedText);
});2. Decline Events
interface CompletionDeclineEvent {
requestId: string;
suggestionText: string;
reason: string;
hitCount: number;
otherSuggestions: string[];
}
completionProvider.events.on('completion:decline', (data: CompletionDeclineEvent) => {
console.log('Declined:', data.suggestionText, 'reason:', data.reason);
console.log('Other suggestions:', data.otherSuggestions);
console.log('Times shown:', data.hitCount);
});3. Ignore Events
interface CompletionIgnoreEvent {
requestId: string;
suggestionText: string;
otherSuggestions: string[];
}
completionProvider.events.on('completion:ignore', (data: CompletionIgnoreEvent) => {
console.log('Ignored:', data.suggestionText);
console.log('Other suggestions:', data.otherSuggestions);
});4. Error Events
completionProvider.events.on('completion:error', (error: Error) => {
console.error('Completion error:', error);
});š ļø Development
Setup
# Install dependencies
npm install
# Start Storybook for development
npm run storybook
# Run tests
npm run test
# Run tests with coverage
npm run test:coverageBuild System
The package uses a hybrid build system:
- TypeScript (tsc) for type checking and declaration files
- esbuild for fast, optimized builds
Output Formats:
- CommonJS:
dist/cjs/index.js - ES Modules:
dist/esm/index.js - TypeScript Declarations:
dist/types/index.d.ts
# Type checking only
npm run type-check
# Build type declarations
npm run build:types
# Build CommonJS version
npm run build:cjs
# Build ES Modules version
npm run build:esm
# Full build (all formats)
npm run buildš„ Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
1. Code Context
- Handle text limits appropriately
- Maintain cursor position accuracy
- Consider edge cases
- Support partial text acceptance
2. Error Handling
- Wrap API calls in try-catch blocks
- Fail gracefully on errors
- Log issues without breaking editor
- Emit error events for monitoring
3. Performance
- Use debouncing for API calls
- Implement efficient caching
- Track suggestion hit counts
- Clean up resources properly
4. Testing
- Add tests for new features
- Maintain backward compatibility
- Test edge cases
- Verify event handling
š License
Apache-2.0 - see LICENSE file for details.
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago