Skip to content
GitHub

Loro

Make the editor collaborative by integrating Loro with ProseKit.

import 'prosekit/basic/style.css'
import 'prosekit/basic/typography.css'
import 'prosekit/extensions/loro/style.css'

import type {
  CursorAwareness,
  LoroDocType,
} from 'loro-prosemirror'
import { createEditor } from 'prosekit/core'
import { ProseKit } from 'prosekit/react'
import { useMemo } from 'react'

import { defineExtension } from './extension'
import Toolbar from './toolbar'

export default function EditorComponent(props: {
  loro: LoroDocType
  awareness: CursorAwareness
}) {
  const editor = useMemo(() => {
    const extension = defineExtension(props.loro, props.awareness)
    return createEditor({ extension })
  }, [props.loro, props.awareness])

  return (
    <ProseKit editor={editor}>
      <div className='box-border h-full w-full min-h-36 overflow-y-hidden overflow-x-hidden rounded-md border border-solid border-gray-200 dark:border-gray-700 shadow flex flex-col bg-white dark:bg-gray-950 color-black dark:color-white'>
        <Toolbar />
        <div className='relative w-full flex-1 box-border overflow-y-scroll'>
          <div ref={editor.mount} className='ProseMirror box-border min-h-full px-[max(4rem,_calc(50%-20rem))] py-8 outline-none outline-0 [&_span[data-mention="user"]]:text-blue-500 [&_span[data-mention="tag"]]:text-violet-500'></div>
        </div>
      </div>
    </ProseKit>
  )
}

You need to install loro-crdt and loro-prosemirror to use this extension.

pnpm add loro-crdt loro-prosemirror
import 'prosekit/extensions/loro/style.css'
import { LoroDoc
class LoroDoc<T extends Record<string, Container> = Record<string, Container>>
interface LoroDoc<T extends Record<string, Container> = Record<string, Container>>
The CRDTs document. Loro supports different CRDTs include [**List**](LoroList), [**RichText**](LoroText), [**Map**](LoroMap) and [**Movable Tree**](LoroTree), you could build all kind of applications by these. **Important:** Loro is a pure library and does not handle network protocols. It is the responsibility of the user to manage the storage, loading, and synchronization of the bytes exported by Loro in a manner suitable for their specific environment.
@example```ts import { LoroDoc } from "loro-crdt" const loro = new LoroDoc(); const text = loro.getText("text"); const list = loro.getList("list"); const map = loro.getMap("Map"); const tree = loro.getTree("tree"); ```
} from 'loro-crdt'
import { CursorAwarenessclass CursorAwareness, type LoroDocType
type LoroDocType = LoroDoc<{
    doc: LoroMap<LoroNodeContainerType>;
    data: LoroMap;
}>
,
} from 'loro-prosemirror' import { defineLorofunction defineLoro(options: LoroOptions): LoroExtension
@public
} from 'prosekit/extensions/loro'
const docconst doc: LoroDocType: LoroDocType
type LoroDocType = LoroDoc<{
    doc: LoroMap<LoroNodeContainerType>;
    data: LoroMap;
}>
= new LoroDoc
new LoroDoc<{
    doc: LoroMap<LoroNodeContainerType>;
    data: LoroMap;
}>(): LoroDoc<{
    doc: LoroMap<LoroNodeContainerType>;
    data: LoroMap;
}>
Create a new loro document. New document will have a random peer id.
()
const awarenessconst awareness: CursorAwareness = new CursorAwarenessnew CursorAwareness(peer: PeerID, timeout?: number): CursorAwareness(docconst doc: LoroDocType.peerIdStrLoroDoc<{ doc: LoroMap<LoroNodeContainerType>; data: LoroMap; }>.peerIdStr: `${number}`
Get peer id in decimal string.
)
const extensionconst extension: LoroExtension = defineLorofunction defineLoro(options: LoroOptions): LoroExtension
@public
({ docLoroOptions.doc: LoroDocType
The Loro instance handles the state of shared data.
, awarenessLoroOptions.awareness: CursorAwareness
The Awareness instance.
})