mirror of
https://github.com/colanode/colanode.git
synced 2026-02-24 11:59:53 +01:00
Improve markdown parser for editor
This commit is contained in:
30
package-lock.json
generated
30
package-lock.json
generated
@@ -13918,6 +13918,23 @@
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/markdown": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/markdown/-/markdown-3.15.3.tgz",
|
||||
"integrity": "sha512-JjjZ/X7H2+/Jeapk8GurbncJVyG9ai5YD/eJLBKDyWqsoQwsrHbDlYBx26q9J5VwWXzxe9G6fmHNlAfw0Pokow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"marked": "^15.0.12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "3.15.3",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.15.3.tgz",
|
||||
@@ -23969,6 +23986,18 @@
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "15.0.12",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
|
||||
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/marky": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz",
|
||||
@@ -32679,6 +32708,7 @@
|
||||
"@tiptap/extension-text": "^3.15.3",
|
||||
"@tiptap/extension-underline": "^3.15.3",
|
||||
"@tiptap/extensions": "^3.15.3",
|
||||
"@tiptap/markdown": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3",
|
||||
"@tiptap/react": "^3.15.3",
|
||||
"@tiptap/suggestion": "^3.15.3",
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
"@tiptap/extension-text": "^3.15.3",
|
||||
"@tiptap/extension-underline": "^3.15.3",
|
||||
"@tiptap/extensions": "^3.15.3",
|
||||
"@tiptap/markdown": "^3.15.3",
|
||||
"@tiptap/pm": "^3.15.3",
|
||||
"@tiptap/react": "^3.15.3",
|
||||
"@tiptap/suggestion": "^3.15.3",
|
||||
|
||||
@@ -83,6 +83,8 @@ import {
|
||||
DatabaseNode,
|
||||
AutoJoiner,
|
||||
HardBreakNode,
|
||||
ParserExtension,
|
||||
Markdown,
|
||||
} from '@colanode/ui/editor/extensions';
|
||||
import { ToolbarMenu, ActionMenu } from '@colanode/ui/editor/menus';
|
||||
|
||||
@@ -170,6 +172,8 @@ export const DocumentEditor = ({
|
||||
{
|
||||
extensions: [
|
||||
IdExtension,
|
||||
ParserExtension,
|
||||
Markdown,
|
||||
DocumentNode,
|
||||
PageNode,
|
||||
FolderNode,
|
||||
|
||||
@@ -27,11 +27,13 @@ import { IdExtension } from '@colanode/ui/editor/extensions/id';
|
||||
import { LinkMark } from '@colanode/ui/editor/extensions/link';
|
||||
import { ListItemNode } from '@colanode/ui/editor/extensions/list-item';
|
||||
import { ListKeymapExtension } from '@colanode/ui/editor/extensions/list-keymap';
|
||||
import { Markdown } from '@colanode/ui/editor/extensions/markdown';
|
||||
import { MentionExtension } from '@colanode/ui/editor/extensions/mention';
|
||||
import { MessageNode } from '@colanode/ui/editor/extensions/message';
|
||||
import { OrderedListNode } from '@colanode/ui/editor/extensions/ordered-list';
|
||||
import { PageNode } from '@colanode/ui/editor/extensions/page';
|
||||
import { ParagraphNode } from '@colanode/ui/editor/extensions/paragraph';
|
||||
import { ParserExtension } from '@colanode/ui/editor/extensions/parser';
|
||||
import { PlaceholderExtension } from '@colanode/ui/editor/extensions/placeholder';
|
||||
import { TabKeymapExtension } from '@colanode/ui/editor/extensions/tab-keymap';
|
||||
import { TableNode } from '@colanode/ui/editor/extensions/table';
|
||||
@@ -87,4 +89,6 @@ export {
|
||||
AutoJoiner,
|
||||
MentionExtension,
|
||||
HardBreakNode,
|
||||
ParserExtension,
|
||||
Markdown,
|
||||
};
|
||||
|
||||
3
packages/ui/src/editor/extensions/markdown.tsx
Normal file
3
packages/ui/src/editor/extensions/markdown.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Markdown } from '@tiptap/markdown';
|
||||
|
||||
export { Markdown };
|
||||
89
packages/ui/src/editor/extensions/parser.tsx
Normal file
89
packages/ui/src/editor/extensions/parser.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state';
|
||||
import { Extension, JSONContent } from '@tiptap/react';
|
||||
|
||||
export const ParserExtension = Extension.create({
|
||||
name: 'parser',
|
||||
addOptions() {
|
||||
return {
|
||||
nodes: [],
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const editor = this.editor;
|
||||
const markdown = editor.markdown;
|
||||
|
||||
if (!markdown) {
|
||||
console.error('Markdown extension not found');
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('parser'),
|
||||
props: {
|
||||
handlePaste(view, event) {
|
||||
const html = event.clipboardData?.getData('text/html');
|
||||
if (html) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const text = event.clipboardData?.getData('text/plain');
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parsedContent = markdown.parse(text);
|
||||
if (!parsedContent || !parsedContent.content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const blocks = parsedContent.content;
|
||||
if (!blocks || blocks.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const normalizedBlocks = normalizeHeadings(blocks);
|
||||
editor.commands.insertContent(normalizedBlocks);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
const normalizeHeadings = (content: JSONContent[]): JSONContent[] => {
|
||||
return content.map(normalizeNode);
|
||||
};
|
||||
|
||||
const normalizeNode = (node: JSONContent): JSONContent => {
|
||||
if (node.type === 'heading' && node.attrs?.level) {
|
||||
const level = node.attrs.level;
|
||||
let newType: string;
|
||||
|
||||
if (level === 1) {
|
||||
newType = 'heading1';
|
||||
} else if (level === 2) {
|
||||
newType = 'heading2';
|
||||
} else {
|
||||
newType = 'heading3';
|
||||
}
|
||||
|
||||
const { level: _, ...restAttrs } = node.attrs;
|
||||
return {
|
||||
...node,
|
||||
type: newType,
|
||||
attrs: Object.keys(restAttrs).length > 0 ? restAttrs : undefined,
|
||||
content: node.content ? node.content.map(normalizeNode) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (node.content) {
|
||||
return {
|
||||
...node,
|
||||
content: node.content.map(normalizeNode),
|
||||
};
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
Reference in New Issue
Block a user