@automerge/prosemirror

中文文档
0.2.0发布于 2026/02/25MITTypeScriptES Module近 30 天下载 3.2万

合作编写紧随 丰富的文本计划 使用ProseMirror. (英语).

中文使用文档
文档目录8 节
  1. 状态
  2. 怎样玩
  3. 示例
  4. Schema 绘图
  5. 将块转换为节点
  6. 将标记转换为标记
  7. API 密码
  8. SchemaAdapter

自动缩放 prosemiror 绑定

合作编写紧随 丰富的文本计划 使用ProseMirror. (英语).

状态

这个插件是β质量软件. API可能会在稳定放出之前稍有改变,并且存在bugs,但也有合理的效果.

怎样玩

此寄存器中有一个功能完备的编辑器, 您可以通过运行来播放 npm run playground 然后访问 http://localhost:5173。 。 。 .

示例

这个库提供了一个插件,用于映射Autommerge文档和ProseMirror文档之间. 这个插件依赖于两件事:第一,你使用的图案是ProseMiror schema的一个非常具体的子集,它被映射到Automerce丰富的文本图案(更多关于这个)上;第二,你从Automerce文档中初始化了ProseMiror文档. 因此,我们提供一个简单的切入点,为你们做所有这些事情:

import { init } from "@automerge/prosemirror"

// Obtain a DocHandle somehow
const handle = repo.find("some-doc-url")
// wait for the handle to be ready before continuing
await handle.whenReady()

// This is the important part, we initialize the plugin with the handle and the path to the text field in the document
// and we get back a schema, a ProseMirror document, and a plugin
const { schema, pmDoc, plugin } = init(handle, ["text"])

// Create your prosemirror state with the schema, plugin, and document
let editorConfig = {
  schema,
  plugins: [
    keymap({
      ...baseKeymap,
      "Mod-b": toggleBold,
      "Mod-i": toggleItalic,
      "Mod-z": undo,
      "Mod-y": redo,
      "Mod-Shift-z": redo,
    }),
    plugin,
  ],
  doc: pmDoc
}

let state = EditorState.create(editorConfig)

const view = new EditorView(<whatever DOM element you are rendering to>, {
  state
})

见 playground/src/Editor.tsx 用于完整显示的示例.

Schema 绘图

ProseMirror 文档有一个计划,它决定文档中允许的节点和标记的种类. 要在自动化文档和ProseMirror文档中的块标记和标记之间映射,您必须创建 SchemaAdapter。对 SchemaAdapter 和那个一样 {nodes, marks} 对象,用于创建 ProseMirror 计划,但是 nodes 财务报告和审定财务报表 marks 对象有一个额外的可选密钥,名为 automerge,它配置自动化文档如何映射到ProseMirror文档.

举例来说,这里有一个片段,它描绘了 paragraph 节点到 paragraph 块类型 :

import {SchemaAdapter} from "@automerge/prosemirror"
const adapter = new SchemaAdapter({
    nodes: {
        ... // other nodes
        /// A plain paragraph textblock. Represented in the DOM
        /// as a `<p>` element.
        paragraph: {
          // ---------------------------------------
          //    This is the automerge configuration
          // ---------------------------------------
          automerge: {
            block: "paragraph",
          },
          // ---------------------------------------
          content: "inline*",
          group: "block",
          parseDOM: [{ tag: "p" }],
          toDOM() {
            return pDOM
          },
        } as NodeSpec,
    }
})

此策略适配器可以传递给 init 作为一种选项:

const { pmDoc, schema, plugin } = init(handle, ["text"], {
  schemaAdapter: adapter,
})

表格中有许多键可用 automerge 测绘. 要了解它们都意味着什么 你需要了解计划绘图的目标:

  • 在自动合并块和ProseMirror节点之间转换
  • 在自动合并标记和ProseMiror标记之间转换
  • 在ProseMirror 文档中代表未知的块和标记,这样编辑一个带有未知标记或块的文档不会导致它们丢失

将块转换为节点

简单块 <- > 节点映射

将块转换为节点的简单案例是当从块类型到节点类型的一对一映射,而节点没有任何额外的属性. 与上段标记的情况一样,这通常看起来像:

nodes: {
    <node name>: {
        // rest of the node spec
        automerge: {
            block: <block name>
        }
    }
}

依靠父母绘制地图

一个更复杂的案例是当块的类型取决于周围的内容. 例如,a <li> proseMirror中的节点可以是 ordered-list-item 或一个 unordered-list-item 在丰富的文本中, 在这种情况下,你可以使用 within 键 :

nodes: {
    list_item: {
        // rest of the node spec
        automerge: {
            // Here the keys of the map are other nodes in the schema
            within: {
                ordered_list: "ordered-list-item",
                bullet_list: "unordered-list-item"
            }
        }
    }
}

具有额外属性的映射

许多节点和块标记比仅仅一种类型名称更为复杂. 它们也带有属性. 在这种情况下,你使用 attrParsers 会 议 日 程 和 议 程 automerge 键。 例如 heading 块标记 level 属性,用于确定标题的级别。 这就是你如何将它映射到ProseMirror节点:

nodes: {
    heading {
    // rest of the node spec
      automerge: {
        block: "heading",
        attrParsers: {
          fromAutomerge: block => ({ level: block.attrs.level }),
          fromProsemirror: node => ({ level: node.attrs.level }),
        },
      },
    }
}

