diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000000..7323d92aff --- /dev/null +++ b/TODO.md @@ -0,0 +1,4 @@ + +## Milkdown Editor Known Issue + +1. GitHub Alerts syntax will escape char `[` and broken it render logic. \ No newline at end of file diff --git a/package.json b/package.json index 420c98b0e4..ce1634cb72 100644 --- a/package.json +++ b/package.json @@ -41,14 +41,19 @@ "@excalidraw/excalidraw": "0.17.2", "@floating-ui/react-dom": "2.0.8", "@milkdown/core": "7.3.3", + "@milkdown/ctx": "7.3.3", "@milkdown/plugin-clipboard": "7.3.3", + "@milkdown/plugin-diagram": "^7.3.3", "@milkdown/plugin-history": "7.3.3", "@milkdown/plugin-indent": "7.3.3", "@milkdown/plugin-listener": "7.3.3", "@milkdown/preset-commonmark": "7.3.3", + "@milkdown/preset-gfm": "7.3.3", + "@milkdown/prose": "7.3.3", "@milkdown/react": "7.3.3", "@milkdown/utils": "7.3.3", "@mx-space/api-client": "1.7.2", + "@prosemirror-adapter/react": "0.2.6", "@radix-ui/react-dialog": "1.0.5", "@radix-ui/react-label": "2.0.2", "@radix-ui/react-scroll-area": "1.0.5", @@ -97,12 +102,16 @@ "react-toastify": "10.0.4", "react-tweet": "3.2.0", "react-wrap-balancer": "1.1.0", + "remark-directive": "3.0.0", + "remark-github-alerts": "^0.0.4", "remove-markdown": "0.5.0", "server-only": "^0.0.1", "socket.io-client": "4.7.4", "tailwind-merge": "2.2.1", "unidata.js": "0.8.0", + "unified": "^11.0.4", "uniqolor": "1.1.1", + "unist-util-visit": "5.0.0", "use-context-selector": "1.4.1", "vaul": "0.8.9", "xss": "1.0.14" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b877edfd34..68c2390cf3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,9 +32,15 @@ dependencies: '@milkdown/core': specifier: 7.3.3 version: 7.3.3(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@milkdown/ctx': + specifier: 7.3.3 + version: 7.3.3 '@milkdown/plugin-clipboard': specifier: 7.3.3 version: 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@milkdown/plugin-diagram': + specifier: ^7.3.3 + version: 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) '@milkdown/plugin-history': specifier: 7.3.3 version: 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) @@ -47,6 +53,12 @@ dependencies: '@milkdown/preset-commonmark': specifier: 7.3.3 version: 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@milkdown/preset-gfm': + specifier: 7.3.3 + version: 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/preset-commonmark@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@milkdown/prose': + specifier: 7.3.3 + version: 7.3.3 '@milkdown/react': specifier: 7.3.3 version: 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3)(react-dom@18.2.0)(react@18.2.0) @@ -56,6 +68,9 @@ dependencies: '@mx-space/api-client': specifier: 1.7.2 version: 1.7.2 + '@prosemirror-adapter/react': + specifier: 0.2.6 + version: 0.2.6(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: 1.0.5 version: 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0) @@ -200,6 +215,12 @@ dependencies: react-wrap-balancer: specifier: 1.1.0 version: 1.1.0(react@18.2.0) + remark-directive: + specifier: 3.0.0 + version: 3.0.0 + remark-github-alerts: + specifier: ^0.0.4 + version: 0.0.4(@types/mdast@4.0.3)(unified@11.0.4) remove-markdown: specifier: 0.5.0 version: 0.5.0 @@ -215,9 +236,15 @@ dependencies: unidata.js: specifier: 0.8.0 version: 0.8.0(typescript@5.3.3) + unified: + specifier: ^11.0.4 + version: 11.0.4 uniqolor: specifier: 1.1.1 version: 1.1.1 + unist-util-visit: + specifier: 5.0.0 + version: 5.0.0 use-context-selector: specifier: 1.4.1 version: 1.4.1(react-dom@18.2.0)(react@18.2.0)(scheduler@0.23.0) @@ -1666,6 +1693,29 @@ packages: - '@milkdown/transformer' dev: false + /@milkdown/plugin-diagram@7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3): + resolution: {integrity: sha512-MFTs+5YcW2wol+AiQXGM1IqTLZDj8H7V1rElGGaxMDtctDm2t8P6Pe25prXB7mxosPndrx60/fSHTAO+YU09pg==} + peerDependencies: + '@milkdown/core': ^7.2.0 + '@milkdown/ctx': ^7.2.0 + '@milkdown/prose': ^7.2.0 + '@milkdown/transformer': ^7.2.0 + dependencies: + '@milkdown/core': 7.3.3(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@milkdown/ctx': 7.3.3 + '@milkdown/exception': 7.3.3 + '@milkdown/prose': 7.3.3 + '@milkdown/transformer': 7.3.3(@milkdown/prose@7.3.3) + '@milkdown/utils': 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@types/dompurify': 3.0.5 + mermaid: 10.7.0 + nanoid: 5.0.4 + tslib: 2.6.2 + unist-util-visit: 5.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /@milkdown/plugin-history@7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3): resolution: {integrity: sha512-f3lokMDzZUIiNwIZ4V5LI6gnFgjgncl6CdgzWimNhU5ar9ptAYjn3P224UDUGRg+QUjqI9o9oX+68JNy3KC5vw==} peerDependencies: @@ -1736,6 +1786,28 @@ packages: unist-util-visit: 5.0.0 dev: false + /@milkdown/preset-gfm@7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/preset-commonmark@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3): + resolution: {integrity: sha512-n5hkuSEPcAe3jAVcvs7gqOgHLR3OEW6+vLfapF4IgD44pF+U9eBLTrmcysVMLRzxXNzzbCqlgofkoye3XVsgew==} + peerDependencies: + '@milkdown/core': ^7.2.0 + '@milkdown/ctx': ^7.2.0 + '@milkdown/preset-commonmark': ^7.2.0 + '@milkdown/prose': ^7.2.0 + '@milkdown/transformer': ^7.2.0 + dependencies: + '@milkdown/core': 7.3.3(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@milkdown/ctx': 7.3.3 + '@milkdown/exception': 7.3.3 + '@milkdown/preset-commonmark': 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + '@milkdown/prose': 7.3.3 + '@milkdown/transformer': 7.3.3(@milkdown/prose@7.3.3) + '@milkdown/utils': 7.3.3(@milkdown/core@7.3.3)(@milkdown/ctx@7.3.3)(@milkdown/prose@7.3.3)(@milkdown/transformer@7.3.3) + remark-gfm: 4.0.0 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + dev: false + /@milkdown/prose@7.3.3: resolution: {integrity: sha512-d+Dv2C7cSWBfkG3scLnc+6IA40nKNIJM37NFBQ6mbOW7/6dj1m5vhNGZN+LDKlEEYOhVko+Kr6vwI4FfuY+pUw==} dependencies: @@ -2092,6 +2164,25 @@ packages: resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} dev: true + /@prosemirror-adapter/core@0.2.6: + resolution: {integrity: sha512-7GXWQBR/bd6ngvek8SgtlEVC0x3KL7LKZ5cxyZl4IBAlRDbITQWsuKLChEXNan9A6+9Ry4a46crbDGTQlzooOg==} + dependencies: + tslib: 2.6.2 + dev: false + + /@prosemirror-adapter/react@0.2.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-JdDqAJV7FnRyIlnXPP+E0Iu80BQPvHeB+7c0ZfdcHLfVb2BLTNHk6iOoqJmBJmqqHt/KjxXXTmxRUS4EEsjVlg==} + peerDependencies: + react: '*' + react-dom: '*' + dependencies: + '@prosemirror-adapter/core': 0.2.6 + nanoid: 4.0.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tslib: 2.6.2 + dev: false + /@radix-ui/number@1.0.1: resolution: {integrity: sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==} dependencies: @@ -2889,6 +2980,12 @@ packages: '@types/ms': 0.7.34 dev: false + /@types/dompurify@3.0.5: + resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==} + dependencies: + '@types/trusted-types': 2.0.7 + dev: false + /@types/express-serve-static-core@4.17.41: resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} dependencies: @@ -3089,6 +3186,10 @@ packages: '@types/node': 20.11.10 dev: false + /@types/trusted-types@2.0.7: + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + dev: false + /@types/unist@2.0.10: resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} dev: false @@ -3734,6 +3835,10 @@ packages: /caniuse-lite@1.0.30001579: resolution: {integrity: sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==} + /ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + dev: false + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3755,10 +3860,22 @@ packages: engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} dev: true + /character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + dev: false + + /character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + dev: false + /character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} dev: false + /character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + dev: false + /charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} dev: false @@ -5514,6 +5631,17 @@ packages: loose-envify: 1.4.0 dev: false + /is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + dev: false + + /is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + dev: false + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -5576,6 +5704,10 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + dev: false + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -5618,6 +5750,10 @@ packages: is-extglob: 2.1.1 dev: true + /is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + dev: false + /is-map@2.0.2: resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} dev: true @@ -6165,6 +6301,10 @@ packages: resolution: {integrity: sha512-Trz4v0+XWlwy68LJIyw3bLbsJiC8XAbRCKF9DbEtZjyndKOGVx6n+wNB0VfoRmY2LKboQLeniap3xrb6LGSJ8A==} dev: false + /markdown-table@3.0.3: + resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} + dev: false + /marked@11.2.0: resolution: {integrity: sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==} engines: {node: '>= 18'} @@ -6187,6 +6327,30 @@ packages: unist-util-visit: 5.0.0 dev: false + /mdast-util-directive@3.0.0: + resolution: {integrity: sha512-JUpYOqKI4mM3sZcNxmF/ox04XYFFkNwr0CFlrQIkCwbvH0xzMCqkMqAde9wRd80VAhaUrwFwKm2nxretdT1h7Q==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + parse-entities: 4.0.1 + stringify-entities: 4.0.3 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-find-and-replace@3.0.1: + resolution: {integrity: sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==} + dependencies: + '@types/mdast': 4.0.3 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + dev: false + /mdast-util-from-markdown@1.3.1: resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} dependencies: @@ -6225,6 +6389,75 @@ packages: - supports-color dev: false + /mdast-util-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==} + dependencies: + '@types/mdast': 4.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.1 + micromark-util-character: 2.0.1 + dev: false + + /mdast-util-gfm-footnote@2.0.0: + resolution: {integrity: sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + micromark-util-normalize-identifier: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + markdown-table: 3.0.3 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + dependencies: + '@types/mdast': 4.0.3 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + + /mdast-util-gfm@3.0.0: + resolution: {integrity: sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==} + dependencies: + mdast-util-from-markdown: 2.0.0 + mdast-util-gfm-autolink-literal: 2.0.0 + mdast-util-gfm-footnote: 2.0.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /mdast-util-phrasing@4.0.0: resolution: {integrity: sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==} dependencies: @@ -6339,6 +6572,90 @@ packages: micromark-util-types: 2.0.0 dev: false + /micromark-extension-directive@3.0.0: + resolution: {integrity: sha512-61OI07qpQrERc+0wEysLHMvoiO3s2R56x5u7glHq2Yqq6EHbH4dW25G9GfDdGCDYqA21KE6DWgNSzxSwHc2hSg==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-factory-whitespace: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + parse-entities: 4.0.1 + dev: false + + /micromark-extension-gfm-autolink-literal@2.0.0: + resolution: {integrity: sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-footnote@2.0.0: + resolution: {integrity: sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==} + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-classify-character: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-table@2.0.0: + resolution: {integrity: sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + dependencies: + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm-task-list-item@2.0.1: + resolution: {integrity: sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==} + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + + /micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + dependencies: + micromark-extension-gfm-autolink-literal: 2.0.0 + micromark-extension-gfm-footnote: 2.0.0 + micromark-extension-gfm-strikethrough: 2.0.0 + micromark-extension-gfm-table: 2.0.0 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.0.1 + micromark-util-combine-extensions: 2.0.0 + micromark-util-types: 2.0.0 + dev: false + /micromark-factory-destination@1.1.0: resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} dependencies: @@ -6744,6 +7061,12 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /nanoid@4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + dev: false + /nanoid@5.0.4: resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==} engines: {node: ^18 || >=20} @@ -6989,6 +7312,19 @@ packages: author-regex: 1.0.0 dev: true + /parse-entities@4.0.1: + resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} + dependencies: + '@types/unist': 2.0.10 + character-entities: 2.0.2 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.0.2 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + dev: false + /parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -7988,6 +8324,41 @@ packages: set-function-name: 2.0.1 dev: true + /remark-directive@3.0.0: + resolution: {integrity: sha512-l1UyWJ6Eg1VPU7Hm/9tt0zKtReJQNOA4+iDMAxTyZNWnJnFlbS/7zhiel/rogTLQ2vMYwDzSJa4BiVNqGlqIMA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-directive: 3.0.0 + micromark-extension-directive: 3.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-gfm@4.0.0: + resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-gfm: 3.0.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + + /remark-github-alerts@0.0.4(@types/mdast@4.0.3)(unified@11.0.4): + resolution: {integrity: sha512-cGO8CrGb4Vvawn71inAv6VNRL8CKkHYwKpI91ZvbuLhW8W5EkLReQOjqsnj18Hw99X5u44hKyYssOzXLlZdGHQ==} + peerDependencies: + '@types/mdast': ^4.0.0 + unified: ^11.0.0 + dependencies: + '@types/mdast': 4.0.3 + unified: 11.0.4 + unist-util-visit: 5.0.0 + dev: false + /remark-inline-links@7.0.0: resolution: {integrity: sha512-4uj1pPM+F495ySZhTIB6ay2oSkTsKgmYaKk/q5HIdhX2fuyLEegpjWa0VdJRJ01sgOqAFo7MBKdDUejIYBMVMQ==} dependencies: @@ -8418,6 +8789,13 @@ packages: es-abstract: 1.22.3 dev: true + /stringify-entities@4.0.3: + resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + dev: false + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} diff --git a/src/app/(app)/timeline/layout.tsx b/src/app/(app)/timeline/layout.tsx index 7d37dc1edb..9bafb234fb 100644 --- a/src/app/(app)/timeline/layout.tsx +++ b/src/app/(app)/timeline/layout.tsx @@ -15,7 +15,7 @@ export const metadata = { title: '时间线', } -export default async (props: NextPageParams<{}, PropsWithChildren>) => { +export default async (props: PropsWithChildren) => { attachUAAndRealIp() const header = headers() const query = header.get(REQUEST_QUERY) diff --git a/src/components/modules/dashboard/writing/Writing.tsx b/src/components/modules/dashboard/writing/Writing.tsx index 6d4bdb496f..ca335a9ad5 100644 --- a/src/components/modules/dashboard/writing/Writing.tsx +++ b/src/components/modules/dashboard/writing/Writing.tsx @@ -2,13 +2,13 @@ import React, { useEffect, useRef } from 'react' import { produce } from 'immer' import { atom, useAtomValue, useSetAtom, useStore } from 'jotai' import type { FC } from 'react' -import type { MilkdownRef } from '../editor' +import type { MilkdownRef } from '../../../ui/editor' import { useEventCallback } from '~/hooks/common/use-event-callback' import { clsxm } from '~/lib/helper' import { jotaiStore } from '~/lib/store' -import { MilkdownEditor } from '../editor' +import { MilkdownEditor } from '../../../ui/editor' import { useBaseWritingContext } from './BaseWritingProvider' import { TitleInput } from './TitleInput' diff --git a/src/components/modules/dashboard/editor/Milkdown/MilkdownEditor.tsx b/src/components/ui/editor/Milkdown/MilkdownEditor.tsx similarity index 86% rename from src/components/modules/dashboard/editor/Milkdown/MilkdownEditor.tsx rename to src/components/ui/editor/Milkdown/MilkdownEditor.tsx index 322bb7cdd4..87b2ed5985 100644 --- a/src/components/modules/dashboard/editor/Milkdown/MilkdownEditor.tsx +++ b/src/components/ui/editor/Milkdown/MilkdownEditor.tsx @@ -1,4 +1,8 @@ import { Milkdown, MilkdownProvider, useEditor } from '@milkdown/react' +import { + ProsemirrorAdapterProvider, + useNodeViewFactory, +} from '@prosemirror-adapter/react' import { forwardRef, useCallback, @@ -22,11 +26,14 @@ import { history } from '@milkdown/plugin-history' import { indent } from '@milkdown/plugin-indent' import { listener, listenerCtx } from '@milkdown/plugin-listener' import { commonmark } from '@milkdown/preset-commonmark' +import { gfm } from '@milkdown/preset-gfm' import { replaceAll } from '@milkdown/utils' import { useIsUnMounted } from '~/hooks/common/use-is-unmounted' +import { setEditorCtx } from './ctx' import styles from './index.module.css' +import { createPlugins } from './plugins' export interface MilkdownProps { initialMarkdown?: string @@ -46,7 +53,9 @@ export const MilkdownEditor = forwardRef( (props, ref) => { return ( - + + + ) }, @@ -69,12 +78,16 @@ const MilkdownEditorImpl = forwardRef( }), [], ) + + const nodeViewFactory = useNodeViewFactory() + const { get } = useEditor((root) => { const editor = Editor.make() editorRef.current = editor return editor .config((ctx) => { + setEditorCtx(ctx) editorCtxRef.current = ctx ctx.set(rootCtx, root) @@ -89,6 +102,7 @@ const MilkdownEditorImpl = forwardRef( .markdownUpdated((ctx, markdown) => { if (isUnMounted.current) return + console.log('markdown', markdown) props.onMarkdownChange?.(markdown) props.onChange?.({ target: { value: markdown } }) }) @@ -101,6 +115,9 @@ const MilkdownEditorImpl = forwardRef( .use(clipboard) .use(history) .use(indent) + .use(gfm) + .use(createPlugins({ nodeViewFactory })) + .onStatusChange((o) => { if (o === EditorStatus.Created) { props.onCreated?.() @@ -130,4 +147,5 @@ const MilkdownEditorImpl = forwardRef( ) }, ) + MilkdownEditorImpl.displayName = 'MilkdownEditorImpl' diff --git a/src/components/ui/editor/Milkdown/ctx.tsx b/src/components/ui/editor/Milkdown/ctx.tsx new file mode 100644 index 0000000000..1bd074e36e --- /dev/null +++ b/src/components/ui/editor/Milkdown/ctx.tsx @@ -0,0 +1,10 @@ +import { atom, useAtomValue } from 'jotai' +import type { Ctx } from '@milkdown/ctx' + +import { jotaiStore } from '~/lib/store' + +export const editorCtxAtom = atom(null) + +export const useEditorCtx = () => useAtomValue(editorCtxAtom) + +export const setEditorCtx = (ctx: Ctx) => jotaiStore.set(editorCtxAtom, ctx) diff --git a/src/components/modules/dashboard/editor/Milkdown/index.module.css b/src/components/ui/editor/Milkdown/index.module.css similarity index 100% rename from src/components/modules/dashboard/editor/Milkdown/index.module.css rename to src/components/ui/editor/Milkdown/index.module.css diff --git a/src/components/modules/dashboard/editor/Milkdown/index.ts b/src/components/ui/editor/Milkdown/index.ts similarity index 100% rename from src/components/modules/dashboard/editor/Milkdown/index.ts rename to src/components/ui/editor/Milkdown/index.ts diff --git a/src/components/ui/editor/Milkdown/plugins/Blockquote.tsx b/src/components/ui/editor/Milkdown/plugins/Blockquote.tsx new file mode 100644 index 0000000000..c69cbe395b --- /dev/null +++ b/src/components/ui/editor/Milkdown/plugins/Blockquote.tsx @@ -0,0 +1,24 @@ +import { useNodeViewContext } from '@prosemirror-adapter/react' +import type { MilkdownPlugin } from '@milkdown/ctx' +import type { PluginCtx } from './types' + +import { blockquoteSchema } from '@milkdown/preset-commonmark' +import { $view } from '@milkdown/utils' + +const Blockquote = () => { + const { contentRef } = useNodeViewContext() + + return ( +
+ ) +} + +export const BlockquotePlugin: (pluginCtx: PluginCtx) => MilkdownPlugin[] = ({ + nodeViewFactory, +}) => [ + $view(blockquoteSchema.node, () => + nodeViewFactory({ + component: Blockquote, + }), + ), +] diff --git a/src/components/ui/editor/Milkdown/plugins/CodeBlock.tsx b/src/components/ui/editor/Milkdown/plugins/CodeBlock.tsx new file mode 100644 index 0000000000..e921fb0123 --- /dev/null +++ b/src/components/ui/editor/Milkdown/plugins/CodeBlock.tsx @@ -0,0 +1,232 @@ +import { useNodeViewContext } from '@prosemirror-adapter/react' +import { lazy, Suspense, useEffect, useMemo, useRef } from 'react' +import { useForceUpdate } from 'framer-motion' +import type { MilkdownPlugin } from '@milkdown/ctx' +import type { NodeViewContext } from '@prosemirror-adapter/react' +import type { ModalContentPropsInternal } from '~/components/ui/modal' +import type { FC } from 'react' +import type { PluginCtx } from './types' + +import { schemaCtx } from '@milkdown/core' +import { codeBlockSchema } from '@milkdown/preset-commonmark' +import { $view } from '@milkdown/utils' + +import { BlockLoading } from '~/components/modules/shared/BlockLoading' +import { StyledButton } from '~/components/ui/button' +import { HighLighter } from '~/components/ui/code-highlighter' +import { Input, TextArea } from '~/components/ui/input' +import { useCurrentModal, useModalStack } from '~/components/ui/modal' +import { useUncontrolledInput } from '~/hooks/common/use-uncontrolled-input' + +import { useEditorCtx } from '../ctx' + +const CodeBlock = () => { + const { node } = useNodeViewContext() + + const language = node.attrs.language + const content = node.content.firstChild?.text || '' + + switch (language) { + case 'excalidraw': { + return + } + } + + return ( +
+ +
+ ) +} + +const NormalCodeBlock: FC<{ + content: string + language: string +}> = ({ content, language }) => { + const nodeCtx = useNodeViewContext() + + const modalStack = useModalStack() + + const handleEdit = () => { + const Content: FC = () => { + const [, getValue, ref] = + useUncontrolledInput(content) + + const nextLangRef = useRef(language) + + return ( +
+