### Initialize and Start OpenSumi Development Environment Source: https://github.com/opensumi/core/blob/main/README.md Commands to install dependencies, initialize the project, and launch the IDE development server. Optionally, a custom workspace directory can be specified using the MY_WORKSPACE environment variable. ```bash $ yarn install $ yarn run init $ yarn run download-extension # Optional $ yarn run start ``` ```bash $ MY_WORKSPACE={local_path} yarn run start ``` -------------------------------- ### Creating and Registering a Custom BrowserModule Source: https://context7.com/opensumi/core/llms.txt Demonstrates how to implement a custom module by extending BrowserModule, defining services with Dependency Injection, and registering command and keybinding contributions. The example concludes with initializing the ClientApp with the custom module. ```typescript import { Injectable, Provider, Autowired } from '@opensumi/di'; import { BrowserModule, Domain, CommandContribution, CommandRegistry, KeybindingContribution, KeybindingRegistry } from '@opensumi/ide-core-browser'; // Define a service for your module export const IMyService = Symbol('IMyService'); export interface IMyService { doSomething(): Promise; } @Injectable() export class MyServiceImpl implements IMyService { async doSomething(): Promise { console.log('Doing something...'); } } // Define contributions for commands and keybindings @Domain(CommandContribution, KeybindingContribution) export class MyContribution implements CommandContribution, KeybindingContribution { @Autowired(IMyService) private myService: IMyService; registerCommands(commands: CommandRegistry): void { commands.registerCommand( { id: 'my.command', label: 'My Custom Command' }, { execute: async () => { await this.myService.doSomething(); }, isEnabled: () => true, } ); } registerKeybindings(keybindings: KeybindingRegistry): void { keybindings.registerKeybinding({ command: 'my.command', keybinding: 'ctrl+shift+m', when: 'editorFocus', }); } } // Create the module @Injectable() export class MyModule extends BrowserModule { providers: Provider[] = [ MyContribution, { token: IMyService, useClass: MyServiceImpl, }, ]; // Optional: Define a React component to render component = MyModuleView; } // Register module in app startup import { ClientApp } from '@opensumi/ide-core-browser'; const app = new ClientApp({ modules: [ MyModule, // ... other modules ], workspaceDir: '/path/to/workspace', wsPath: 'ws://localhost:8000', }); await app.start(document.getElementById('app')); ``` -------------------------------- ### Define and Consume Preferences in OpenSumi Source: https://context7.com/opensumi/core/llms.txt Demonstrates how to define a PreferenceSchema with various data types, register it using a PreferenceContribution, and utilize the PreferenceService to get, set, and observe preference changes. ```typescript import { Autowired, Injectable } from '@opensumi/di'; import { PreferenceSchema, PreferenceSchemaProperty, PreferenceScope, PreferenceContribution, Domain, PreferenceService } from '@opensumi/ide-core-browser'; // Define preference schema export const myPreferenceSchema: PreferenceSchema = { properties: { 'myExtension.enableFeature': { type: 'boolean', default: true, description: 'Enable the main feature', scope: 'window', }, 'myExtension.maxItems': { type: 'integer', default: 100, minimum: 1, maximum: 1000, description: 'Maximum number of items to display', scope: 'resource', }, 'myExtension.theme': { type: 'string', enum: ['default', 'dark', 'light', 'high-contrast'], enumDescriptions: [ 'Use system default', 'Dark theme', 'Light theme', 'High contrast for accessibility' ], default: 'default', description: 'Color theme for the extension', scope: 'application', }, 'myExtension.include': { type: 'array', items: { type: 'string' }, default: ['**/*.ts', '**/*.js'], description: 'Glob patterns for files to include', scope: 'resource', }, 'myExtension.advanced': { type: 'object', properties: { timeout: { type: 'number', default: 5000 }, retries: { type: 'number', default: 3 }, }, default: { timeout: 5000, retries: 3 }, description: 'Advanced settings', scope: 'window', }, }, }; // Register preferences via contribution @Domain(PreferenceContribution) @Injectable() export class MyPreferenceContribution implements PreferenceContribution { readonly schema = myPreferenceSchema; } // Use preferences in a service @Injectable() export class MyFeatureService { @Autowired(PreferenceService) private preferenceService: PreferenceService; async initialize(): Promise { // Get preference value const isEnabled = this.preferenceService.get('myExtension.enableFeature'); const maxItems = this.preferenceService.get('myExtension.maxItems', 50); // with default const theme = this.preferenceService.get('myExtension.theme'); // Get preference for specific resource/scope const resourcePrefs = this.preferenceService.get( 'myExtension.include', undefined, '/path/to/workspace/file.ts' ); // Listen for preference changes this.preferenceService.onPreferenceChanged((event) => { if (event.preferenceName === 'myExtension.enableFeature') { console.log('Feature toggled:', event.newValue); this.toggleFeature(event.newValue as boolean); } }); // Set preference value await this.preferenceService.set( 'myExtension.maxItems', 200, PreferenceScope.User ); // Get all preferences matching a pattern const allMyPrefs = this.preferenceService.resolve('myExtension'); console.log('All myExtension preferences:', allMyPrefs); } private toggleFeature(enabled: boolean): void { console.log('Feature is now:', enabled ? 'enabled' : 'disabled'); } } ``` -------------------------------- ### VS Code Extension API Usage in OpenSumi (TypeScript) Source: https://context7.com/opensumi/core/llms.txt Demonstrates the core functionalities of the VS Code Extension API within OpenSumi. This includes registering commands, language features like completion and hover providers, UI elements such as TreeViews and Webview panels, file system watching, and workspace configuration. It serves as a comprehensive example for extension development. ```typescript import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { // Register a command const disposable = vscode.commands.registerCommand('myExtension.helloWorld', () => { vscode.window.showInformationMessage('Hello from OpenSumi!'); }); context.subscriptions.push(disposable); // Register a completion provider const completionProvider = vscode.languages.registerCompletionItemProvider( { language: 'typescript', scheme: 'file' }, { provideCompletionItems(document, position) { const linePrefix = document.lineAt(position).text.substring(0, position.character); if (!linePrefix.endsWith('console.')) { return undefined; } return [ new vscode.CompletionItem('log', vscode.CompletionItemKind.Method), new vscode.CompletionItem('warn', vscode.CompletionItemKind.Method), new vscode.CompletionItem('error', vscode.CompletionItemKind.Method), ]; } }, '.' // Trigger character ); context.subscriptions.push(completionProvider); // Register a hover provider const hoverProvider = vscode.languages.registerHoverProvider('typescript', { provideHover(document, position) { const word = document.getText(document.getWordRangeAtPosition(position)); if (word === 'OpenSumi') { return new vscode.Hover([ 'OpenSumi Framework', new vscode.MarkdownString('**AI Native IDE Framework**\n\nBuild custom IDEs quickly.') ]); } return undefined; } }); context.subscriptions.push(hoverProvider); // Create a TreeView const treeDataProvider = new MyTreeDataProvider(); const treeView = vscode.window.createTreeView('myExtension.treeView', { treeDataProvider, showCollapseAll: true, }); context.subscriptions.push(treeView); // Create a Webview panel const panel = vscode.window.createWebviewPanel( 'myExtension.panel', 'My Panel', vscode.ViewColumn.One, { enableScripts: true, retainContextWhenHidden: true, } ); panel.webview.html = `

