The core module of milkdown - a plugin-driven WYSIWYG markdown Editor built on top of prosemirror and remark
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The Editor class is the central orchestrator of the Milkdown editor, managing the complete lifecycle from initialization through destruction. It provides a fluent API for configuration and plugin management.
The main editor class that manages the entire editor lifecycle.
/**
* The milkdown editor class that orchestrates editor lifecycle
*/
class Editor {
/** Create a new editor instance */
static make(): Editor;
/** Enable or disable inspector for debugging (default: true) */
enableInspector(enable?: boolean): Editor;
/** Subscribe to status change events - replaces previous subscription */
onStatusChange(onChange: OnStatusChange): Editor;
/** Add a configuration function to be executed during initialization */
config(configure: Config): Editor;
/** Remove a previously added configuration function */
removeConfig(configure: Config): Editor;
/** Add plugin(s) to the editor - can be single plugin or array */
use(plugins: MilkdownPlugin | MilkdownPlugin[]): Editor;
/** Remove plugin(s) from the editor - returns Promise for async cleanup */
remove(plugins: MilkdownPlugin | MilkdownPlugin[]): Promise<Editor>;
/** Create/initialize the editor with current config and plugins */
create(): Promise<Editor>;
/** Destroy the editor, optionally clearing all plugins */
destroy(clearPlugins?: boolean): Promise<Editor>;
/** Execute an action with access to the editor context */
action<T>(action: (ctx: Ctx) => T): T;
/** Get inspection data - requires inspector to be enabled */
inspect(): Telemetry[];
/** Get the editor's context instance */
readonly ctx: Ctx;
/** Get the current editor status */
readonly status: EditorStatus;
}Usage Examples:
import { Editor, EditorStatus } from "@milkdown/core";
// Basic editor creation
const editor = Editor.make()
.config((ctx) => {
// Configure context slices
ctx.set(rootCtx, document.getElementById('editor'));
})
.use([plugin1, plugin2]) // Add multiple plugins
.create(); // Returns Promise<Editor>
// Monitor status changes
editor.onStatusChange((status) => {
if (status === EditorStatus.Created) {
console.log('Editor ready!');
}
});
// Execute commands
editor.action((ctx) => {
const commandManager = ctx.get(commandsCtx);
commandManager.call(someCommand, payload);
});
// Cleanup
await editor.destroy(true); // Clear all pluginsEnum representing the current state of the editor lifecycle.
/**
* The status of the editor throughout its lifecycle
*/
enum EditorStatus {
/** The editor is not initialized */
Idle = 'Idle',
/** The editor is in the process of being created */
OnCreate = 'OnCreate',
/** The editor has been created and is ready to use */
Created = 'Created',
/** The editor is in the process of being destroyed */
OnDestroy = 'OnDestroy',
/** The editor has been destroyed */
Destroyed = 'Destroyed'
}Type for functions that respond to editor status changes.
/**
* Callback function type for editor status changes
* @param status - The new editor status
*/
type OnStatusChange = (status: EditorStatus) => void;Usage Examples:
import { Editor, EditorStatus } from "@milkdown/core";
const editor = Editor.make()
.onStatusChange((status) => {
switch (status) {
case EditorStatus.OnCreate:
showLoadingSpinner();
break;
case EditorStatus.Created:
hideLoadingSpinner();
enableEditorFeatures();
break;
case EditorStatus.OnDestroy:
disableEditorFeatures();
break;
case EditorStatus.Destroyed:
cleanup();
break;
}
});
// Status is initially Idle
console.log(editor.status); // EditorStatus.Idle
// Status changes to OnCreate, then Created
await editor.create();
console.log(editor.status); // EditorStatus.Created
// Status changes to OnDestroy, then Destroyed
await editor.destroy();
console.log(editor.status); // EditorStatus.DestroyedType for configuration functions that modify the editor context during initialization.
/**
* Configuration function type for customizing editor initialization
* @param ctx - The editor context to configure
*/
type Config = (ctx: Ctx) => void | Promise<void>;Usage Examples:
import { Editor, defaultValueCtx, rootCtx } from "@milkdown/core";
// Synchronous configuration
const syncConfig: Config = (ctx) => {
ctx.set(defaultValueCtx, '# Hello World\n\nThis is **bold** text.');
ctx.set(rootCtx, document.getElementById('editor'));
};
// Asynchronous configuration
const asyncConfig: Config = async (ctx) => {
const content = await fetch('/api/initial-content').then(r => r.text());
ctx.set(defaultValueCtx, content);
};
const editor = Editor.make()
.config(syncConfig)
.config(asyncConfig)
.removeConfig(syncConfig) // Remove if needed
.create();Adding Plugins:
import { Editor } from "@milkdown/core";
import { somePlugin, anotherPlugin } from "@milkdown/preset-plugins";
const editor = Editor.make()
.use(somePlugin) // Single plugin
.use([anotherPlugin, somePlugin]) // Multiple plugins
.create();Removing Plugins:
// Remove plugins after creation (async operation)
await editor.remove(somePlugin);
await editor.remove([plugin1, plugin2]);Enable Inspector:
const editor = Editor.make()
.enableInspector(true) // Enable debugging
.create();
// Get telemetry data
const telemetry = editor.inspect();
console.log('Plugin telemetry:', telemetry);The editor provides several safeguards for common error scenarios:
inspect() without enabling inspector will log a warning and return empty arrayaction() method ensures you have proper context access for operationsExample Error Handling:
try {
const editor = Editor.make()
.config((ctx) => {
// Configuration that might throw
if (!document.getElementById('editor')) {
throw new Error('Editor root element not found');
}
ctx.set(rootCtx, document.getElementById('editor'));
})
.create();
// Safe action execution
editor.action((ctx) => {
const commands = ctx.get(commandsCtx);
if (!commands.call(someCommand, payload)) {
console.warn('Command execution failed');
}
});
} catch (error) {
console.error('Editor setup failed:', error);
}