feat(lesson-preparation): Tiptap AnchorMark + AnchorPoint 扩展

This commit is contained in:
SpecialX
2026-07-04 11:18:56 +08:00
parent ccb2cf05df
commit 5c4db2fedc

View File

@@ -0,0 +1,133 @@
import { Mark, mergeAttributes } from "@tiptap/core";
import { Node } from "@tiptap/core";
/**
* V4 锚点系统:用 Tiptap Mark 内嵌锚点,替代 v3 的字符串偏移。
*
* - AnchorMarkrange 锚点):包裹选中文本,渲染为高亮 + 底部细线 + 行内标签
* - AnchorPointpoint 锚点):独立 Node渲染为黑色小圆圈带数字
*
* 锚点数据存储在 Mark/Node 的 attributes 中,编辑时自动跟随文本移动。
*/
export interface AnchorMarkAttributes {
anchorId: string;
nodeId: string;
type: "range";
}
export const AnchorMark = Mark.create({
name: "anchor",
addAttributes() {
return {
anchorId: {
default: null,
parseHTML: (el) => el.getAttribute("data-anchor-id"),
renderHTML: (attrs) => ({
"data-anchor-id": attrs.anchorId,
}),
},
nodeId: {
default: null,
parseHTML: (el) => el.getAttribute("data-node-id"),
renderHTML: (attrs) => ({
"data-node-id": attrs.nodeId,
}),
},
type: {
default: "range",
parseHTML: (el) => el.getAttribute("data-anchor-type") ?? "range",
renderHTML: (attrs) => ({
"data-anchor-type": attrs.type,
}),
},
};
},
parseHTML() {
return [
{
tag: "span[data-anchor-id]",
},
];
},
renderHTML({ HTMLAttributes }) {
return [
"span",
mergeAttributes(HTMLAttributes, {
class: "lp-anchor-range",
}),
];
},
// 不自动添加任何快捷键
addKeyboardShortcuts() {
return {};
},
});
/**
* AnchorPoint点锚点作为独立 Node 插入文本流。
* 渲染为带数字的黑色小圆圈。
*/
export const AnchorPoint = Node.create({
name: "anchorPoint",
group: "inline",
inline: true,
atom: true,
addAttributes() {
return {
anchorId: {
default: null,
parseHTML: (el) => el.getAttribute("data-anchor-id"),
renderHTML: (attrs) => ({
"data-anchor-id": attrs.anchorId,
}),
},
nodeId: {
default: null,
parseHTML: (el) => el.getAttribute("data-node-id"),
renderHTML: (attrs) => ({
"data-node-id": attrs.nodeId,
}),
},
label: {
default: "•",
parseHTML: (el) => el.getAttribute("data-label") ?? "•",
renderHTML: (attrs) => ({
"data-label": attrs.label,
}),
},
};
},
parseHTML() {
return [
{
tag: "span[data-anchor-point]",
},
];
},
renderHTML({ HTMLAttributes }) {
return [
"span",
mergeAttributes(HTMLAttributes, {
"data-anchor-point": "true",
class: "lp-anchor-point",
contenteditable: "false",
}),
HTMLAttributes.label ?? "•",
];
},
addKeyboardShortcuts() {
return {};
},
});