Hello from Webview!

`; panel.webview.onDidReceiveMessage((message) => { if (message.command === 'click') { vscode.window.showInformationMessage(message.text); } }); // Watch workspace files const watcher = vscode.workspace.createFileSystemWatcher('**/*.ts'); watcher.onDidCreate((uri) => console.log('Created:', uri.fsPath)); watcher.onDidChange((uri) => console.log('Changed:', uri.fsPath)); watcher.onDidDelete((uri) => console.log('Deleted:', uri.fsPath)); context.subscriptions.push(watcher); // Read workspace configuration const config = vscode.workspace.getConfiguration('myExtension'); const setting = config.get('enableFeature', true); // Update configuration config.update('enableFeature', false, vscode.ConfigurationTarget.Workspace); } // TreeDataProvider implementation class MyTreeDataProvider implements vscode.TreeDataProvider { private _onDidChangeTreeData = new vscode.EventEmitter(); readonly onDidChangeTreeData = this._onDidChangeTreeData.event; getTreeItem(element: TreeItem): vscode.TreeItem { return element; } getChildren(element?: TreeItem): TreeItem[] { if (!element) { return [ new TreeItem('Item 1', vscode.TreeItemCollapsibleState.Collapsed), new TreeItem('Item 2', vscode.TreeItemCollapsibleState.None), ]; } return [ new TreeItem('Child 1', vscode.TreeItemCollapsibleState.None), new TreeItem('Child 2', vscode.TreeItemCollapsibleState.None), ]; } refresh(): void { this._onDidChangeTreeData.fire(undefined); } } class TreeItem extends vscode.TreeItem { constructor(label: string, collapsibleState: vscode.TreeItemCollapsibleState) { super(label, collapsibleState); this.tooltip = `Tooltip for ${label}`; this.command = { command: 'myExtension.itemClicked', title: 'Click Item', arguments: [this], }; } } export function deactivate() { // Cleanup } ``` -------------------------------- ### Initialize OpenSumi Application with AppConfig Source: https://context7.com/opensumi/core/llms.txt This snippet demonstrates how to define an AppConfig object and initialize a ClientApp instance. It covers essential configurations including workspace paths, layout slots, extension directories, and AI native feature toggles. ```typescript import { ClientApp, SlotLocation } from '@opensumi/ide-core-browser'; import { Injector } from '@opensumi/di'; const injector = new Injector(); const appConfig = { workspaceDir: '/home/user/projects/my-project', wsPath: 'ws://localhost:8000/service', injector, appName: 'MyIDE', appHost: 'web', uriScheme: 'myide', extensionDir: '/home/user/.myide/extensions', extensionCandidate: [ { path: '/path/to/builtin/extension', isBuiltin: true }, ], layoutConfig: { [SlotLocation.top]: { modules: ['@opensumi/ide-menu-bar'] }, [SlotLocation.view]: { modules: [ '@opensumi/ide-explorer', '@opensumi/ide-search', '@opensumi/ide-scm', '@opensumi/ide-debug', '@opensumi/ide-extension-manager', ], }, [SlotLocation.main]: { modules: ['@opensumi/ide-editor'] }, [SlotLocation.panel]: { modules: [ '@opensumi/ide-terminal-next', '@opensumi/ide-output', '@opensumi/ide-markers', ], }, [SlotLocation.statusBar]: { modules: ['@opensumi/ide-status-bar'] }, }, defaultPreferences: { 'editor.fontSize': 14, 'editor.tabSize': 2, 'workbench.colorTheme': 'opensumi-dark', }, AINativeConfig: { enable: true, capabilities: { supportsInlineChat: true, supportsInlineCompletion: true, supportsChatAssistant: true, }, }, isElectronRenderer: false, didRendered: () => { console.log('IDE interface rendered'); }, }; const app = new ClientApp(appConfig); await app.start(document.getElementById('main')); ``` -------------------------------- ### Initialize IDE Layout and Theme from LocalStorage Source: https://github.com/opensumi/core/blob/main/tools/dev-tool/src/index.html Restores the IDE's visual state by parsing saved layout and theme configurations from localStorage. It applies the editor background color to the document root if available. ```javascript let savedColors = {}; try { savedLayout = JSON.parse(localStorage.getItem('layout') || '{}'); savedColors = JSON.parse(localStorage.getItem('theme') || '{}'); } catch (err) {} if (savedColors.editorBackground) { document.getElementsByTagName('html')[0].style.backgroundColor = savedColors.editorBackground; } ``` -------------------------------- ### Registering and Executing Commands in OpenSumi Source: https://context7.com/opensumi/core/llms.txt Demonstrates how to define commands, register them with handlers, implement toggle states, and use command services to execute or intercept actions within an OpenSumi extension. ```typescript import { Autowired, Injectable } from '@opensumi/di'; import { CommandRegistry, CommandService, CommandContribution, Command, Domain, IDisposable } from '@opensumi/ide-core-common'; const MY_COMMANDS = { SAVE_ALL: { id: 'workbench.action.files.saveAll', label: '%command.saveAll%', category: 'File', }, TOGGLE_SIDEBAR: { id: 'workbench.action.toggleSidebar', label: 'Toggle Sidebar', iconClass: 'codicon codicon-layout-sidebar-left', }, CUSTOM_ACTION: { id: 'myExtension.customAction', label: 'My Custom Action', category: 'My Extension', enablement: 'editorFocus && resourceScheme == file', }, }; @Domain(CommandContribution) @Injectable() export class MyCommandContribution implements CommandContribution { @Autowired(CommandRegistry) private commandRegistry: CommandRegistry; @Autowired(CommandService) private commandService: CommandService; registerCommands(registry: CommandRegistry): void { registry.registerCommand(MY_COMMANDS.SAVE_ALL, { execute: async (...args: any[]) => { console.log('Saving all files...', args); return { success: true }; }, isEnabled: () => true, isVisible: () => true, }); let sidebarVisible = true; registry.registerCommand(MY_COMMANDS.TOGGLE_SIDEBAR, { execute: () => { sidebarVisible = !sidebarVisible; return sidebarVisible; }, isToggled: () => sidebarVisible, }); registry.registerCommand(MY_COMMANDS.CUSTOM_ACTION); registry.registerHandler(MY_COMMANDS.CUSTOM_ACTION.id, { execute: async (resource: any) => { console.log('Custom action on:', resource); }, isEnabled: (resource) => resource?.scheme === 'file', }); registry.beforeExecuteCommand('myExtension.customAction', async (args) => { return args; }); registry.afterExecuteCommand('myExtension.customAction', async (result) => { return result; }); } async executeCustomCommand(): Promise { const result = await this.commandService.executeCommand( 'myExtension.customAction', { path: '/path/to/file.ts' } ); await this.commandService.tryExecuteCommand('workbench.action.files.saveAll'); this.commandService.onWillExecuteCommand(({ commandId, args }) => { console.log(`About to execute: ${commandId}`); }); } } ``` -------------------------------- ### OpenSumi Extension API Usage (TypeScript) Source: https://context7.com/opensumi/core/llms.txt Demonstrates how to use various OpenSumi extension APIs including layout, toolbar, theme, IDE window, lifecycle, event, chat, reporter, and commands. This snippet requires the 'sumi' and 'vscode' modules. ```typescript // Extension using Sumi APIs (extension.ts) import * as sumi from 'sumi'; import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { // Layout API - Register a custom view const viewDisposable = sumi.layout.registerTabbarView({ id: 'myExtension.customView', title: 'My Custom View', containerId: 'left', // 'left', 'right', 'bottom' component: MyViewComponent, // React component icon: 'codicon codicon-symbol-misc', priority: 10, }); context.subscriptions.push(viewDisposable); // Toggle view visibility sumi.layout.toggleTabbarComponent('myExtension.customView'); // Get view visibility state const isVisible = sumi.layout.isViewVisible('myExtension.customView'); // Toolbar API - Add toolbar button const toolbarAction = sumi.toolbar.registerToolbarAction({ id: 'myExtension.toolbarAction', title: 'My Action', iconPath: 'path/to/icon.svg', command: 'myExtension.executeAction', position: 'left', group: 'navigation', }); context.subscriptions.push(toolbarAction); // Theme API - Get current theme const currentTheme = sumi.theme.getCurrentTheme(); console.log('Current theme:', currentTheme.name, currentTheme.type); // Listen for theme changes sumi.theme.onThemeChanged((theme) => { console.log('Theme changed to:', theme.name); updateComponentStyles(theme.type); }); // IDE Window API sumi.ideWindow.setTitle('My Custom IDE Title'); // Lifecycle API sumi.lifecycle.onWillStop(() => { // Cleanup before IDE stops saveState(); return false; // Don't prevent stop }); // Event API - Custom events const eventDisposable = sumi.event.subscribe('myExtension.customEvent', (data) => { console.log('Custom event received:', data); }); context.subscriptions.push(eventDisposable); // Fire custom event sumi.event.fire('myExtension.customEvent', { message: 'Hello!' }); // Chat API (AI features) const chatAgent = sumi.chat.registerChatAgent({ id: 'myExtension.chatAgent', name: 'My Assistant', description: 'A helpful coding assistant', systemPrompt: 'You are a helpful coding assistant.', async invoke(request, progress, history, token) { // Handle chat request progress({ content: 'Thinking...' }); const response = await callAIBackend(request.message, history); progress({ content: response }); return { success: true }; }, async provideSlashCommands(token) { return [ { name: 'explain', description: 'Explain the selected code' }, { name: 'refactor', description: 'Suggest refactoring' }, { name: 'test', description: 'Generate unit tests' }, ]; }, async provideFollowups(sessionId, token) { return [ { kind: 'reply', message: 'Can you explain more?', title: 'Explain more' }, { kind: 'reply', message: 'Show me an example', title: 'Show example' }, ]; }, }); context.subscriptions.push(chatAgent); // Reporter API - Analytics sumi.reporter.point('myExtension', 'activated', { version: context.extension.packageJSON.version, }); // Commands with enhanced features sumi.commands.registerCommand('myExtension.enhanced', async (args) => { // Access additional context const activeEditor = vscode.window.activeTextEditor; const selection = activeEditor?.selection; if (selection && !selection.isEmpty) { const text = activeEditor.document.getText(selection); await processSelectedText(text); } }); } // React component for custom view const MyViewComponent: React.FC = () => { const [items, setItems] = React.useState([]); React.useEffect(() => { // Initialize view loadItems().then(setItems); }, []); return (

