Files
blockflow-workbench/src/editor/extensions/SelectedBlockExtension.ts
gamewhale 589ff15213 chore: 初始化 BlockFlow Workbench 仓库
建立前端与 Tauri 桌面端的首个版本提交,包含核心编辑器、项目文件读写、测试与构建配置。

补充 Git 忽略规则和换行规范,排除依赖、构建产物、本地运行日志与临时验证文件,方便在其他电脑继续开发。
2026-05-29 17:23:43 +08:00

62 lines
1.9 KiB
TypeScript

import { Extension } from "@tiptap/core"
import { Plugin, PluginKey } from "@tiptap/pm/state"
import { Decoration, DecorationSet } from "@tiptap/pm/view"
import type { Node as ProseMirrorNode } from "@tiptap/pm/model"
export const selectedBlockPluginKey = new PluginKey<DecorationSet>("blockFlowSelectedBlock")
export const SelectedBlockExtension = Extension.create({
name: "blockFlowSelectedBlock",
addStorage() {
return {
selectedBlockId: null as string | null
}
},
addProseMirrorPlugins() {
return [
new Plugin({
key: selectedBlockPluginKey,
state: {
init: (_, state) => buildSelectedBlockDecorations(state.doc, this.storage.selectedBlockId as string | null),
apply: (transaction, previous, _oldState, newState) => {
const selectedBlockId = transaction.getMeta(selectedBlockPluginKey) as string | null | undefined
if (selectedBlockId === undefined && !transaction.docChanged) {
return previous
}
return buildSelectedBlockDecorations(
newState.doc,
selectedBlockId === undefined ? (this.storage.selectedBlockId as string | null) : selectedBlockId
)
}
},
props: {
decorations(state) {
return selectedBlockPluginKey.getState(state)
}
}
})
]
}
})
function buildSelectedBlockDecorations(doc: ProseMirrorNode, selectedBlockId: string | null): DecorationSet {
if (selectedBlockId === null) {
return DecorationSet.empty
}
const decorations: Decoration[] = []
doc.descendants((node, position) => {
if (node.attrs.id === selectedBlockId) {
decorations.push(Decoration.node(position, position + node.nodeSize, { class: "bf-selected-block" }))
return false
}
return true
})
return DecorationSet.create(doc, decorations)
}