@dxpos/markdown 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +77 -0
  2. package/dist/editor/ColorMenu.d.ts +18 -0
  3. package/dist/editor/ColorMenu.d.ts.map +1 -0
  4. package/dist/editor/ColorMenu.js +90 -0
  5. package/dist/editor/SlashMenu.d.ts +24 -0
  6. package/dist/editor/SlashMenu.d.ts.map +1 -0
  7. package/dist/editor/SlashMenu.js +179 -0
  8. package/dist/editor/TextStyleMenu.d.ts +45 -0
  9. package/dist/editor/TextStyleMenu.d.ts.map +1 -0
  10. package/dist/editor/TextStyleMenu.js +65 -0
  11. package/dist/editor/VideoEmbed.d.ts +15 -0
  12. package/dist/editor/VideoEmbed.d.ts.map +1 -0
  13. package/dist/editor/VideoEmbed.js +95 -0
  14. package/dist/editor/VideoMenu.d.ts +12 -0
  15. package/dist/editor/VideoMenu.d.ts.map +1 -0
  16. package/dist/editor/VideoMenu.js +35 -0
  17. package/dist/editor/color-menu/index.d.ts +19 -0
  18. package/dist/editor/color-menu/index.d.ts.map +1 -0
  19. package/dist/editor/color-menu/index.js +90 -0
  20. package/dist/editor/color-menu/text-color-presets.d.ts +9 -0
  21. package/dist/editor/color-menu/text-color-presets.d.ts.map +1 -0
  22. package/dist/editor/color-menu/text-color-presets.js +46 -0
  23. package/dist/editor/index.d.ts +12 -0
  24. package/dist/editor/index.d.ts.map +1 -0
  25. package/dist/editor/index.js +314 -0
  26. package/dist/editor/letter-spacing/index.d.ts +17 -0
  27. package/dist/editor/letter-spacing/index.d.ts.map +1 -0
  28. package/dist/editor/letter-spacing/index.js +35 -0
  29. package/dist/editor/letter-spacing.d.ts +17 -0
  30. package/dist/editor/letter-spacing.d.ts.map +1 -0
  31. package/dist/editor/letter-spacing.js +35 -0
  32. package/dist/editor/slash-menu/index.d.ts +25 -0
  33. package/dist/editor/slash-menu/index.d.ts.map +1 -0
  34. package/dist/editor/slash-menu/index.js +179 -0
  35. package/dist/editor/text-color-presets.d.ts +9 -0
  36. package/dist/editor/text-color-presets.d.ts.map +1 -0
  37. package/dist/editor/text-color-presets.js +46 -0
  38. package/dist/editor/text-style-menu/index.d.ts +46 -0
  39. package/dist/editor/text-style-menu/index.d.ts.map +1 -0
  40. package/dist/editor/text-style-menu/index.js +65 -0
  41. package/dist/editor/theme.d.ts +8 -0
  42. package/dist/editor/theme.d.ts.map +1 -0
  43. package/dist/editor/theme.js +18 -0
  44. package/dist/editor/toolbar/index.d.ts +17 -0
  45. package/dist/editor/toolbar/index.d.ts.map +1 -0
  46. package/dist/editor/toolbar/index.js +206 -0
  47. package/dist/editor/toolbar/items.d.ts +10 -0
  48. package/dist/editor/toolbar/items.d.ts.map +1 -0
  49. package/dist/editor/toolbar/items.js +34 -0
  50. package/dist/editor/toolbar/theme.d.ts +8 -0
  51. package/dist/editor/toolbar/theme.d.ts.map +1 -0
  52. package/dist/editor/toolbar/theme.js +18 -0
  53. package/dist/editor/toolbar-items.d.ts +10 -0
  54. package/dist/editor/toolbar-items.d.ts.map +1 -0
  55. package/dist/editor/toolbar-items.js +34 -0
  56. package/dist/editor/toolbar.d.ts +16 -0
  57. package/dist/editor/toolbar.d.ts.map +1 -0
  58. package/dist/editor/toolbar.js +206 -0
  59. package/dist/editor/use-dom-id.d.ts +3 -0
  60. package/dist/editor/use-dom-id.d.ts.map +1 -0
  61. package/dist/editor/use-dom-id.js +11 -0
  62. package/dist/editor/video-embed/index.d.ts +15 -0
  63. package/dist/editor/video-embed/index.d.ts.map +1 -0
  64. package/dist/editor/video-embed/index.js +95 -0
  65. package/dist/editor/video-embed/video-url.d.ts +8 -0
  66. package/dist/editor/video-embed/video-url.d.ts.map +1 -0
  67. package/dist/editor/video-embed/video-url.js +102 -0
  68. package/dist/editor/video-menu/index.d.ts +13 -0
  69. package/dist/editor/video-menu/index.d.ts.map +1 -0
  70. package/dist/editor/video-menu/index.js +35 -0
  71. package/dist/editor/video-url.d.ts +8 -0
  72. package/dist/editor/video-url.d.ts.map +1 -0
  73. package/dist/editor/video-url.js +102 -0
  74. package/dist/index.d.ts +5 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +4 -0
  77. package/dist/plugin.d.ts +6 -0
  78. package/dist/plugin.d.ts.map +1 -0
  79. package/dist/plugin.js +1 -0
  80. package/dist/plugins/gfm/index.d.ts +3 -0
  81. package/dist/plugins/gfm/index.d.ts.map +1 -0
  82. package/dist/plugins/gfm/index.js +2 -0
  83. package/dist/plugins/highlight/index.d.ts +3 -0
  84. package/dist/plugins/highlight/index.d.ts.map +1 -0
  85. package/dist/plugins/highlight/index.js +2 -0
  86. package/dist/plugins/raw/index.d.ts +3 -0
  87. package/dist/plugins/raw/index.d.ts.map +1 -0
  88. package/dist/plugins/raw/index.js +2 -0
  89. package/dist/view/index.d.ts +9 -0
  90. package/dist/view/index.d.ts.map +1 -0
  91. package/dist/view/index.js +18 -0
  92. package/package.json +61 -0
  93. package/styles.css +760 -0
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Loader2, Video } from "lucide-react";
4
+ import { useEffect, useId, useRef, useState } from "react";
5
+ /** 工具栏视频菜单:上传文件或粘贴链接 */
6
+ export const VideoMenu = ({ open, uploading, error, onOpenChange, onUpload, onSubmitUrl }) => {
7
+ const rootRef = useRef(null);
8
+ const inputId = useId();
9
+ const [url, setUrl] = useState("");
10
+ useEffect(() => {
11
+ if (!open) {
12
+ return;
13
+ }
14
+ const onPointerDown = (event) => {
15
+ if (!rootRef.current?.contains(event.target)) {
16
+ onOpenChange(false);
17
+ }
18
+ };
19
+ const onKeyDown = (event) => {
20
+ if (event.key === "Escape") {
21
+ onOpenChange(false);
22
+ }
23
+ };
24
+ document.addEventListener("pointerdown", onPointerDown);
25
+ document.addEventListener("keydown", onKeyDown);
26
+ return () => {
27
+ document.removeEventListener("pointerdown", onPointerDown);
28
+ document.removeEventListener("keydown", onKeyDown);
29
+ };
30
+ }, [open, onOpenChange]);
31
+ return (_jsxs("div", { className: "wsmd-video-menu", ref: rootRef, children: [_jsx("button", { className: "wsmd-icon-btn wsmd-icon-btn--ghost wsmd-toolbar__btn", type: "button", title: uploading ? "上传中" : "视频", "aria-label": uploading ? "上传中" : "视频", "aria-expanded": open, "aria-haspopup": "dialog", disabled: uploading, onClick: () => onOpenChange(!open), children: uploading ? _jsx(Loader2, { className: "wsmd-icon-btn__spin", "aria-hidden": true }) : _jsx(Video, { "aria-hidden": true }) }), open ? (_jsxs("div", { className: "wsmd-video-menu__panel", role: "dialog", "aria-label": "\u89C6\u9891", children: [_jsx("button", { className: "wsmd-video-menu__upload", type: "button", onClick: onUpload, children: "\u4E0A\u4F20\u89C6\u9891" }), _jsxs("form", { className: "wsmd-video-menu__form", onSubmit: (event) => {
32
+ event.preventDefault();
33
+ onSubmitUrl(url);
34
+ }, children: [_jsx("label", { className: "wsmd-video-menu__label", htmlFor: inputId, children: "\u89C6\u9891\u94FE\u63A5" }), _jsx("input", { id: inputId, className: "wsmd-video-menu__input", type: "url", value: url, placeholder: "mp4\u3001webm\u3001YouTube\u3001Bilibili", onChange: (event) => setUrl(event.target.value) }), error ? _jsx("p", { className: "wsmd-video-menu__error", children: error }) : null, _jsx("button", { className: "wsmd-video-menu__submit", type: "submit", children: "\u63D2\u5165" })] })] })) : null] }));
35
+ };
@@ -0,0 +1,8 @@
1
+ export type VideoProvider = "file" | "youtube" | "bilibili";
2
+ export type VideoEmbedAttrs = {
3
+ provider: VideoProvider;
4
+ src: string;
5
+ };
6
+ /** 把用户粘贴的地址解析成可嵌入的视频节点属性 */
7
+ export declare function parseVideoUrl(input: string): VideoEmbedAttrs | null;
8
+ //# sourceMappingURL=video-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-url.d.ts","sourceRoot":"","sources":["../../src/editor/video-url.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAE5D,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,aAAa,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AA0FF,4BAA4B;AAC5B,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAkBnE"}
@@ -0,0 +1,102 @@
1
+ const YOUTUBE_ID = /^[\w-]{11}$/;
2
+ const BVID = /^BV[\w]+$/i;
3
+ function httpUrl(input) {
4
+ const trimmed = input.trim();
5
+ if (!trimmed) {
6
+ return null;
7
+ }
8
+ let url;
9
+ try {
10
+ url = new URL(trimmed);
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
16
+ return null;
17
+ }
18
+ return url;
19
+ }
20
+ function bareHost(url) {
21
+ return url.hostname.replace(/^www\./, "").toLowerCase();
22
+ }
23
+ function youtubeId(url) {
24
+ const host = bareHost(url);
25
+ if (host === "youtu.be") {
26
+ const id = url.pathname.split("/").filter(Boolean)[0];
27
+ return id && YOUTUBE_ID.test(id) ? id : null;
28
+ }
29
+ if (host !== "youtube.com" &&
30
+ host !== "m.youtube.com" &&
31
+ host !== "music.youtube.com" &&
32
+ host !== "youtube-nocookie.com") {
33
+ return null;
34
+ }
35
+ if (url.pathname === "/watch") {
36
+ const id = url.searchParams.get("v");
37
+ return id && YOUTUBE_ID.test(id) ? id : null;
38
+ }
39
+ const parts = url.pathname.split("/").filter(Boolean);
40
+ if ((parts[0] === "embed" || parts[0] === "shorts") && parts[1] && YOUTUBE_ID.test(parts[1])) {
41
+ return parts[1];
42
+ }
43
+ return null;
44
+ }
45
+ function bilibiliEmbed(url) {
46
+ const host = bareHost(url);
47
+ if (host === "bilibili.com" || host === "m.bilibili.com") {
48
+ const bv = url.pathname.match(/\/video\/(BV[\w]+)/i);
49
+ if (bv?.[1] && BVID.test(bv[1])) {
50
+ return {
51
+ provider: "bilibili",
52
+ src: `https://player.bilibili.com/player.html?bvid=${bv[1]}&autoplay=0`,
53
+ };
54
+ }
55
+ const av = url.pathname.match(/\/video\/av(\d+)/i);
56
+ if (av?.[1]) {
57
+ return {
58
+ provider: "bilibili",
59
+ src: `https://player.bilibili.com/player.html?aid=${av[1]}&autoplay=0`,
60
+ };
61
+ }
62
+ return null;
63
+ }
64
+ if (host !== "player.bilibili.com") {
65
+ return null;
66
+ }
67
+ const bvid = url.searchParams.get("bvid");
68
+ if (bvid && BVID.test(bvid)) {
69
+ return {
70
+ provider: "bilibili",
71
+ src: `https://player.bilibili.com/player.html?bvid=${bvid}&autoplay=0`,
72
+ };
73
+ }
74
+ const aid = url.searchParams.get("aid");
75
+ if (aid && /^\d+$/.test(aid)) {
76
+ return {
77
+ provider: "bilibili",
78
+ src: `https://player.bilibili.com/player.html?aid=${aid}&autoplay=0`,
79
+ };
80
+ }
81
+ return null;
82
+ }
83
+ /** 把用户粘贴的地址解析成可嵌入的视频节点属性 */
84
+ export function parseVideoUrl(input) {
85
+ const url = httpUrl(input);
86
+ if (!url) {
87
+ return null;
88
+ }
89
+ const youtube = youtubeId(url);
90
+ if (youtube) {
91
+ return { provider: "youtube", src: `https://www.youtube.com/embed/${youtube}` };
92
+ }
93
+ const bilibili = bilibiliEmbed(url);
94
+ if (bilibili) {
95
+ return bilibili;
96
+ }
97
+ const path = decodeURIComponent(url.pathname).toLowerCase();
98
+ if (path.endsWith(".mp4") || path.endsWith(".webm")) {
99
+ return { provider: "file", src: url.toString() };
100
+ }
101
+ return null;
102
+ }
@@ -0,0 +1,5 @@
1
+ export { Editor } from "./editor";
2
+ export { Viewer } from "./view";
3
+ export { DEFAULT_TOOLBAR, type ToolbarItem, type UploadFile, type UploadFileResult, type UploadFileType } from "./editor/toolbar/items";
4
+ export type { MarkdownPlugin } from "./plugin";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxI,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ "use client";
2
+ export { Editor } from "./editor";
3
+ export { Viewer } from "./view";
4
+ export { DEFAULT_TOOLBAR } from "./editor/toolbar/items";
@@ -0,0 +1,6 @@
1
+ /** Viewer 插件:remark 或 rehype 二选一 */
2
+ export type MarkdownPlugin = {
3
+ remark?: (this: unknown, ...args: never[]) => unknown;
4
+ rehype?: (this: unknown, ...args: never[]) => unknown;
5
+ };
6
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;IACtD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;CACvD,CAAC"}
package/dist/plugin.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { MarkdownPlugin } from "../../plugin";
2
+ export declare const gfm: MarkdownPlugin;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/gfm/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,eAAO,MAAM,GAAG,EAAE,cAAsC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import remarkGfm from "remark-gfm";
2
+ export const gfm = { remark: remarkGfm };
@@ -0,0 +1,3 @@
1
+ import type { MarkdownPlugin } from "../../plugin";
2
+ export declare const highlight: MarkdownPlugin;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/highlight/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,eAAO,MAAM,SAAS,EAAE,cAA4C,CAAC"}
@@ -0,0 +1,2 @@
1
+ import rehypeHighlight from "rehype-highlight";
2
+ export const highlight = { rehype: rehypeHighlight };
@@ -0,0 +1,3 @@
1
+ import type { MarkdownPlugin } from "../../plugin";
2
+ export declare const raw: MarkdownPlugin;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/raw/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,eAAO,MAAM,GAAG,EAAE,cAAsC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import rehypeRaw from "rehype-raw";
2
+ export const raw = { rehype: rehypeRaw };
@@ -0,0 +1,9 @@
1
+ import type { MarkdownPlugin } from "../plugin";
2
+ interface ViewerProps {
3
+ content: string;
4
+ plugins?: MarkdownPlugin[];
5
+ className?: string;
6
+ }
7
+ export declare const Viewer: import("react").ForwardRefExoticComponent<ViewerProps & import("react").RefAttributes<HTMLDivElement>>;
8
+ export {};
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/view/index.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,MAAM,wGAmBjB,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { forwardRef } from "react";
4
+ import Markdown from "react-markdown";
5
+ export const Viewer = forwardRef(({ content, plugins = [], className }, ref) => {
6
+ const remarkPlugins = [];
7
+ const rehypePlugins = [];
8
+ for (const plugin of plugins) {
9
+ if (plugin.remark) {
10
+ remarkPlugins.push(plugin.remark);
11
+ }
12
+ if (plugin.rehype) {
13
+ rehypePlugins.push(plugin.rehype);
14
+ }
15
+ }
16
+ const rootClass = className ? `wsmd-markdown-body ${className}` : "wsmd-markdown-body";
17
+ return (_jsx("div", { ref: ref, className: rootClass, children: _jsx(Markdown, { remarkPlugins: remarkPlugins, rehypePlugins: rehypePlugins, children: content }) }));
18
+ });
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@dxpos/markdown",
3
+ "version": "0.0.1",
4
+ "description": "Markdown 编辑器与渲染器",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./styles.css": "./styles.css",
14
+ "./plugins/gfm": {
15
+ "types": "./dist/plugins/gfm/index.d.ts",
16
+ "import": "./dist/plugins/gfm/index.js"
17
+ },
18
+ "./plugins/raw": {
19
+ "types": "./dist/plugins/raw/index.d.ts",
20
+ "import": "./dist/plugins/raw/index.js"
21
+ },
22
+ "./plugins/highlight": {
23
+ "types": "./dist/plugins/highlight/index.d.ts",
24
+ "import": "./dist/plugins/highlight/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "styles.css",
30
+ "README.md"
31
+ ],
32
+ "peerDependencies": {
33
+ "react": "^18.0.0 || ^19.0.0",
34
+ "react-dom": "^18.0.0 || ^19.0.0"
35
+ },
36
+ "dependencies": {
37
+ "@tiptap/core": "^3.30.5",
38
+ "@tiptap/extension-color": "3.30.5",
39
+ "@tiptap/extension-highlight": "^3.30.5",
40
+ "@tiptap/extension-image": "^3.30.5",
41
+ "@tiptap/extension-link": "^3.30.5",
42
+ "@tiptap/extension-placeholder": "^3.30.5",
43
+ "@tiptap/extension-table": "^3.30.5",
44
+ "@tiptap/extension-task-item": "^3.30.5",
45
+ "@tiptap/extension-task-list": "^3.30.5",
46
+ "@tiptap/extension-text-align": "^3.30.5",
47
+ "@tiptap/extension-text-style": "3.30.5",
48
+ "@tiptap/pm": "^3.30.5",
49
+ "@tiptap/react": "^3.30.5",
50
+ "@tiptap/starter-kit": "^3.30.5",
51
+ "@tiptap/suggestion": "^3.30.5",
52
+ "highlight.js": "^11.12.0",
53
+ "lucide-react": "^1.37.0",
54
+ "react-markdown": "^10.1.0",
55
+ "rehype-highlight": "^7.0.2",
56
+ "rehype-raw": "^7.0.0",
57
+ "remark-gfm": "^4.0.1",
58
+ "tiptap-markdown": "^0.9.0"
59
+ },
60
+ "license": "MIT"
61
+ }