remons-markdown-editor 1.0.0 → 2.0.0-beta.0

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.
@@ -2,13 +2,30 @@ import { ComponentSchema } from '../types';
2
2
  /**
3
3
  * 组件注册中心,用于管理自定义组件的 schema。
4
4
  * 每个容器组件(如 :::linkCard)在此注册其可编辑字段。
5
+ * 内置版本计数 + 订阅:注册/移除后通知监听方(如编辑器工具栏)刷新。
5
6
  */
7
+ type Listener = (version: number) => void;
6
8
  declare class ComponentRegistry {
7
9
  private schemas;
10
+ private listeners;
11
+ private version;
8
12
  /**
9
13
  * 注册组件 schema
10
14
  */
11
15
  register(name: string, schema: ComponentSchema): void;
16
+ /**
17
+ * 移除组件注册
18
+ */
19
+ unregister(name: string): void;
20
+ /**
21
+ * 当前版本号,任意注册变更都会自增
22
+ */
23
+ getVersion(): number;
24
+ /**
25
+ * 订阅注册变更,返回退订函数
26
+ */
27
+ subscribe(listener: Listener): () => void;
28
+ private emit;
12
29
  /**
13
30
  * 根据组件名获取已注册的 schema
14
31
  */
@@ -25,10 +42,6 @@ declare class ComponentRegistry {
25
42
  * 获取所有已注册的 schema
26
43
  */
27
44
  getAll(): ComponentSchema[];
28
- /**
29
- * 移除组件注册
30
- */
31
- unregister(name: string): void;
32
45
  }
33
46
  declare const componentRegistry: ComponentRegistry;
34
47
  export default componentRegistry;
@@ -1,10 +1,12 @@
1
1
  import { EditorView } from '@codemirror/view';
2
2
  import { EditorState as CMEditorState } from '@codemirror/state';
3
- import { FormInstance } from 'antd';
3
+ import { FormInstance, FormItemProps } from 'antd';
4
4
  import { default as React } from 'react';
5
+ import { ContainerProps } from '../utils/parseContainer';
5
6
  export interface ToolbarButtonConfig {
6
7
  id?: string;
7
- icon?: React.ComponentType | React.ReactNode;
8
+ /** 图标统一为组件类型,由渲染层 <Icon /> 实例化 */
9
+ icon?: React.ComponentType;
8
10
  label?: string;
9
11
  children?: ToolbarButtonConfig[];
10
12
  handler?: (view: EditorView, state: CMEditorState) => void;
@@ -28,7 +30,7 @@ export interface DialogField {
28
30
  /** 字段显示名称(表单 label) */
29
31
  name?: string;
30
32
  /** 默认值 */
31
- defaultValue?: string;
33
+ defaultValue?: string | number | boolean;
32
34
  /**
33
35
  * 表单组件类型(可选)
34
36
  * 配置后 EditDialog 会自动渲染对应的 antd 表单组件,无需手写 Form 结构
@@ -36,10 +38,15 @@ export interface DialogField {
36
38
  */
37
39
  componentType?: FormComponentType;
38
40
  /**
39
- * 组件额外属性(可选)
40
- * 传递对应 antd 组件的 props
41
+ * 控件组件额外属性(可选),仅传给表单控件本身
42
+ * 如 Select 的 options、Input.TextArea 的 rows
41
43
  */
42
44
  componentProps?: Record<string, any>;
45
+ /**
46
+ * Form.Item 额外属性(可选),仅传给 Form.Item
47
+ * 如 rules、required 等,不会污染控件 DOM
48
+ */
49
+ formItemProps?: FormItemProps;
43
50
  }
44
51
  /**
45
52
  * 组件 Schema
@@ -51,8 +58,8 @@ export interface ComponentSchema {
51
58
  name: string;
52
59
  /** 工具栏按钮显示的标签 */
53
60
  label: string;
54
- /** 工具栏按钮显示的图标 */
55
- icon?: React.ReactNode;
61
+ /** 工具栏按钮显示的图标(组件类型,与 ToolbarButtonConfig.icon 一致) */
62
+ icon?: React.ComponentType;
56
63
  /**
57
64
  * 标签包裹配置:设置为 [openTag, closeTag] 时,使用标签包裹模式(而非 :::name 容器)
58
65
  * 配置后无需提供 fields 和 renderDialog
@@ -114,14 +121,16 @@ export interface ComponentSchema {
114
121
  */
115
122
  export interface DialogRenderContext {
116
123
  /** 当前属性值(合并默认值与当前的值) */
117
- props: Record<string, string>;
124
+ props: ContainerProps;
118
125
  /** 表单实例,用于 Form.Item 自动绑定 */
119
126
  form: FormInstance;
120
127
  /** 属性变更回调(手动控制方式) */
121
- onChange: (key: string, value: string) => void;
128
+ onChange: (key: string, value: string | number | boolean) => void;
122
129
  /** 直接保存属性(完全自定义方式,调用后弹窗不会自动关闭) */
123
- onSave: (props: Record<string, string>) => void;
130
+ onSave: (props: ContainerProps) => void;
124
131
  }
132
+ /** 全局消息提示注入点(如 antd message.error),缺省用 console,宿主可接管 */
133
+ export type NotifyFn = (type: 'info' | 'warning' | 'error', content: string) => void;
125
134
  /**
126
135
  * 图片上传配置
127
136
  */
@@ -1,18 +1,32 @@
1
+ /** 容器属性对象:值保留原始类型(string/number/boolean),写回时统一序列化 */
2
+ export type ContainerProps = Record<string, string | number | boolean>;
1
3
  /**
2
- * 解析容器属性 JSON 字符串。
3
- * 支持标准 JSON 和宽松格式: { key: 'value', key2: 'value2' }
4
+ * 解析容器属性行。
5
+ * 优先按 JSON5 解析(兼容标准 JSON 与 { key: 'value' } 宽松写法),
6
+ * 失败时返回空对象,保证往返编辑不丢数据。
4
7
  */
5
- export declare function parseContainerProps(jsonStr: string): Record<string, string>;
8
+ export declare function parseContainerProps(line: string): ContainerProps;
6
9
  /**
7
- * 在编辑器文档中查找容器块。
8
- * 返回包含类型、属性和行位置的容器位置数组。
10
+ * 容器块位置信息(基于文档的绝对字符偏移)
9
11
  */
10
12
  export interface ContainerBlock {
13
+ /** ::: 打开行行首位置 */
11
14
  startPos: number;
15
+ /** 组件类型名称 (如 "linkCard") */
12
16
  typeName: string;
17
+ /** 属性行起始位置(0 表示无属性行) */
13
18
  propsLineFrom: number;
19
+ /** 属性行结束位置(0 表示无属性行) */
14
20
  propsLineTo: number;
15
- props: Record<string, string>;
21
+ /** 解析出的属性 */
22
+ props: ContainerProps;
23
+ /** ::: 关闭行行首位置(0 表示未闭合) */
16
24
  endPos: number;
25
+ /** 类型是否已在注册中心登记(未登记的仅装饰、不可编辑) */
26
+ registered: boolean;
17
27
  }
28
+ /**
29
+ * 在编辑器文档中查找容器块。
30
+ * 单次遍历 + 预累计行偏移,复杂度 O(行数)。
31
+ */
18
32
  export declare function findContainers(docText: string): ContainerBlock[];
@@ -1,16 +1,19 @@
1
1
  import { EditorView } from '@codemirror/view';
2
+ import { ContainerProps } from './parseContainer';
2
3
  /**
3
- * 从属性对象生成 JSON 属性行。
4
- * 格式: { key: 'value', key2: 'value2' }
4
+ * 从属性对象生成属性行。
5
+ * 使用 JSON5 序列化:单引号风格与旧格式兼容,特殊字符会被正确转义,
6
+ * 配合 parseContainerProps 保证读写往返无损。
5
7
  */
6
- export declare function writeContainerProps(props: Record<string, string>): string;
8
+ export declare function writeContainerProps(props: ContainerProps): string;
7
9
  /**
8
10
  * 更新编辑器文档中的容器属性。
9
- * 替换 JSON 属性行(::: 打开行之后的一行)为新属性。
11
+ * 已有属性行则整行替换;没有属性行则在容器头行后插入一行。
12
+ * @param headerPos 容器头行(:::type)任意位置,用于无属性行时定位插入点
10
13
  */
11
- export declare function updateContainerProps(view: EditorView, propsLineFrom: number, propsLineTo: number, newProps: Record<string, string>): void;
14
+ export declare function updateContainerProps(view: EditorView, headerPos: number, propsLineFrom: number, propsLineTo: number, newProps: ContainerProps): void;
12
15
  /**
13
16
  * 在当前光标位置插入新容器。
14
- * 创建 :::type 打开行 + JSON 属性 + ::: 关闭行的结构。
17
+ * 创建 :::type 打开行 + 属性行 + ::: 关闭行的结构。
15
18
  */
16
- export declare function insertContainer(view: EditorView, typeName: string, defaultProps: Record<string, string>): void;
19
+ export declare function insertContainer(view: EditorView, typeName: string, defaultProps: ContainerProps): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remons-markdown-editor",
3
- "version": "1.0.0",
3
+ "version": "2.0.0-beta.0",
4
4
  "type": "module",
5
5
  "description": "一个可复用的 React Markdown 编辑器组件,支持工具栏、预览和自定义容器",
6
6
  "main": "dist/index.cjs",
@@ -13,21 +13,17 @@
13
13
  },
14
14
  "homepage": "https://github.com/liRemons/markdown-editor#readme",
15
15
  "sideEffects": [
16
- "*.css"
16
+ "*.css",
17
+ "*.less"
17
18
  ],
18
19
  "files": [
19
20
  "dist"
20
21
  ],
21
22
  "exports": {
22
23
  ".": {
23
- "import": {
24
- "types": "./dist/index.d.ts",
25
- "default": "./dist/index.js"
26
- },
27
- "require": {
28
- "types": "./dist/index.d.cts",
29
- "default": "./dist/index.cjs"
30
- }
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "require": "./dist/index.cjs"
31
27
  },
32
28
  "./style.css": "./dist/index.css"
33
29
  },
@@ -42,9 +38,7 @@
42
38
  "registry": "https://registry.npmjs.org/"
43
39
  },