My Custom View

    {items.map((item, index) => (
  • {item}
  • ))}
); }; async function callAIBackend(message: string, history: any[]): Promise { // AI backend call implementation return 'AI response'; } async function processSelectedText(text: string): Promise { // Process text } async function loadItems(): Promise { return ['Item 1', 'Item 2', 'Item 3']; } function updateComponentStyles(themeType: string): void { // Update styles based on theme } function saveState(): void { // Save extension state } export function deactivate() {} ``` -------------------------------- ### Implement ClientAppContribution for Application Lifecycle Hooks (TypeScript) Source: https://context7.com/opensumi/core/llms.txt This TypeScript code demonstrates how to implement the `ClientAppContribution` interface to hook into various stages of the OpenSumi application lifecycle. It covers initialization, startup, shutdown, and reconnection events, allowing for custom logic at each stage. Dependencies like `@opensumi/di` and `@opensumi/ide-core-browser` are required. ```typescript import { Autowired, Injectable } from '@opensumi/di'; import { ClientAppContribution, Domain, IClientApp } from '@opensumi/ide-core-browser'; import { Disposable } from '@opensumi/ide-core-common'; @Domain(ClientAppContribution) @Injectable() export class MyAppContribution implements ClientAppContribution { private disposables = new Disposable(); // Called first, before commands/menus/keybindings are initialized async initialize(app: IClientApp): Promise { console.log('Initializing with config:', app.config.appName); // Set up early dependencies await this.setupDatabase(); } // Called when application starts (shell not yet attached) async onStart(app: IClientApp): Promise { console.log('Application starting...'); // Initialize services that need the injector const myService = app.injector.get(IMyService); await myService.initialize(); } // Called when most modules are started async onDidStart(app: IClientApp): Promise { console.log('Application fully started'); // Safe to interact with all IDE features await app.commandRegistry.executeCommand('workbench.action.openRecent'); // Start background tasks this.startAutoSave(app); } // Called on window close - return true to prevent closing onWillStop(app: IClientApp): boolean | void { const hasUnsavedChanges = this.checkUnsavedChanges(); if (hasUnsavedChanges) { // Return true to prevent window close // Show confirmation dialog return true; } // Allow close return false; } // Called when window is closing (last tick, no async) onStop(app: IClientApp): void { console.log('Application stopping'); // Synchronous cleanup only this.saveStateSync(); } // Called for cleanup during graceful disposal onDisposeSideEffects(app: IClientApp): void { console.log('Disposing side effects'); this.disposables.dispose(); } // Called when WebSocket reconnects after disconnect onReconnect(app: IClientApp): void { console.log('Reconnected to server'); // Resync state with server this.resyncWithServer(); } private async setupDatabase(): Promise { // Database initialization } private checkUnsavedChanges(): boolean { return false; } private saveStateSync(): void { localStorage.setItem('app-state', JSON.stringify({})); } private startAutoSave(app: IClientApp): void { const interval = setInterval(() => { app.commandRegistry.executeCommand('workbench.action.files.saveAll'); }, 60000); this.disposables.addDispose({ dispose: () => clearInterval(interval) }); } private resyncWithServer(): void { // Resync logic } } ``` -------------------------------- ### Register Keybindings with KeybindingRegistry (TypeScript) Source: https://context7.com/opensumi/core/llms.txt Demonstrates how to register various types of keybindings using the KeybindingRegistry in OpenSumi. This includes simple commands, commands with when-clause conditions, multi-chord key sequences, and batch registration. It also shows how to specify scope and priority. ```typescript import { Autowired, Injectable } from '@opensumi/di'; import { KeybindingRegistry, KeybindingContribution, KeybindingScope, KeybindingWeight, Domain, } from '@opensumi/ide-core-browser'; @Domain(KeybindingContribution) @Injectable() export class MyKeybindingContribution implements KeybindingContribution { @Autowired(KeybindingRegistry) private keybindingRegistry: KeybindingRegistry; registerKeybindings(registry: KeybindingRegistry): void { // Simple keybinding registry.registerKeybinding({ command: 'workbench.action.files.save', keybinding: 'ctrlcmd+s', // ctrlcmd = Ctrl on Windows/Linux, Cmd on macOS }); // Keybinding with when clause registry.registerKeybinding({ command: 'editor.action.formatDocument', keybinding: 'shift+alt+f', when: 'editorTextFocus && !editorReadonly', }); // Multi-chord keybinding (Ctrl+K Ctrl+C) registry.registerKeybinding({ command: 'editor.action.addCommentLine', keybinding: 'ctrlcmd+k ctrlcmd+c', when: 'editorTextFocus', }); // Keybinding with priority (higher = more important) registry.registerKeybinding({ command: 'myExtension.specialAction', keybinding: 'ctrl+shift+p', when: 'myExtension.contextActive', priority: KeybindingWeight.ExternalExtension * 100, }); // Register multiple keybindings at once registry.registerKeybindings([ { command: 'workbench.action.terminal.toggleTerminal', keybinding: 'ctrl+`', }, { command: 'workbench.action.quickOpen', keybinding: 'ctrlcmd+p', }, { command: 'workbench.action.showCommands', keybinding: 'ctrlcmd+shift+p', }, ], KeybindingScope.DEFAULT); // User-scope keybinding (overrides defaults) registry.registerKeybinding({ command: 'workbench.action.files.newFile', keybinding: 'ctrlcmd+n', }, KeybindingScope.USER); } // Programmatic keybinding management async manageKeybindings(): Promise { // Check if keybinding exists const hasBinding = this.keybindingRegistry.containsKeybinding( [{ command: 'my.command', keybinding: 'ctrl+m' }], { command: 'my.command', keybinding: 'ctrl+m' } ); // Unregister a keybinding this.keybindingRegistry.unregisterKeybinding('ctrl+shift+m'); // Get accelerator string for display const accelerator = await this.keybindingRegistry.getKeybindingsForCommand('workbench.action.files.save'); console.log('Save keybinding:', accelerator); } } ``` -------------------------------- ### Define Base IDE Styles Source: https://github.com/opensumi/core/blob/main/tools/dev-tool/src/index.html Sets the foundational CSS rules for the IDE container to ensure it occupies the full viewport dimensions. ```css html, body { margin: 0; width: 100%; height: 100%; } ``` -------------------------------- ### Implement ContributionProvider Pattern in TypeScript Source: https://context7.com/opensumi/core/llms.txt Demonstrates the ContributionProvider pattern using TypeScript, enabling modular contributions to extension points. It involves defining contribution interfaces, registries, and concrete implementations. ```typescript import { Autowired, Injectable, Injector, Domain } from '@opensumi/di'; import { ContributionProvider, createContributionProvider, CommandContribution, CommandRegistry } from '@opensumi/ide-core-common'; // Define a custom contribution interface export const MyContribution = Symbol('MyContribution'); export interface MyContribution { registerFeatures(registry: MyFeatureRegistry): void; } // Feature registry that collects contributions @Injectable() export class MyFeatureRegistry { private features: Map = new Map(); @Autowired(MyContribution) private contributionProvider: ContributionProvider; initialize(): void { // Collect all contributions from modules const contributions = this.contributionProvider.getContributions(); for (const contribution of contributions) { contribution.registerFeatures(this); } } registerFeature(id: string, feature: any): void { this.features.set(id, feature); } getFeature(id: string): any { return this.features.get(id); } } // Create contribution provider during module initialization export function createMyContributionProvider(injector: Injector): void { createContributionProvider(injector, MyContribution); } // Implement a contribution @Domain(MyContribution) @Injectable() export class FeatureAContribution implements MyContribution { registerFeatures(registry: MyFeatureRegistry): void { registry.registerFeature('featureA', { name: 'Feature A', execute: () => console.log('Feature A executed'), }); } } // Another contribution from a different module @Domain(MyContribution) @Injectable() export class FeatureBContribution implements MyContribution { registerFeatures(registry: MyFeatureRegistry): void { registry.registerFeature('featureB', { name: 'Feature B', execute: () => console.log('Feature B executed'), }); } } // Register in your module @Injectable() export class MyModule extends BrowserModule { providers = [ MyFeatureRegistry, FeatureAContribution, FeatureBContribution, ]; contributionProvider = MyContribution; } ``` -------------------------------- ### Implement Custom AI Chat Agent Source: https://context7.com/opensumi/core/llms.txt Demonstrates how to implement the IChatAgent interface to create a custom AI assistant. Includes handling slash commands, streaming responses, and providing follow-up questions. ```typescript @Injectable() export class MyChatAgent implements IChatAgent { id = 'myAgent'; metadata = { description: 'My custom AI assistant', fullName: 'My Assistant', isDefault: false, }; async invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: CoreMessage[], token: CancellationToken): Promise { progress({ content: 'Analyzing your request...' }); if (token.isCancellationRequested) return { errorDetails: { message: 'Cancelled' } }; if (request.command === 'explain') return this.handleExplain(request, progress, token); const response = await this.callLLM(request.message, history); for (const chunk of response) { if (token.isCancellationRequested) break; progress({ content: chunk }); } return {}; } } ``` -------------------------------- ### Theme and Event API Source: https://context7.com/opensumi/core/llms.txt Utilities for observing IDE theme changes and managing custom cross-extension events. ```APIDOC ## GET sumi.theme.getCurrentTheme ### Description Retrieves the current IDE theme metadata. ### Response - **name** (string) - Theme name. - **type** (string) - Theme type (e.g., 'dark', 'light'). ## POST sumi.event.fire ### Description Broadcasts a custom event to other extensions subscribed to the event bus. ### Parameters - **eventName** (string) - Required - The event identifier. - **data** (object) - Optional - Payload to send. ``` -------------------------------- ### Import JSON-RPC Runtime Abstraction Source: https://github.com/opensumi/core/wiki/Breaking-Changes Fixes runtime errors related to missing abstraction layers by importing the required JSON-RPC main entry point. ```typescript import '@opensumi/vscode-jsonrpc/lib/node/main'; ``` -------------------------------- ### Importing Path Utility Method in TypeScript Source: https://github.com/opensumi/core/wiki/Breaking-Changes Demonstrates how to import and use the Path utility method from the '@opensumi/ide-core-common' package. This change reflects the refactoring of utility functions into the new '@opensumi/ide-utils' module. ```typescript import { path } from '@opensumi/ide-core-common'; const { Path } = path; ``` -------------------------------- ### Contribute MCP Tools Source: https://context7.com/opensumi/core/llms.txt Illustrates how to define and register custom tools for an MCP (Model Context Protocol) server within the OpenSumi DI system. ```typescript @Injectable() export class MyMCPToolContribution { @Autowired(MCPServerContribution) contribute(): MCPTool[] { return [{ name: 'search_codebase', description: 'Search the codebase for patterns', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, filePattern: { type: 'string', description: 'File glob pattern' }, }, required: ['query'], }, handler: async (args) => { const results = await searchCodebase(args.query, args.filePattern); return { success: true, results }; }, }]; } } ``` -------------------------------- ### VS Code Extension API Usage Source: https://context7.com/opensumi/core/llms.txt Demonstrates common patterns for using the VS Code Extension API within an OpenSumi environment, including command registration, language features, UI elements, and workspace interactions. ```APIDOC ## VS Code Extension API OpenSumi provides VS Code extension API compatibility through the `vscode` namespace. Extensions can use familiar APIs for editors, languages, commands, and workspace functionality. ### Key Features Demonstrated: - **Command Registration**: Registering custom commands that can be invoked by users or other extensions. - **Language Features**: Implementing features like completion item providers and hover providers for specific languages. - **UI Elements**: Creating and managing UI components such as TreeViews and Webview panels. - **Workspace Interaction**: Monitoring file system changes and accessing workspace configuration settings. ### Example Code Structure: ```typescript import * as vscode from 'vscode'; export function activate(context: vscode.ExtensionContext) { // Register a command const disposable = vscode.commands.registerCommand('myExtension.helloWorld', () => { vscode.window.showInformationMessage('Hello from OpenSumi!'); }); context.subscriptions.push(disposable); // Register a completion provider const completionProvider = vscode.languages.registerCompletionItemProvider( { language: 'typescript', scheme: 'file' }, { provideCompletionItems(document, position) { // ... completion logic ... return [new vscode.CompletionItem('log', vscode.CompletionItemKind.Method)]; } }, '.' ); context.subscriptions.push(completionProvider); // Register a hover provider const hoverProvider = vscode.languages.registerHoverProvider('typescript', { provideHover(document, position) { // ... hover logic ... return new vscode.Hover(['OpenSumi Framework']); } }); context.subscriptions.push(hoverProvider); // Create a TreeView const treeDataProvider = new MyTreeDataProvider(); const treeView = vscode.window.createTreeView('myExtension.treeView', { treeDataProvider, showCollapseAll: true, }); context.subscriptions.push(treeView); // Create a Webview panel const panel = vscode.window.createWebviewPanel( 'myExtension.panel', 'My Panel', vscode.ViewColumn.One, { enableScripts: true } ); panel.webview.html = `

Hello from Webview!

`; panel.webview.onDidReceiveMessage((message) => { /* ... */ }); // Watch workspace files const watcher = vscode.workspace.createFileSystemWatcher('**/*.ts'); watcher.onDidCreate((uri) => console.log('Created:', uri.fsPath)); context.subscriptions.push(watcher); // Read and update workspace configuration const config = vscode.workspace.getConfiguration('myExtension'); const setting = config.get('enableFeature', true); config.update('enableFeature', false, vscode.ConfigurationTarget.Workspace); } // TreeDataProvider implementation class MyTreeDataProvider implements vscode.TreeDataProvider { readonly onDidChangeTreeData = new vscode.EventEmitter().event; getTreeItem(element: TreeItem): vscode.TreeItem { return element; } getChildren(element?: TreeItem): TreeItem[] { /* ... */ return []; } refresh(): void { /* ... */ } } class TreeItem extends vscode.TreeItem { constructor(label: string, collapsibleState: vscode.TreeItemCollapsibleState) { super(label, collapsibleState); this.tooltip = `Tooltip for ${label}`; this.command = { command: 'myExtension.itemClicked', title: 'Click Item', arguments: [this] }; } } export function deactivate() { // Cleanup } ``` ``` -------------------------------- ### Layout Management API Source: https://context7.com/opensumi/core/llms.txt Endpoints for registering custom views and managing their visibility within the IDE layout. ```APIDOC ## POST sumi.layout.registerTabbarView ### Description Registers a custom React component as a view in the IDE tabbar. ### Parameters - **id** (string) - Required - Unique identifier for the view. - **title** (string) - Required - Display title of the view. - **containerId** (string) - Required - Target container ('left', 'right', 'bottom'). - **component** (React.FC) - Required - The React component to render. ### Request Example { "id": "myExtension.customView", "title": "My Custom View", "containerId": "left" } ``` -------------------------------- ### Define and Fire Custom Events with EventBus (TypeScript) Source: https://context7.com/opensumi/core/llms.txt Demonstrates how to define custom events by extending `BasicEvent` and how to fire these events using the `IEventBus` service within a module. It shows both simple `fire` and `fireAndAwait` methods, including optional timeouts for awaited events. ```typescript import { Autowired, Injectable } from '@opensumi/di'; import { BasicEvent, IEventBus, WithEventBus, OnEvent, Disposable } from '@opensumi/ide-core-common'; // Define custom events by extending BasicEvent export class FileCreatedEvent extends BasicEvent<{ uri: string; content: string }> {} export class FileSavedEvent extends BasicEvent<{ uri: string; version: number }> {} export class ThemeChangedEvent extends BasicEvent<{ themeName: string; type: 'dark' | 'light' }> {} @Injectable() export class FileService extends WithEventBus { @Autowired(IEventBus) private eventBus: IEventBus; async createFile(uri: string, content: string): Promise { // ... file creation logic // Fire event to notify other modules this.eventBus.fire(new FileCreatedEvent({ uri, content })); } async saveFile(uri: string): Promise { // ... file save logic // Fire and wait for all listeners to complete const results = await this.eventBus.fireAndAwait( new FileSavedEvent({ uri, version: 1 }), { timeout: 5000 } // Optional timeout ); console.log('All save handlers completed:', results); } } // Subscribe to events using decorators @Injectable() export class FileWatcher extends WithEventBus { // Decorator-based subscription (auto-disposed) @OnEvent(FileCreatedEvent) onFileCreated(event: FileCreatedEvent): void { console.log('File created:', event.payload.uri); } @OnEvent(FileSavedEvent) async onFileSaved(event: FileSavedEvent): Promise { console.log('File saved:', event.payload.uri); // Async handlers are supported await this.syncToCloud(event.payload.uri); } } // Manual subscription @Injectable() export class ThemeManager { @Autowired(IEventBus) private eventBus: IEventBus; private disposables = new Disposable(); initialize(): void { // Subscribe to event const disposer = this.eventBus.on(ThemeChangedEvent, (event) => { console.log('Theme changed to:', event.payload.themeName); this.applyTheme(event.payload); }); this.disposables.addDispose(disposer); // One-time subscription this.eventBus.once(FileCreatedEvent, (event) => { console.log('First file created:', event.payload.uri); }); // Directive-based events (string keys) this.eventBus.onDirective('custom:notification', (data) => { console.log('Custom directive received:', data); }); } changeTheme(name: string): void { this.eventBus.fire(new ThemeChangedEvent({ themeName: name, type: name.includes('dark') ? 'dark' : 'light' })); } dispose(): void { this.disposables.dispose(); } } ``` -------------------------------- ### Apply Theme from Local Storage (JavaScript) Source: https://github.com/opensumi/core/blob/main/tools/electron/src/browser/index.html This JavaScript code retrieves a theme object from local storage. It attempts to parse the theme string and, if successful, applies the 'editorBackground' property to the document's root element. It includes error handling for invalid JSON. ```javascript const theme = localStorage.getItem('theme'); try { const themeObj = JSON.parse(theme); if (themeObj.editorBackground) { document.documentElement.style.backgroundColor = themeObj.editorBackground; } } catch (e) {} ``` -------------------------------- ### Refactor ITerminalClient Properties Source: https://github.com/opensumi/core/wiki/Breaking-Changes Replaces the deprecated options getter with the launchConfig property to align with IShellLaunchConfig standards. ```typescript class TerminalClient { get launchConfig(): IShellLaunchConfig { return this._launchConfig; } updateLaunchConfig(launchConfig: IShellLaunchConfig) { // ... } updateTerminalName(options: ITerminalNameOptions) { // ... } } ``` -------------------------------- ### Import Core Commands Source: https://github.com/opensumi/core/wiki/Breaking-Changes Standardizes the import path for common framework commands like TERMINAL_COMMANDS from the core-browser package. ```typescript import { TERMINAL_COMMANDS } from '@opensumi/ide-core-browser'; ``` -------------------------------- ### Configure External MCP Servers in JSON Source: https://github.com/opensumi/core/blob/main/packages/ai-native/MCP.md Defines the structure for adding external MCP servers to the IDE settings. It requires a name, execution command, arguments, and environment variables. ```json { "ai.native.mcp.servers": [ { "name": "server-name", "command": "command-to-execute", "args": ["command-arguments"], "env": { "ENV_VAR_NAME": "env-var-value" } } ] } ``` -------------------------------- ### Create, Update, and Destroy DOM Overlays with createOverlay (TypeScript/React) Source: https://github.com/opensumi/core/blob/main/packages/core-browser/README.md This snippet demonstrates the usage of the `createOverlay` function to create a DOM overlay with a React element. It shows how to update the overlay's content and how to destroy it. The `destroyAllOverlays` function is also shown for cleaning up all created overlays. This utility is useful for displaying temporary UI elements. ```tsx import { createOverlay, destroyAllOverlays } from '@opensumi/ide-core-browser/lib/utils/create-overlay'; const overlayInstance = createOverlay(

hello world

); overlayInstance.update(

hello world

); overlayInstance.destroy(); destroyAllOverlays(); ``` -------------------------------- ### Registering a New MCP Tool (TypeScript) Source: https://github.com/opensumi/core/blob/main/packages/ai-native/MCP.md Demonstrates how to register a new custom tool within the MCP system using a TypeScript class that implements MCPServerContribution. It shows how to define the tool's name, description, input schema, and handler function. ```typescript @Domain(MCPServerContribution) export class MyCustomTool implements MCPServerContribution { registerMCPServer(registry: IMCPServerRegistry): void { registry.registerMCPTool({ name: 'my_custom_tool', description: 'Description of what the tool does', inputSchema: zodToJsonSchema(myInputSchema), handler: async (args, logger) => { // Tool implementation return { content: [{ type: 'text', text: 'Result' }], }; }, }); } } ``` -------------------------------- ### Import Global Explorer Container ID Source: https://github.com/opensumi/core/wiki/Breaking-Changes Demonstrates the usage of the unified uppercase constant for the Explorer container ID. ```typescript import { EXPLORER_CONTAINER_ID } from '@opensumi/ide-explorer/lib/browser/explorer-contribution'; ``` -------------------------------- ### Register Theme Colors Source: https://github.com/opensumi/core/wiki/Breaking-Changes Demonstrates the updated signature for the registerColor function used in theme definitions. ```typescript import { registerColor, Color } from '@opensumi/ide-theme/lib'; export const defaultButtonForeground = registerColor( 'xxxx', { dark: '#D7DBDE', light: '#4D4D4D', hcDark: Color.black, hcLight: Color.white }, localize('xxxx', 'Danger Ghost Button Foreground color.') ); ``` -------------------------------- ### Kaitain IDE CSS Styling Source: https://github.com/opensumi/core/blob/main/tools/cli-engine/src/browser/index.html Provides basic CSS styling for the Kaitain IDE, setting the background color and defining styles for loading indicators. It ensures a dark theme and a centered loading message. ```css body { margin: 0; background-color: #2a2a2a; } #loading { position: relative; width: 100vw; height: 100vh; line-height: 100vh; color: #fff; text-align: center; z-index: 1; transition: opacity 500ms; font-size: 32px; background-color: #2a2a2a; } .loading-hidden { opacity: 0; } ``` -------------------------------- ### Upgrade Monaco Editor Dependencies Source: https://github.com/opensumi/core/wiki/Breaking-Changes Updates package.json dependencies for vscode-textmate and vscode-oniguruma to support Monaco Editor 0.35.x. ```json "vscode-textmate": "7.0.1" "vscode-oniguruma": "1.6.1" ```