tiptap-editor

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Tiptap Editor API Patterns

Tiptap编辑器API模式

Overview

概述

This skill documents proper Tiptap API usage patterns for vmark development. It helps distinguish when to use Tiptap's high-level API vs direct ProseMirror access.
本技能文档记录了vmark开发中正确使用Tiptap API的模式,帮助区分何时使用Tiptap的高级API,何时直接使用ProseMirror。

When to Use Tiptap API

何时使用Tiptap API

Always prefer Tiptap API for:
  • Format commands (bold, italic, underline, etc.)
  • Block type changes (heading, paragraph, code block)
  • List operations (bullet, ordered, toggle, indent/outdent)
  • Table operations via Tiptap table extension
  • Content insertion and replacement
  • Editor state queries (
    isActive
    ,
    getAttributes
    )
Tiptap patterns to use:
typescript
// Direct commands
editor.commands.toggleBold()
editor.commands.setHeading({ level: 2 })
editor.commands.setContent(doc, { emitUpdate: false })

// Chained commands (for multiple operations)
editor.chain().focus().setHeading({ level: 2 }).run()
editor.chain().focus().toggleMark("underline").run()

// State queries
editor.isActive("blockquote")
editor.isActive("heading", { level: 2 })
editor.getAttributes("link")
优先使用Tiptap API的场景:
  • 格式设置命令(加粗、斜体、下划线等)
  • 块类型更改(标题、段落、代码块)
  • 列表操作(无序列表、有序列表、切换、缩进/取消缩进)
  • 通过Tiptap表格扩展进行表格操作
  • 内容插入与替换
  • 编辑器状态查询(
    isActive
    getAttributes
推荐使用的Tiptap模式:
typescript
// Direct commands
editor.commands.toggleBold()
editor.commands.setHeading({ level: 2 })
editor.commands.setContent(doc, { emitUpdate: false })

// Chained commands (for multiple operations)
editor.chain().focus().setHeading({ level: 2 }).run()
editor.chain().focus().toggleMark("underline").run()

// State queries
editor.isActive("blockquote")
editor.isActive("heading", { level: 2 })
editor.getAttributes("link")

When Direct ProseMirror is Appropriate

何时适合直接使用ProseMirror

Use ProseMirror directly for:
  • Markdown conversion layer (
    proseMirrorToMdast.ts
    ,
    mdastToProseMirror.ts
    )
  • Multi-cursor/selection subclassing (
    MultiSelection.ts
    )
  • Custom node views
  • Low-level transaction manipulation
  • Schema-level operations
直接使用ProseMirror的场景:
  • Markdown转换层(
    proseMirrorToMdast.ts
    mdastToProseMirror.ts
  • 多光标/选区子类化(
    MultiSelection.ts
  • 自定义节点视图
  • 底层事务操作
  • 模式级操作

Known Issues in vmark

vmark中的已知问题

1. cursorHandlers.ts Block Boundary Issue

1. cursorHandlers.ts块边界问题

src/hooks/mcpBridge/cursorHandlers.ts
uses
doc.textContent
which flattens the document and loses block boundaries. The correct approach is to use
$pos
helpers:
typescript
// WRONG - loses block structure
const text = doc.textContent;

// RIGHT - respects block boundaries
const $pos = doc.resolve(from);
const currentNode = $pos.parent;
const blockStart = $pos.before($pos.depth);
const blockEnd = $pos.after($pos.depth);
src/hooks/mcpBridge/cursorHandlers.ts
中使用了
doc.textContent
,会扁平化文档并丢失块边界。正确的做法是使用
$pos
辅助方法:
typescript
// WRONG - loses block structure
const text = doc.textContent;

// RIGHT - respects block boundaries
const $pos = doc.resolve(from);
const currentNode = $pos.parent;
const blockStart = $pos.before($pos.depth);
const blockEnd = $pos.after($pos.depth);

2. Cursor Sync Drift After WYSIWYG Edits

2. WYSIWYG编辑后光标同步偏移

sourceLine
attributes are only set on initial parse. After WYSIWYG edits that add/remove blocks, line numbers no longer match the source. This is a known limitation.
sourceLine
属性仅在初始解析时设置。在WYSIWYG编辑添加/删除块后,行号与源内容不再匹配。这是一个已知限制。

3. HtmlNodeView.ts Store Issue

3. HtmlNodeView.ts存储问题

src/plugins/markdownArtifacts/HtmlNodeView.ts
writes cursor info to wrong store.
src/plugins/markdownArtifacts/HtmlNodeView.ts
将光标信息写入了错误的存储位置。

References

参考资料

  • references/patterns.md
    - Detailed API patterns and $pos usage
  • references/examples.md
    - Real code examples from vmark codebase
  • references/patterns.md
    - 详细的API模式和$pos使用方法
  • references/examples.md
    - 来自vmark代码库的真实代码示例

Workflow

工作流程

  1. Identify operation type (format, block, selection, traversal)
  2. Check if Tiptap has a built-in command for it
  3. Use
    editor.commands.xxx()
    for single operations
  4. Use
    editor.chain().focus().xxx().run()
    when focus is needed or chaining
  5. For node traversal, use
    doc.resolve(pos)
    to get
    $pos
    helpers
  6. For state queries, use
    editor.isActive()
    or
    editor.getAttributes()
  1. 确定操作类型(格式设置、块操作、选区、遍历)
  2. 检查Tiptap是否有内置命令
  3. 单个操作使用
    editor.commands.xxx()
  4. 需要获取焦点或链式操作时,使用
    editor.chain().focus().xxx().run()
  5. 节点遍历使用
    doc.resolve(pos)
    获取
    $pos
    辅助方法
  6. 状态查询使用
    editor.isActive()
    editor.getAttributes()