这个 fromAutomerge 函数将会被传递到块标记,并且应该返回一组节点属性,同时 fromProsemirror 函数将传递一个节点,并返回块属性地图.

嵌入区块

一些块类型并不代表分级内容,而是代表嵌入内容,这些内容不会改变文本继而起的结构作用。 例如,一个图像块标记只是表示一个图像应该出现在块标记的位置上. 对于这些街区,你可以设置 isEmbed 键到 true数字 :

nodes: {
  image: {
    // the rest of the node spec
    automerge: {
      block: "image",
      isEmbed: true,
      attrParsers: { .. },
    }
  }
}

将标记转换为标记

标记比块简单一点 这个 automerge 在标记 spec上键可以包含 markName 键,该键指定自动合并文档中的标记与ProseMirror文档中的标记相对应.

例如,a strong 标记可能是这样绘制的:

marks: {
    strong: {
      // rest of the mark spec
      automerge: {
        markName: "strong",
      },
    },
}

与块一样,一些标记也具有需要转换的属性。 这事是用 attrParsers 键。 与块标记不同的是,块标记不能将复杂内容存储在自动化的标记值上,因此在解析器中,我们通常会转换成或从JSON编码的字符串,比如:

marks: {
    link: {
      automerge: {
        markName: "link",
        parsers: {
          fromAutomerge: (mark: am.MarkValue) => {
            if (typeof mark === "string") {
              try {
                const value = JSON.parse(mark)
                return {
                  href: value.href || "",
                  title: value.title || "",
                }
              } catch (e) {
                console.warn("failed to parse link mark as JSON")
              }
            }
            return {
              href: "",
              title: "",
            }
          },
          fromProsemirror: (mark: Mark) =>
            JSON.stringify({
              href: mark.attrs.href,
              title: mark.attrs.title,
            }),
        },
      },
  }
}

未知块

每个变相器都必须提供某种方式来代表未知的块. 这使得应用程序可以使用不同的方案在同一个文本文档上合作. 未知块将代表我的 node 拥有 automerge.unknownBlock 键集为正则,其中可以包含计划中的任何其他节点.

一个简单的方法是:

nodes: {
  unknownBlock: {
    automerge: {
      unknownBlock: true,
    },
    group: "block",
    content: "block+", // Allow any block content
    parseDOM: [{ tag: "div", attrs: { "data-unknown-block": "true" } }],
    toDOM() {
      return ["div", { "data-unknown-block": "true" }, 0]
    },
  },
}

API 密码

SchemaAdapter

A 类 SchemaAdapter 提供ProseMirror Schema和您在自动合并文档中使用的块标记之间的映射。 这个API要理解的部分是您传递给 SchemaAdapter 建筑师.

一个schema适配器规格是ProseMiror schema的延伸. 它代表着一个物体 nodes 键和 a marks 键,每个键都类似于它们在 ProseMirror schema 中的等同键,但带有几个额外的键.

这个 nodes 值必须是ProseMirror NodeSpec 附加对象 automerge 键,类型如下:

type AutomergeNodeSpec = {
  unknownBlock?: boolean
  block?: BlockMappingSpec
  isEmbed?: boolean
  attrParsers?: {
    fromProsemirror: (node: Node) => { [key: string]: am.MaterializeValue }
    fromAutomerge: (block: BlockMarker) => Attrs
  }
}

type BlockMappingSpec = string | { within: { [key: string]: string } }

type BlockMarker = {
  type: automerge.ImmutableString
  parents: automerge.ImmutableString[]
  attrs: { [key: string]: any }
  isEmbed?: boolean
}

这个 marks 值必须是ProseMirror MarkSpec 附加对象 automerge 键,类型如下:

automerge?: {
    markName: string
    parsers?: {
      fromAutomerge: (value: automerge.MarkValue) => Attrs
      fromProsemirror: (mark: Mark) => automerge.MarkValue
    }
  }

版本与发布记录

@automerge/prosemirror 共发布 18 个版本,首次发布于 2024/03/16;过去 12 个月发布了 1 个版本。

版本发布日期距今
0.2.0latest2026/02/258 个月前
0.2.0-alpha.0next2025/08/071.3 年前
0.1.02025/07/301.3 年前
0.0.132024/10/052 年前
0.0.13-alpha.32024/08/072 年前
0.0.13-alpha.22024/08/012 年前

关于 @automerge/prosemirror 的常见问题

@automerge/prosemirror 是什么?

合作编写紧随 丰富的文本计划 使用ProseMirror. (英语).

如何安装 @automerge/prosemirror?

在项目目录运行 npm install @automerge/prosemirror,或使用 pnpm add / yarn add / bun add。

@automerge/prosemirror 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@automerge/prosemirror 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@automerge/prosemirror 可以商用吗?

@automerge/prosemirror 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@automerge/prosemirror 的下载量有多少?

根据 npm 官方下载统计,@automerge/prosemirror 在 2026/10/07 – 2026/11/05 这 30 天内被下载 32,178 次,日均约 1,073 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@automerge/prosemirror 还在维护吗?

最新版本 0.2.0 发布于 2026/02/25(8 个月前);至今共发布 18 个版本,其中过去 12 个月发布了 1 个;首次发布于 2024/03/16。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。