ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

The lunatic is on the grass

The lunatic is on the grass The lunatic is on the grass【免费下载链接】milkdown Plugin driven WYSIWYG markdown editor framework.项目地址: https://gitcode.com/GitHub_Trending/mi/milkdownThe lunatic is on the grassThe lunatic is on the grassThe lunatic is on the grass该文件覆盖了 Markdown 最常用的四级标题# 到 ####对应 HTML 的 h1 到 h4并且刻意让每级标题使用**相同的文本内容** The lunatic is on the grass。这种设计并非随意为之它让测试能够在“文本内容完全一致”的前提下专注于验证标题**级别level属性**、**DOM 标签h1/h2/h3/h4**以及**标题 id 去重机制**的行为差异从而精确暴露标题节点独有的处理逻辑。 需要说明的是e2e/tests/data 下的夹具大多由测试用例通过 loadFixture 统一加载见 [e2e/tests/misc/index.ts](https://link.gitcode.com/i/a74c742b469094e0e57409df487c53b9)而 heading.md 同时被输入类、快捷键类测试直接引用是理解 Milkdown 标题节点行为的核心数据源。 ## Markdown 解析阶段标题如何进入编辑器 Milkdown 通过 milkdown/transformer 将 Markdown 文本解析为 ProseMirror 文档。标题的解析入口位于 preset-commonmark 的 heading 节点定义 [packages/plugins/preset-commonmark/src/node/heading.ts](https://link.gitcode.com/i/a12110c4b3c2af7092be5cf5cddc6e64) ts parseMarkdown: { match: ({ type }) type heading, runner: (state, node, type) { const depth node.depth as number state.openNode(type, { level: depth }) state.next(node.children) state.closeNode() }, },解析逻辑非常直白当 Markdown 抽象语法树中出现heading类型节点时直接取其depth即#的个数作为 ProseMirror 节点属性level然后递归处理子节点标题内的行内内容如加粗、斜体、链接等最后关闭节点。对应的序列化方向同样在同一个文件中toMarkdown: { match: (node) node.type.name heading, runner: (state, node) { state.openNode(heading, undefined, { depth: node.attrs.level }) serializeText(state, node) state.closeNode() }, },toMarkdown将编辑器内的 heading 节点重新写回 Markdown以node.attrs.level作为depth再序列化标题的行内文本。这样# The lunatic is on the grass在“Markdown → 编辑器 DOM → Markdown”的往返过程中可以保持语义一致。Heading 节点 Schemalevel 与 id 双属性建模标题节点的 ProseMirror Schema 定义在同一文件 heading.ts其核心结构如下export const headingSchema $nodeSchema(heading, (ctx) { const getId ctx.get(headingIdGenerator.key) return { content: inline*, group: block, defining: true, attrs: { id: { default: , validate: string }, level: { default: 1, validate: number }, }, parseDOM: headingIndex.map((x) ({ tag: h${x}, getAttrs: (node) ({ level: x, id: node.id }), })), toDOM: (node) [ h${node.attrs.level}, { ...ctx.get(headingAttr.key)(node), id: node.attrs.id || getId(node) }, 0, ], } })值得注意的细节content: inline*标题内只允许行内内容文本、强调、代码、链接等不允许嵌套块级节点defining: true声明标题是“定义性”块在块删除/提升等操作中会保留其结构边界level标题级别16对应h1h6id标题锚点 idtoDOM中当id为空时会调用getId(node)自动生成详见下文。parseDOM由headingIndex动态生成Array(6).fill(0).map((_, i) i 1)即同时注册h1h6六个 HTML 标签的解析规则粘贴 HTML 内容时也会被识别为标题节点。标题与段落等节点一起在 composed/schema.ts 中被注册进preset-commonmark的 schema 组合中。标题 id 的生成与去重机制标题 id 是 Milkdown 标题节点的一大特色默认根据文本内容生成 slug 形式的锚点 id。生成器通过$ctx暴露可在初始化编辑器时自定义function defaultHeadingIdGenerator(node: Node) { return node.textContent.toLowerCase().trim().replace(/\s/g, -) } export const headingIdGenerator $ctx( defaultHeadingIdGenerator, headingIdGenerator )默认规则为取标题文本 → 转小写 → 去首尾空白 → 将连续空白替换为-。例如# The lunatic is on the grass会生成the-lunatic-is-on-the-grass。重复 id 的处理由syncHeadingIdPlugin完成它被注册在 composed/plugins.ts 中。其行为在 e2e/tests/input/heading.spec.ts 中有精确断言# Heading1 → id: heading1 # Heading1 → id: heading1-#2 ## Heading1 → id: heading1-#3即当出现同文本的重复标题时后续标题会在基础 id 后追加-#2、-#3等后缀以保证唯一性。这意味着即使标题文本完全相同每个标题仍拥有可稳定引用的锚点 id——这正是 heading.md 夹具让四行标题文本全部相同的原因之一它便于在“文本相同”的前提下单独验证 id 去重逻辑。输入规则输入#即可生成标题wrapInHeadingInputRule提供了 Markdown 风格的快捷输入在行首输入若干#并跟一个空格当前块即被转换为对应级别的标题heading.tstextblockTypeInputRule( /^(?hashes#)\s$/, headingSchema.type(ctx), (match) { const x match.groups?.hashes?.length || 0 const view ctx.get(editorViewCtx) const { $from } view.state.selection const node $from.node() if (node.type.name heading) { let level Number(node.attrs.level) Number(x) if (level 6) level 6 return { level } } return { level: x } } )【免费下载链接】milkdown Plugin driven WYSIWYG markdown editor framework.项目地址: https://gitcode.com/GitHub_Trending/mi/milkdown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表