- 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.
123 lines
3.5 KiB
Markdown
123 lines
3.5 KiB
Markdown
# Collaborative Editing
|
|
|
|
Milkdown supports collaborative editing powered by [Y.js](https://docs.yjs.dev/).
|
|
We provide the [@milkdown/plugin-collab](/docs/api/plugin-collab) plugin to help you use milkdown with yjs easily.
|
|
This plugin includes basic collaborative editing features like:
|
|
|
|
- Sync between clients.
|
|
- Remote cursor support.
|
|
- Undo/Redo support.
|
|
|
|
::iframe{src="https://stackblitz.com/github/Milkdown/examples/tree/main/vanilla-collab"}
|
|
|
|
## Configure Plugin
|
|
|
|
First you need to install the plugin and yjs through npm:
|
|
|
|
```bash
|
|
npm install @milkdown/plugin-collab
|
|
|
|
npm install yjs y-protocols y-prosemirror
|
|
```
|
|
|
|
And you also need to choose a [provider for yjs](https://docs.yjs.dev/ecosystem/connection-provider), here we use [y-websocket](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket) as an example.
|
|
|
|
After the installation, you can configure your editor:
|
|
|
|
```typescript
|
|
// ...import other plugins
|
|
import { collab, collabServiceCtx } from "@milkdown/plugin-collab";
|
|
|
|
async function setup() {
|
|
const editor = await Editor.make()
|
|
.config(nord)
|
|
.use(commonmark)
|
|
.use(collab)
|
|
.create();
|
|
|
|
const doc = new Doc();
|
|
const wsProvider = new WebsocketProvider("<YOUR_WS_HOST>", "milkdown", doc);
|
|
|
|
editor.action((ctx) => {
|
|
const collabService = ctx.get(collabServiceCtx);
|
|
|
|
collabService
|
|
// bind doc and awareness
|
|
.bindDoc(doc)
|
|
.setAwareness(wsProvider.awareness)
|
|
// connect yjs with milkdown
|
|
.connect();
|
|
});
|
|
}
|
|
```
|
|
|
|
Now your editor can support collaborative editing. Isn't it easy?
|
|
|
|
## Connect and Disconnect
|
|
|
|
You may want to control the connect status of the editor manually.
|
|
|
|
```typescript
|
|
editor.action((ctx) => {
|
|
const collabService = ctx.get(collabServiceCtx);
|
|
const doc = new Doc();
|
|
const wsProvider = new WebsocketProvider("<YOUR_WS_HOST>", "milkdown", doc);
|
|
|
|
collabService.bindDoc(doc).setAwareness(wsProvider.awareness);
|
|
|
|
document.getElementById("connect").onclick = () => {
|
|
wsProvider.connect();
|
|
collabService.connect();
|
|
};
|
|
|
|
document.getElementById("disconnect").onclick = () => {
|
|
wsProvider.disconnect();
|
|
collabService.disconnect();
|
|
};
|
|
});
|
|
```
|
|
|
|
## Default Template
|
|
|
|
By default, the editor will show a empty document. You may want to use a template to show a document.
|
|
|
|
```typescript
|
|
const template = `# Heading`;
|
|
|
|
editor.action((ctx) => {
|
|
const collabService = ctx.get(collabServiceCtx);
|
|
const doc = new Doc();
|
|
const wsProvider = new WebsocketProvider("<YOUR_WS_HOST>", "milkdown", doc);
|
|
|
|
collabService.bindDoc(doc).setAwareness(wsProvider.awareness);
|
|
|
|
wsProvider.once("synced", async (isSynced: boolean) => {
|
|
if (isSynced) {
|
|
collabService
|
|
// apply your template
|
|
.applyTemplate(markdown)
|
|
// don't forget connect
|
|
.connect();
|
|
}
|
|
});
|
|
});
|
|
```
|
|
|
|
Keep in mind that applying a template multiple times may cause some unexpected behavior, such as duplicate content.
|
|
Because of this you need to make sure **the template is applied only once**.
|
|
|
|
By default, the template will only be applied if _document get from remote server is empty_.
|
|
You can control this behavior through passing second parameter to `applyTemplate`:
|
|
|
|
```typescript
|
|
collabService
|
|
.applyTemplate(markdown, (remoteNode, templateNode) => {
|
|
// return true to apply template
|
|
})
|
|
// don't forget connect
|
|
.connect();
|
|
```
|
|
|
|
Here the nodes we get are [prosemirror nodes](https://prosemirror.net/docs/ref/#model.Node).
|
|
You should return `true` if the template should be applied, and `false` if not.
|