diff --git a/packages/client/src/tiptap/core/extensions/excalidraw.ts b/packages/client/src/tiptap/core/extensions/excalidraw.ts index e5b227f1..030d9bbe 100644 --- a/packages/client/src/tiptap/core/extensions/excalidraw.ts +++ b/packages/client/src/tiptap/core/extensions/excalidraw.ts @@ -2,7 +2,7 @@ import { IUser } from '@think/domains'; import { mergeAttributes, Node, nodeInputRule } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; import { ExcalidrawWrapper } from 'tiptap/core/wrappers/excalidraw'; -import { getDatasetAttribute } from 'tiptap/prose-utils'; +import { getDatasetAttribute, nodeAttrsToDataset } from 'tiptap/prose-utils'; const DEFAULT_MIND_DATA = { elements: [] }; @@ -56,7 +56,7 @@ export const Excalidraw = Node.create({ addOptions() { return { HTMLAttributes: { - class: 'mind', + class: 'excalidraw', }, }; }, @@ -64,13 +64,13 @@ export const Excalidraw = Node.create({ parseHTML() { return [ { - tag: 'div[class=mind]', + tag: 'div[class=excalidraw]', }, ]; }, - renderHTML({ HTMLAttributes }) { - return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; + renderHTML({ HTMLAttributes, node }) { + return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, nodeAttrsToDataset(node))]; }, addCommands() { diff --git a/packages/client/src/tiptap/core/extensions/mind.ts b/packages/client/src/tiptap/core/extensions/mind.ts index a1e03868..2dfc47eb 100644 --- a/packages/client/src/tiptap/core/extensions/mind.ts +++ b/packages/client/src/tiptap/core/extensions/mind.ts @@ -2,7 +2,7 @@ import { IUser } from '@think/domains'; import { mergeAttributes, Node, nodeInputRule } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; import { MindWrapper } from 'tiptap/core/wrappers/mind'; -import { getDatasetAttribute } from 'tiptap/prose-utils'; +import { getDatasetAttribute, nodeAttrsToDataset } from 'tiptap/prose-utils'; const DEFAULT_MIND_DATA = { root: { data: { text: '中心节点' }, children: [] }, @@ -77,8 +77,8 @@ export const Mind = Node.create({ ]; }, - renderHTML({ HTMLAttributes }) { - return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; + renderHTML({ HTMLAttributes, node }) { + return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, nodeAttrsToDataset(node))]; }, addCommands() { diff --git a/packages/client/src/tiptap/core/extensions/paste.ts b/packages/client/src/tiptap/core/extensions/paste.ts index 2109bb26..db1bcbab 100644 --- a/packages/client/src/tiptap/core/extensions/paste.ts +++ b/packages/client/src/tiptap/core/extensions/paste.ts @@ -23,6 +23,11 @@ interface IPasteOptions { */ htmlToProsemirror: (arg: { schema: Schema; html: string; needTitle: boolean; defaultTitle?: string }) => Node; + /** + * 将 markdown 转换为 html + */ + markdownToHTML: (arg: string) => string; + /** * 将 markdown 转换为 prosemirror 节点 */ @@ -41,6 +46,7 @@ export const Paste = Extension.create({ addOptions() { return { htmlToProsemirror: (arg) => '', + markdownToHTML: (arg) => arg, markdownToProsemirror: (arg) => arg.content, prosemirrorToMarkdown: (arg) => String(arg.content), }; diff --git a/packages/client/src/tiptap/editor/collaboration/kit.ts b/packages/client/src/tiptap/editor/collaboration/kit.ts index b5b52a43..bf3d4144 100644 --- a/packages/client/src/tiptap/editor/collaboration/kit.ts +++ b/packages/client/src/tiptap/editor/collaboration/kit.ts @@ -66,7 +66,7 @@ import { TrailingNode } from 'tiptap/core/extensions/trailing-node'; import { Underline } from 'tiptap/core/extensions/underline'; // markdown 支持 import { htmlToProsemirror } from 'tiptap/markdown/html-to-prosemirror'; -import { markdownToProsemirror } from 'tiptap/markdown/markdown-to-prosemirror'; +import { markdownToHTML, markdownToProsemirror } from 'tiptap/markdown/markdown-to-prosemirror'; import { prosemirrorToMarkdown } from 'tiptap/markdown/prosemirror-to-markdown'; const DocumentWithTitle = Document.extend({ @@ -137,6 +137,7 @@ export const CollaborationKit = [ Underline, Paste.configure({ htmlToProsemirror, + markdownToHTML, markdownToProsemirror, prosemirrorToMarkdown, }), diff --git a/packages/client/src/tiptap/markdown/markdown-to-prosemirror/html-to-prosemirror/nodes/excalidraw.ts b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/html-to-prosemirror/nodes/excalidraw.ts new file mode 100644 index 00000000..e79ead58 --- /dev/null +++ b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/html-to-prosemirror/nodes/excalidraw.ts @@ -0,0 +1,9 @@ +import { Node } from './node'; + +export class Excalidraw extends Node { + type = 'excalidraw'; + + matching() { + return this.DOMNode.nodeName === 'DIV' && this.DOMNode.classList.contains('excalidraw'); + } +} diff --git a/packages/client/src/tiptap/markdown/markdown-to-prosemirror/html-to-prosemirror/renderer.ts b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/html-to-prosemirror/renderer.ts index 0941c54d..e683ca9a 100644 --- a/packages/client/src/tiptap/markdown/markdown-to-prosemirror/html-to-prosemirror/renderer.ts +++ b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/html-to-prosemirror/renderer.ts @@ -17,6 +17,7 @@ import { CodeBlockWrapper } from './nodes/code-block-wrapper'; import { Countdown } from './nodes/countdown'; import { DocumentChildren } from './nodes/document-children'; import { DocumentReference } from './nodes/document-reference'; +import { Excalidraw } from './nodes/excalidraw'; import { Flow } from './nodes/flow'; import { HardBreak } from './nodes/hard-break'; import { Heading } from './nodes/heading'; @@ -57,6 +58,7 @@ export class Renderer { Attachment, Countdown, Callout, + Excalidraw, Iframe, Status, Mention, diff --git a/packages/client/src/tiptap/markdown/markdown-to-prosemirror/index.tsx b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/index.tsx index 40a094ea..18845670 100644 --- a/packages/client/src/tiptap/markdown/markdown-to-prosemirror/index.tsx +++ b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/index.tsx @@ -37,3 +37,5 @@ export const markdownToProsemirror = ({ schema, content, needTitle, defaultTitle return node; }; + +export { markdownToHTML }; diff --git a/packages/client/src/tiptap/markdown/markdown-to-prosemirror/markdown-to-html/index.ts b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/markdown-to-html/index.ts index 23987085..d2f5a1fd 100644 --- a/packages/client/src/tiptap/markdown/markdown-to-prosemirror/markdown-to-html/index.ts +++ b/packages/client/src/tiptap/markdown/markdown-to-prosemirror/markdown-to-html/index.ts @@ -21,6 +21,7 @@ const markdownDocumentChildren = createMarkdownContainer('documentChildren'); const markdownIframe = createMarkdownContainer('iframe'); const markdownMention = createMarkdownContainer('mention'); const markdownMind = createMarkdownContainer('mind'); +const markdownExcalidraw = createMarkdownContainer('excalidraw'); const markdownFlow = createMarkdownContainer('flow'); const markdownTableOfContents = createMarkdownContainer('tableOfContents'); const markdownTitle = createMarkdownContainer('title'); @@ -44,6 +45,7 @@ const markdown = markdownit('commonmark') .use(markdownStatus) .use(markdownMention) .use(markdownMind) + .use(markdownExcalidraw) .use(markdownDocumentReference) .use(markdownDocumentChildren) .use(markdownFlow) diff --git a/packages/client/src/tiptap/markdown/prosemirror-to-markdown/index.ts b/packages/client/src/tiptap/markdown/prosemirror-to-markdown/index.ts index 06b719a7..ab19f90e 100644 --- a/packages/client/src/tiptap/markdown/prosemirror-to-markdown/index.ts +++ b/packages/client/src/tiptap/markdown/prosemirror-to-markdown/index.ts @@ -9,6 +9,7 @@ import { CodeBlock } from 'tiptap/core/extensions/code-block'; import { Countdown } from 'tiptap/core/extensions/countdown'; import { DocumentChildren } from 'tiptap/core/extensions/document-children'; import { DocumentReference } from 'tiptap/core/extensions/document-reference'; +import { Excalidraw } from 'tiptap/core/extensions/excalidraw'; import { Flow } from 'tiptap/core/extensions/flow'; import { HardBreak } from 'tiptap/core/extensions/hard-break'; import { Heading } from 'tiptap/core/extensions/heading'; @@ -127,6 +128,7 @@ const SerializerConfig = { [Countdown.name]: renderCustomContainer('countdown'), [DocumentChildren.name]: renderCustomContainer('documentChildren'), [DocumentReference.name]: renderCustomContainer('documentReference'), + [Excalidraw.name]: renderCustomContainer('excalidraw'), [Flow.name]: renderCustomContainer('flow'), [HardBreak.name]: renderHardBreak, [Heading.name]: defaultMarkdownSerializer.nodes.heading, diff --git a/packages/client/src/tiptap/prose-utils/dom-dataset.ts b/packages/client/src/tiptap/prose-utils/dom-dataset.ts index 18b73016..87582c10 100644 --- a/packages/client/src/tiptap/prose-utils/dom-dataset.ts +++ b/packages/client/src/tiptap/prose-utils/dom-dataset.ts @@ -1,4 +1,5 @@ import { safeJSONParse } from 'helpers/json'; +import { Node } from 'prosemirror-state'; /** * 将 JSON 转为字符串 @@ -89,3 +90,32 @@ export const getDatasetAttribute = const toNumber = parseInt(value); return toNumber !== toNumber ? value : toNumber; // 避免 NaN }; + +/** + * 将节点属性转换为 dataset + * @param node + * @returns + */ +export const nodeAttrsToDataset = (node: Node) => { + const { attrs } = node; + + return Object.keys(attrs).reduce((accu, key) => { + const value = attrs[key]; + + if (value == null) { + return accu; + } + + let encodeValue = ''; + + if (typeof value === 'object') { + encodeValue = jsonToStr(value); + } else { + encodeValue = value; + } + + accu[key] = encodeValue; + + return accu; + }, Object.create(null)); +};