44
40
  "dependencies": {
45
- "@ant-design/icons": "^6.3.4",
46
41
  "@codemirror/commands": "^6.11.0",
47
- "@codemirror/highlight": "^0.19.8",
48
42
  "@codemirror/lang-markdown": "^6.3.2",
49
43
  "@codemirror/language": "^6.12.4",
50
44
  "@codemirror/state": "^6.5.2",
@@ -52,13 +46,16 @@
52
46
  "@codemirror/view": "^6.36.4",
53
47
  "@lezer/common": "^1.2.3",
54
48
  "@lezer/highlight": "^1.2.3",
55
- "antd": "^6.6.3",
56
49
  "codemirror": "^6.0.1",
57
50
  "highlight.js": "^11.12.0",
58
- "remons-markdown-plugins": "^1.0.0",
59
- "remons-render-markdown": "^1.0.28"
51
+ "json5": "^2.2.3",
52
+ "remons-markdown-plugins": "^2.0.0-beta.1",
53
+ "remons-render-markdown": "^2.0.1-beta.5"
60
54
  },
61
55
  "peerDependencies": {
56
+ "antd": ">=5.21.6",
57
+ "@ant-design/icons": ">=5.0.0",
58
+ "dayjs": ">=1.11.13",
62
59
  "react": "^18.0.0",
63
60
  "react-dom": "^18.0.0"
64
61
  },
