- Created a new document for using components in Milkdown. - Added a guide for using plugins in Milkdown, including toggling plugins programmatically and listing official plugins. - Introduced a recipe for integrating Milkdown with Angular, including installation steps and component creation. - Added a recipe for using Milkdown with Next.js, detailing installation and component setup. - Created a guide for integrating Milkdown with NuxtJS, including installation and component creation. - Added a comprehensive guide for using Milkdown with React, covering both Crepe and core Milkdown usage. - Introduced a recipe for SolidJS integration with Milkdown, including installation and component creation. - Added a guide for using Milkdown with Svelte, detailing installation and component setup. - Created a comprehensive guide for integrating Milkdown with Vue, covering both Crepe and core Milkdown usage. - Added a recipe for using Milkdown with Vue2, including installation and component creation.
8.9 KiB
Interacting with Editor
This guide covers the essential ways to interact with the Milkdown editor, including initialization, content management, and editor lifecycle.
Using Crepe Editor
Crepe is a high-level wrapper around Milkdown that provides a simpler API for common editor operations. Here's how to use it:
import { Crepe } from "@milkdown/crepe";
// Create a new editor instance
const editor = new Crepe({
// Optional: specify root element (DOM node or selector)
root: "#editor",
// Optional: set default content, supports markdown, json and dom.
defaultValue: "# Hello Crepe!",
});
// Create the editor
await editor.create();
// Get markdown content
const markdown = editor.getMarkdown();
// Set readonly mode
editor.setReadonly(true);
// Register event listeners
editor.on((listener) => {
listener.markdownUpdated((ctx, markdown) => {
console.log("Content updated:", markdown);
});
listener.focus((ctx) => {
console.log("Editor focused");
});
listener.blur((ctx) => {
console.log("Editor blurred");
});
listener.selectionUpdated((ctx, selection, prevSelection) => {
console.log("Selection updated:", selection);
});
listener.updated((ctx, doc, prevDoc) => {
console.log("Document updated:", doc);
});
});
// Destroy the editor when done
await editor.destroy();
Register to DOM
By default, milkdown will create editor on the document.body. Alternatively, you can also point out which dom node you want it to load into:
import { rootCtx } from "@milkdown/kit/core";
Editor.make().config((ctx) => {
ctx.set(rootCtx, document.querySelector("#editor"));
});
It's also possible to just pass a selector to rootCtx:
The selector will be passed to
document.querySelectorto get the dom.
import { rootCtx } from "@milkdown/kit/core";
Editor.make().config((ctx) => {
ctx.set(rootCtx, "#editor");
});
Setting Default Value
We support three types of default values:
- Markdown strings
- HTML DOM
- Prosemirror documentation JSON
Markdown
You can set a markdown string as the default value of the editor.
import { defaultValueCtx } from "@milkdown/kit/core";
const defaultValue = "# Hello milkdown";
Editor.make().config((ctx) => {
ctx.set(defaultValueCtx, defaultValue);
});
Dom
You can also use HTML as default value.
Let's assume that we have the following html snippets:
<div id="pre">
<h1>Hello milkdown!</h1>
</div>
Then we can use it as a defaultValue with a type specification:
import { defaultValueCtx } from "@milkdown/kit/core";
const defaultValue = {
type: "html",
dom: document.querySelector("#pre"),
};
Editor.make().config((ctx) => {
ctx.set(defaultValueCtx, defaultValue);
});
JSON
We can also use a JSON object as a default value.
This JSON object can be obtained by a listener through the listener-plugin, for example:
import { listener, listenerCtx } from "@milkdown/kit/plugin/listener";
let jsonOutput;
Editor.make()
.config((ctx) => {
ctx.get(listenerCtx).updated((ctx, doc, prevDoc) => {
jsonOutput = doc.toJSON();
});
})
.use(listener);
Then we can use this jsonOutput as default Value:
import { defaultValueCtx } from "@milkdown/kit/core";
const defaultValue = {
type: "json",
value: jsonOutput,
};
Editor.make().config((ctx) => {
ctx.set(defaultValueCtx, defaultValue);
});
Inspecting Editor Status
You can inspect the editor's status through the status property.
import { Editor, EditorStatus } from "@milkdown/kit/core";
const editor = Editor.make().use(/* some plugins */);
assert(editor.status === EditorStatus.Idle);
editor.create().then(() => {
assert(editor.status === EditorStatus.Created);
});
assert(editor.status === EditorStatus.OnCreate);
editor.destroy().then(() => {
assert(editor.status === EditorStatus.Destroyed);
});
assert(editor.status === EditorStatus.OnDestroyed);
You can also listen to the status changes:
import { Editor, EditorStatus } from "@milkdown/kit/core";
const editor = Editor.make().use(/* some plugins */);
editor.onStatusChange((status: EditorStatus) => {
console.log(status);
});
Status Lifecycle
Idle: Initial stateOnCreate: During creationCreated: Successfully createdOnDestroyed: During destructionDestroyed: Successfully destroyed
Adding Listeners
As mentioned above, you can add a listener to the editor, in order to get its value when needed. You can add as many listeners as you want, all the listeners will be triggered at once.
Markdown Listener
You can add markdown listener to get the editor's contents as a markdown string.
⚠️ Markdown listener will influence the performance for large documents, please use it carefully. If you have a large document, I suggest you to only
parseandserializethe document when needed.
import { listener, listenerCtx } from "@milkdown/kit/plugin/listener";
let output = "";
Editor.make()
.config((ctx) => {
ctx.get(listenerCtx).markdownUpdated((ctx, markdown, prevMarkdown) => {
output = markdown;
});
})
.use(listener);
Doc Listener
You can also listen to the raw prosemirror document node, and do things you want from there.
import { listener, listenerCtx } from "@milkdown/kit/plugin/listener";
let jsonOutput;
Editor.make()
.config((ctx) => {
ctx.get(listenerCtx).updated((ctx, doc, prevDoc) => {
jsonOutput = doc.toJSON();
});
})
.use(listener);
Selection Listener
You can track changes to the editor's selection using the selectionUpdated event. This is useful for implementing features like:
- Custom toolbars that update based on selection
- Context menus
- Selection-based formatting controls
import { listener, listenerCtx } from "@milkdown/kit/plugin/listener";
import { Selection, TextSelection } from "@milkdown/prose/state";
Editor.make()
.config((ctx) => {
ctx.get(listenerCtx).selectionUpdated((ctx, selection, prevSelection) => {
if (selection instanceof TextSelection) {
// Get selection range
const { from, to } = selection;
// Example: Update toolbar based on selection
updateToolbar({
hasSelection: from !== to,
selectionStart: from,
selectionEnd: to,
});
}
});
})
.use(listener);
The selection listener will be triggered when the selection is changed. So you don't need to compare them manually.
For more details about listeners, please check Using Listeners.
Readonly Mode
You can set the editor to readonly mode by setting the editable property.
import { editorViewOptionsCtx } from "@milkdown/kit/core";
let readonly = false;
const editable = () => !readonly;
Editor.make().config((ctx) => {
ctx.update(editorViewOptionsCtx, (prev) => ({
...prev,
editable,
}));
});
// set to readonly after 5 secs.
setTimeout(() => {
readonly = true;
}, 5000);
Use Cases for Readonly Mode
- Preview mode
- Document review
- Print-friendly views
- Mobile device optimization
Using Actions
You can use an action to get the context value in a running editor on demand.
For example, to get the markdown string by running an action:
import { Editor, editorViewCtx, serializerCtx } from "@milkdown/kit/core";
async function playWithEditor() {
const editor = await Editor.make().use(commonmark).create();
const getMarkdown = () =>
editor.action((ctx) => {
const editorView = ctx.get(editorViewCtx);
const serializer = ctx.get(serializerCtx);
return serializer(editorView.state.doc);
});
// get markdown string:
getMarkdown();
}
We provide some macros out of the box, you can use them as actions:
import { insert } from "@milkdown/kit/utils";
editor.action(insert("# Hello milkdown"));
Common Actions
- Insert content
- Get current selection
- Apply formatting
- Execute commands
For more details about macros, please check macros.
Destroying
You can call editor.destroy to destroy an existing editor. You can create a new editor again with editor.create.
await editor.destroy();
// Then create again
await editor.create();
If you just want to recreate the editor, you can use editor.create, it will destroy the old editor and create a new one.
await editor.create();
// This equals to call `editor.destroy` and `editor.create` again.
await editor.create();
If you want to clear the plugins and configs for the editor when calling editor.destroy, you can pass true to editor.destroy.
await editor.destroy(true);