package/readme.md CHANGED
@@ -10,9 +10,10 @@ npm install remons-markdown-editor
10
10
 
11
11
  ## 基本用法
12
12
 
13
+ 入口已自动携带组件样式,无需手动导入 CSS;若使用打包器的 sideEffects 裁剪,也可显式引入 `remons-markdown-editor/style.css`。
14
+
13
15
  ```tsx
14
16
  import { MarkdownEditor } from 'remons-markdown-editor'
15
- import 'remons-markdown-editor/style.css'
16
17
 
17
18
  export default function App() {
18
19
  return <MarkdownEditor />
@@ -25,13 +26,18 @@ export default function App() {
25
26
 
26
27
  | 属性名 | 类型 | 默认值 | 说明 |
27
28
  |--------|------|--------|------|
28
- | value | string | - | 受控模式下的内容 |
29
+ | value | string | - | 受控模式下的内容(需配合 onChange 使用) |
29
30
  | defaultValue | string | '' | 非受控模式下的初始内容 |
30
31
  | onChange | (value: string) => void | - | 内容变更回调 |
31
32
  | showPreview | boolean | true | 是否显示预览面板 |
32
33
  | fullscreen | boolean | true | 是否显示全屏按钮 |
33
34
  | previewOptions | PreviewOptions | - | 预览渲染配置,类型继承自 `RenderMarkdownProps` |
35
+ | languages | Record<string, any> \| null | - | 代码高亮语言包(与内置 languagesCommon 合并注册) |
34
36
  | uploadConfig | ImageUploadConfig | - | 图片上传配置 |
37
+ | schemas | ComponentSchema[] | - | 局部注册的容器 schema,仅对当前实例生效,同名时覆盖全局注册 |
38
+ | onNotify | (type, content) => void | - | 提示注入点,缺省使用 antd App 上下文的 message |
39
+
40
+ > 同页面存在多个编辑器实例时,推荐用 `schemas` 局部注册代替 `useRegisterToolbar`,避免全局注册表互相覆盖。
35
41
 
36
42
  ### MarkdownEditorRef
37
43
 
@@ -41,29 +47,32 @@ export default function App() {
41
47
 
42
48
  ### PreviewOptions
43
49
 
44
- 预览渲染配置,类型继承自 `RenderMarkdownProps`(来自 `remons-render-markdown`),并扩展了 `languages` 字段:
50
+ 预览渲染配置,类型即 `Omit<RenderMarkdownProps, 'content'>`(来自 `remons-render-markdown` v2):
45
51
 
46
52
  ```typescript
47
53
  import type { RenderMarkdownProps } from 'remons-render-markdown'
48
54
 
49
- export type PreviewOptions = Omit<RenderMarkdownProps, 'content'> & {
50
- languages?: Record<string, any> | null | undefined
51
- }
55
+ export type PreviewOptions = Omit<RenderMarkdownProps, 'content'>
52
56
  ```
53
57
 
58
+ 常用字段(v2 重命名后的新名,旧名仍可用但已废弃):
59
+
54
60
  | 属性名 | 类型 | 默认值 | 说明 |
55
61
  |--------|------|--------|------|
56
- | languages | Record<string, any> | - | 自定义代码高亮语言配置 |
57
62
  | customRenderers | MarkdownPlugin[] | - | 自定义 markdown-it 渲染插件 |
58
- | excludedSelectors | string[] | - | 图片预览排除名单 |
59
- | isSlotMermaid | boolean | - | 是否使用 slot 方式渲染 mermaid |
60
- | isShowCollapsed | boolean | - | 是否显示代码折叠 |
61
- | defaultCollapsed | boolean | - | 默认识否折叠代码 |
63
+ | excludedSelectors | string[] | - | 图片预览排除名单(实例级) |
64
+ | enableMermaid | boolean | - | 是否将 mermaid 代码块渲染为图表(旧名 isSlotMermaid) |
65
+ | showCollapseButton | boolean | - | 是否显示代码折叠按钮(旧名 isShowCollapsed) |
66
+ | defaultCollapsed | boolean | - | 代码块默认是否折叠 |
62
67
  | showBackTop | boolean | - | 是否显示返回顶部 |
68
+ | showToc | boolean | - | 是否显示目录(TOC) |
69
+ | incremental | boolean | - | 是否启用增量渲染(旧名 useIncremental) |
63
70
  | isPrintPreview | boolean | - | 是否为打印模式 |
64
- | mermaidDebounce | number | - | Mermaid 渲染防抖延迟(ms) |
71
+ | toolbarDebounceMs | number | - | 工具栏/mermaid 扫描防抖延迟(旧名 mermaidDebounce) |
65
72
  | cdn | Record<string, string> | - | 自定义 CDN 配置 |
66
- | chartConfig | (text: string) => string | - | 渲染前文本修改函数 |
73
+ | transformSource | (text: string) => string | - | 渲染前改写 mermaid 源码(旧名 chartConfig) |
74
+ | html | boolean | true | 是否渲染源码中的原生 HTML |
75
+ | validateLink | (url: string) => boolean | - | 覆盖链接校验 |
67
76
 
68
77
  更多配置请参考 [remons-render-markdown](https://github.com/liRemons/render-markdown)。
69
78
 
@@ -85,27 +94,22 @@ export type PreviewOptions = Omit<RenderMarkdownProps, 'content'> & {
85
94
  import { MarkdownEditor } from 'remons-markdown-editor'
86
95
  import type { PreviewOptions } from 'remons-markdown-editor'
87
96
  import { languagesCommon } from 'remons-render-markdown'
88
- import 'remons-markdown-editor/style.css'
89
97
 
90
98
  function App() {
91
99
  const previewOptions: PreviewOptions = {
92
- // 自定义高亮语言(在 languagesCommon 基础上扩展)
93
- languages: {
94
- ...languagesCommon,
95
- // 添加自定义语言配置
96
- },
97
100
  // 自定义 markdown-it 插件,可使用 remons-markdown-plugins 按需导入
98
101
  // 参考 https://github.com/liRemons/markdown-plugin
99
102
  customRenderers: [
100
103
  (md) => md.use(yourCustomPlugin)
101
104
  ],
102
- // 禁用 mermaid slot 渲染
103
- isSlotMermaid: false,
105
+ // 禁用 mermaid 图表渲染
106
+ enableMermaid: false,
104
107
  // 自定义排除选择器
105
108
  excludedSelectors: ['.my-custom-class'],
106
109
  }
107
110
 
108
- return <MarkdownEditor previewOptions={previewOptions} />
111
+ // 高亮语言包通过独立的 languages prop 传入(在 languagesCommon 基础上扩展)
112
+ return <MarkdownEditor previewOptions={previewOptions} languages={{ ...languagesCommon }} />
109
113
  }
110
114
  ```
111
115
 
package/dist/App.d.ts DELETED
@@ -1,2 +0,0 @@
1
- declare function App(): import('react').JSX.Element;
2
- export default App;
package/dist/main.d.ts DELETED
File without changes