@pactor-app/markdown 1.58.0 → 1.59.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.
- package/README.md +36 -1
- package/dist/index.cjs +136 -0
- package/dist/index.d.cts +85 -4
- package/dist/index.d.ts +85 -4
- package/dist/index.js +156 -0
- package/package.json +4 -3
- package/styles.css +31 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @pactor-app/markdown
|
|
2
2
|
|
|
3
|
-
[Pactor](https://github.com/innospots/pactor) 的 Markdown 模块:安全的 Markdown 渲染器(`MarkdownRenderer
|
|
3
|
+
[Pactor](https://github.com/innospots/pactor) 的 Markdown 模块:安全的 Markdown 渲染器(`MarkdownRenderer`)、Markdown 富文本编辑器(`MarkdownEditor`,基于 [@mdxeditor/editor](https://github.com/mdx-editor/editor))与 DSL Markdown 组件适配(`type: Markdown`)。Chat 消息与 Page/Overlay DSL 共用同一渲染器。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -38,6 +38,26 @@ import '@pactor-app/markdown/styles.css';
|
|
|
38
38
|
|
|
39
39
|
样式全部作用域在 `.pactor-markdown` 容器内:颜色 / 圆角经 `--pactor-markdown-*` 令牌解析,优先跟随 shadcn 语义令牌(`--foreground` / `--border` / `--primary` 等,暗色主题自动适配),无宿主主题时回退 GitHub 风格配色(跟随系统明暗)。宿主可重定义令牌或以更高优先级规则覆盖(如 `@pactor-app/chat` 的消息流主题)。启用 `math` 时仍需自行引入 `katex/dist/katex.min.css`。
|
|
40
40
|
|
|
41
|
+
## 编辑器(MarkdownEditor)
|
|
42
|
+
|
|
43
|
+
受控 Markdown 富文本编辑器,基于 `@mdxeditor/editor`(Lexical 内核),默认带标题 / 列表 / 引用 / 链接 / 表格 / 代码块(CodeMirror)插件与工具栏:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { useState } from 'react';
|
|
47
|
+
import { MarkdownEditor } from '@pactor-app/markdown';
|
|
48
|
+
import '@mdxeditor/editor/style.css'; // 编辑器自身样式(必需)
|
|
49
|
+
import '@pactor-app/markdown/styles.css'; // 容器边框 / 圆角等令牌化样式(可选)
|
|
50
|
+
|
|
51
|
+
function Demo() {
|
|
52
|
+
const [value, setValue] = useState('# 标题\n\n开始编辑……');
|
|
53
|
+
return <MarkdownEditor value={value} onChange={setValue} />;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
常用 props:`readOnly`(隐藏工具栏)、`toolbar={false}`、`placeholder`、`renderToolbar`(自定义工具栏内容)、`plugins`(追加 MDXEditor 插件)、`editorProps`(透传底层 props)。容器带基础 class `pactor-markdown-editor`。注意:编辑器体积较大且依赖浏览器 DOM API,SSR 场景请动态导入(如 `next/dynamic` 的 `ssr: false`)。
|
|
58
|
+
|
|
59
|
+
`value` 为受控语义:用户输入经 `onChange` 回流不重复导入(光标不跳动);外部变更(如 DSL state 回写)经 `setMarkdown` 同步进编辑器。
|
|
60
|
+
|
|
41
61
|
## DSL 使用
|
|
42
62
|
|
|
43
63
|
```tsx
|
|
@@ -59,6 +79,21 @@ props:
|
|
|
59
79
|
|
|
60
80
|
`props.content` 中的表达式(`${data.xxx}` 等)由 Renderer 在下发前解析,组件拿到的已是最终文本。Chat 的 Safe Registry 场景可复用同一 `registerMarkdownComponents`。
|
|
61
81
|
|
|
82
|
+
`registerMarkdownComponents` 同时注册 `MarkdownEditor`(编辑器 DSL 类型)。`props.value` 支持表达式,`events.onChange` 的事件对象为新的 Markdown 字符串(`${event}`),典型写法是写回同一 state 形成受控回路:
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
type: MarkdownEditor
|
|
86
|
+
props:
|
|
87
|
+
value: '${state.draft}'
|
|
88
|
+
events:
|
|
89
|
+
onChange:
|
|
90
|
+
- action: setState
|
|
91
|
+
params:
|
|
92
|
+
draft: '${event}'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
支持 props:`value` / `placeholder` / `readOnly` / `toolbar` / `autoFocus` / `className` / `style`。注意:编辑器依赖浏览器 DOM API 与 `@mdxeditor/editor/style.css`,仅适合 Page DSL(浏览器页面),不要注册进 Chat 的 Safe Registry。
|
|
96
|
+
|
|
62
97
|
## 文档
|
|
63
98
|
|
|
64
99
|
完整文档见 <https://github.com/innospots/pactor/tree/main/docs>(`pnpm docs:dev` 本地启动文档站)。
|
package/dist/index.cjs
CHANGED
|
@@ -32,7 +32,10 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CodeBlock: () => CodeBlock,
|
|
34
34
|
MARKDOWN_DSL_TYPE: () => MARKDOWN_DSL_TYPE,
|
|
35
|
+
MARKDOWN_EDITOR_DSL_TYPE: () => MARKDOWN_EDITOR_DSL_TYPE,
|
|
35
36
|
MarkdownDslComponent: () => MarkdownDslComponent,
|
|
37
|
+
MarkdownEditor: () => MarkdownEditor,
|
|
38
|
+
MarkdownEditorDslComponent: () => MarkdownEditorDslComponent,
|
|
36
39
|
MarkdownRenderer: () => MarkdownRenderer,
|
|
37
40
|
isSafeUrl: () => isSafeUrl,
|
|
38
41
|
registerCodeLanguages: () => registerCodeLanguages,
|
|
@@ -233,16 +236,149 @@ function MarkdownDslComponent({
|
|
|
233
236
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MarkdownRenderer, { content: content ?? "", className, style });
|
|
234
237
|
}
|
|
235
238
|
|
|
239
|
+
// src/editor.tsx
|
|
240
|
+
var import_react3 = require("react");
|
|
241
|
+
var import_editor = require("@mdxeditor/editor");
|
|
242
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
243
|
+
var defaultToolbarContents = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
244
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.UndoRedo, {}),
|
|
245
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
246
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.BoldItalicUnderlineToggles, {}),
|
|
247
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.CodeToggle, {}),
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
249
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.ListsToggle, {}),
|
|
250
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
251
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.BlockTypeSelect, {}),
|
|
252
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
253
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.CreateLink, {}),
|
|
254
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.InsertTable, {}),
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.InsertThematicBreak, {}),
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.InsertCodeBlock, {})
|
|
257
|
+
] });
|
|
258
|
+
var MarkdownEditor = (0, import_react3.forwardRef)(
|
|
259
|
+
function MarkdownEditor2({
|
|
260
|
+
value = "",
|
|
261
|
+
onChange,
|
|
262
|
+
onBlur,
|
|
263
|
+
placeholder,
|
|
264
|
+
readOnly = false,
|
|
265
|
+
autoFocus,
|
|
266
|
+
className,
|
|
267
|
+
style,
|
|
268
|
+
toolbar = true,
|
|
269
|
+
renderToolbar,
|
|
270
|
+
plugins,
|
|
271
|
+
editorProps
|
|
272
|
+
}, ref) {
|
|
273
|
+
const showToolbar = toolbar && !readOnly;
|
|
274
|
+
const containerClass = className ? `pactor-markdown-editor ${className}` : "pactor-markdown-editor";
|
|
275
|
+
const innerRef = (0, import_react3.useRef)(null);
|
|
276
|
+
(0, import_react3.useImperativeHandle)(ref, () => innerRef.current, []);
|
|
277
|
+
const lastEmittedRef = (0, import_react3.useRef)(value);
|
|
278
|
+
(0, import_react3.useEffect)(() => {
|
|
279
|
+
if (value !== lastEmittedRef.current) {
|
|
280
|
+
lastEmittedRef.current = value;
|
|
281
|
+
innerRef.current?.setMarkdown(value);
|
|
282
|
+
}
|
|
283
|
+
}, [value]);
|
|
284
|
+
const handleChange = (next) => {
|
|
285
|
+
lastEmittedRef.current = next;
|
|
286
|
+
onChange?.(next);
|
|
287
|
+
};
|
|
288
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: containerClass, style, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
289
|
+
import_editor.MDXEditor,
|
|
290
|
+
{
|
|
291
|
+
ref: innerRef,
|
|
292
|
+
markdown: value,
|
|
293
|
+
onChange: handleChange,
|
|
294
|
+
onBlur,
|
|
295
|
+
readOnly,
|
|
296
|
+
autoFocus,
|
|
297
|
+
placeholder,
|
|
298
|
+
contentEditableClassName: "pactor-markdown-editor-content",
|
|
299
|
+
plugins: [
|
|
300
|
+
(0, import_editor.headingsPlugin)(),
|
|
301
|
+
(0, import_editor.listsPlugin)(),
|
|
302
|
+
(0, import_editor.quotePlugin)(),
|
|
303
|
+
(0, import_editor.thematicBreakPlugin)(),
|
|
304
|
+
(0, import_editor.linkPlugin)(),
|
|
305
|
+
(0, import_editor.linkDialogPlugin)(),
|
|
306
|
+
(0, import_editor.tablePlugin)(),
|
|
307
|
+
(0, import_editor.codeBlockPlugin)({ defaultCodeBlockLanguage: "" }),
|
|
308
|
+
(0, import_editor.codeMirrorPlugin)({
|
|
309
|
+
codeBlockLanguages: {
|
|
310
|
+
"": "Plain text",
|
|
311
|
+
js: "JavaScript",
|
|
312
|
+
ts: "TypeScript",
|
|
313
|
+
tsx: "TSX",
|
|
314
|
+
css: "CSS",
|
|
315
|
+
html: "HTML",
|
|
316
|
+
json: "JSON",
|
|
317
|
+
yaml: "YAML",
|
|
318
|
+
bash: "Bash",
|
|
319
|
+
python: "Python",
|
|
320
|
+
go: "Go",
|
|
321
|
+
rust: "Rust",
|
|
322
|
+
sql: "SQL"
|
|
323
|
+
}
|
|
324
|
+
}),
|
|
325
|
+
(0, import_editor.markdownShortcutPlugin)(),
|
|
326
|
+
...showToolbar ? [
|
|
327
|
+
(0, import_editor.toolbarPlugin)({
|
|
328
|
+
toolbarClassName: "pactor-markdown-editor-toolbar",
|
|
329
|
+
toolbarContents: () => renderToolbar ? renderToolbar(defaultToolbarContents) : defaultToolbarContents
|
|
330
|
+
})
|
|
331
|
+
] : [],
|
|
332
|
+
...plugins ?? []
|
|
333
|
+
],
|
|
334
|
+
...editorProps
|
|
335
|
+
}
|
|
336
|
+
) });
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// src/dsl-editor-component.tsx
|
|
341
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
342
|
+
function MarkdownEditorDslComponent({
|
|
343
|
+
value,
|
|
344
|
+
placeholder,
|
|
345
|
+
readOnly,
|
|
346
|
+
toolbar,
|
|
347
|
+
autoFocus,
|
|
348
|
+
onChange,
|
|
349
|
+
className,
|
|
350
|
+
style
|
|
351
|
+
}) {
|
|
352
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
353
|
+
MarkdownEditor,
|
|
354
|
+
{
|
|
355
|
+
value,
|
|
356
|
+
placeholder,
|
|
357
|
+
readOnly,
|
|
358
|
+
toolbar,
|
|
359
|
+
autoFocus,
|
|
360
|
+
onChange,
|
|
361
|
+
className,
|
|
362
|
+
style
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
236
367
|
// src/register.ts
|
|
237
368
|
var MARKDOWN_DSL_TYPE = "Markdown";
|
|
369
|
+
var MARKDOWN_EDITOR_DSL_TYPE = "MarkdownEditor";
|
|
238
370
|
function registerMarkdownComponents(registry) {
|
|
239
371
|
registry.register(MARKDOWN_DSL_TYPE, MarkdownDslComponent);
|
|
372
|
+
registry.register(MARKDOWN_EDITOR_DSL_TYPE, MarkdownEditorDslComponent);
|
|
240
373
|
}
|
|
241
374
|
// Annotate the CommonJS export names for ESM import in node:
|
|
242
375
|
0 && (module.exports = {
|
|
243
376
|
CodeBlock,
|
|
244
377
|
MARKDOWN_DSL_TYPE,
|
|
378
|
+
MARKDOWN_EDITOR_DSL_TYPE,
|
|
245
379
|
MarkdownDslComponent,
|
|
380
|
+
MarkdownEditor,
|
|
381
|
+
MarkdownEditorDslComponent,
|
|
246
382
|
MarkdownRenderer,
|
|
247
383
|
isSafeUrl,
|
|
248
384
|
registerCodeLanguages,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { CSSProperties } from 'react';
|
|
2
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
3
3
|
import { Registry } from '@pactor-app/core';
|
|
4
|
+
import { MDXEditorProps, MDXEditorMethods } from '@mdxeditor/editor';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 协议白名单检查:相对路径(无协议)放行;
|
|
@@ -52,10 +53,13 @@ declare function MarkdownDslComponent({ content, className, style, }: MarkdownDs
|
|
|
52
53
|
|
|
53
54
|
/** Markdown 组件的 DSL 类型名 */
|
|
54
55
|
declare const MARKDOWN_DSL_TYPE = "Markdown";
|
|
56
|
+
/** MarkdownEditor 组件的 DSL 类型名 */
|
|
57
|
+
declare const MARKDOWN_EDITOR_DSL_TYPE = "MarkdownEditor";
|
|
55
58
|
/**
|
|
56
|
-
* 把 Markdown DSL 组件注册到给定的 Component Registry
|
|
59
|
+
* 把 Markdown / MarkdownEditor DSL 组件注册到给定的 Component Registry
|
|
57
60
|
* (@pactor-app/runtime 的 `ComponentRegistry` 即 `Registry<unknown>`,
|
|
58
|
-
* @pactor-app/chat 的 Safe Registry
|
|
61
|
+
* @pactor-app/chat 的 Safe Registry 场景亦可复用——注意 Safe Registry
|
|
62
|
+
* 场景通常只需只读渲染,可不注册 MarkdownEditor)。
|
|
59
63
|
*/
|
|
60
64
|
declare function registerMarkdownComponents(registry: Registry<unknown>): void;
|
|
61
65
|
|
|
@@ -68,4 +72,81 @@ interface CodeBlockProps {
|
|
|
68
72
|
/** fenced 代码块高亮渲染(Prism Light + one-light / one-dark 随深浅模式切换) */
|
|
69
73
|
declare function CodeBlock({ language, children }: CodeBlockProps): react.JSX.Element;
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
/** 工具栏自定义渲染:拿到默认工具栏内容,可包裹 / 替换 */
|
|
76
|
+
type MarkdownEditorToolbarRender = (defaultContents: ReactNode) => ReactNode;
|
|
77
|
+
interface MarkdownEditorProps {
|
|
78
|
+
/** Markdown 源文本(受控) */
|
|
79
|
+
value?: string;
|
|
80
|
+
/** 内容变化回调(编辑器内部每次变更后触发) */
|
|
81
|
+
onChange?: (value: string) => void;
|
|
82
|
+
/** 失焦回调 */
|
|
83
|
+
onBlur?: (event: FocusEvent) => void;
|
|
84
|
+
/** 占位文本 */
|
|
85
|
+
placeholder?: ReactNode;
|
|
86
|
+
/** 只读模式(隐藏工具栏) */
|
|
87
|
+
readOnly?: boolean;
|
|
88
|
+
/** 自动聚焦 */
|
|
89
|
+
autoFocus?: boolean;
|
|
90
|
+
/** 附加 className,与基础 class `pactor-markdown-editor` 并存 */
|
|
91
|
+
className?: string;
|
|
92
|
+
/** 通用:根节点内联样式 */
|
|
93
|
+
style?: CSSProperties;
|
|
94
|
+
/** 是否渲染工具栏,默认 true(readOnly 时强制隐藏) */
|
|
95
|
+
toolbar?: boolean;
|
|
96
|
+
/** 自定义工具栏内容:参数为默认工具栏节点 */
|
|
97
|
+
renderToolbar?: MarkdownEditorToolbarRender;
|
|
98
|
+
/**
|
|
99
|
+
* 追加的 MDXEditor 插件(插在默认插件集之后)。
|
|
100
|
+
* 默认插件集:headings / lists / quote / thematicBreak / link(+dialog) /
|
|
101
|
+
* table / codeBlock(+codeMirror) / markdownShortcut,以及工具栏。
|
|
102
|
+
*/
|
|
103
|
+
plugins?: MDXEditorProps['plugins'];
|
|
104
|
+
/** 透传给底层 MDXEditor 的其余 props(会被上述显式 props 覆盖) */
|
|
105
|
+
editorProps?: Omit<MDXEditorProps, 'markdown' | 'onChange' | 'onBlur' | 'readOnly' | 'autoFocus' | 'placeholder' | 'plugins'>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Markdown 富文本编辑器:基于 @mdxeditor/editor(Lexical 内核)封装,
|
|
109
|
+
* 受控 `value` / `onChange`,默认带 GFM 常用插件与工具栏。
|
|
110
|
+
*
|
|
111
|
+
* 样式:使用方需引入 `@mdxeditor/editor/style.css`(编辑器自身样式),
|
|
112
|
+
* 容器外观令牌化样式见包根 `styles.css`
|
|
113
|
+
* (`import '@pactor-app/markdown/styles.css'`),可被宿主主题覆盖。
|
|
114
|
+
*
|
|
115
|
+
* 注意:编辑器体积较大且依赖浏览器 DOM API,SSR 场景请动态导入
|
|
116
|
+
* (如 next/dynamic 的 `ssr: false`)。
|
|
117
|
+
*/
|
|
118
|
+
declare const MarkdownEditor: react.ForwardRefExoticComponent<MarkdownEditorProps & react.RefAttributes<MDXEditorMethods>>;
|
|
119
|
+
|
|
120
|
+
interface MarkdownEditorDslComponentProps {
|
|
121
|
+
/**
|
|
122
|
+
* Markdown 内容(DSL props.value)。
|
|
123
|
+
* `${state.xxx}` 等表达式由 Renderer 在 props 下发前解析;
|
|
124
|
+
* 外部 value 变化经 setMarkdown 同步进编辑器(见 MarkdownEditor)。
|
|
125
|
+
*/
|
|
126
|
+
value?: string;
|
|
127
|
+
/** 占位文本 */
|
|
128
|
+
placeholder?: string;
|
|
129
|
+
/** 只读模式(隐藏工具栏;权限 denied 策略 readonly 注入同名字段) */
|
|
130
|
+
readOnly?: boolean;
|
|
131
|
+
/** 是否渲染工具栏,默认 true */
|
|
132
|
+
toolbar?: boolean;
|
|
133
|
+
/** 自动聚焦 */
|
|
134
|
+
autoFocus?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* 内容变化回调(由 DSL events.onChange 绑定注入):
|
|
137
|
+
* 事件对象为新的 Markdown 字符串,action 中以 `${event}` 引用。
|
|
138
|
+
*/
|
|
139
|
+
onChange?: (value: string) => void;
|
|
140
|
+
/** 通用:根节点 class(与基础 class 并存) */
|
|
141
|
+
className?: string;
|
|
142
|
+
/** 通用:根节点内联样式 */
|
|
143
|
+
style?: CSSProperties;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* DSL 组件适配:`{ type: 'MarkdownEditor', props: { value }, events: { onChange } }`。
|
|
147
|
+
* 仅做 props 透传,编辑能力全部委托给 MarkdownEditor。
|
|
148
|
+
* 典型用法:value 绑定 state 表达式,onChange action 把 `${event}` 写回同一 state。
|
|
149
|
+
*/
|
|
150
|
+
declare function MarkdownEditorDslComponent({ value, placeholder, readOnly, toolbar, autoFocus, onChange, className, style, }: MarkdownEditorDslComponentProps): react.JSX.Element;
|
|
151
|
+
|
|
152
|
+
export { CodeBlock, type CodeBlockProps, MARKDOWN_DSL_TYPE, MARKDOWN_EDITOR_DSL_TYPE, MarkdownDslComponent, type MarkdownDslComponentProps, MarkdownEditor, MarkdownEditorDslComponent, type MarkdownEditorDslComponentProps, type MarkdownEditorProps, type MarkdownEditorToolbarRender, MarkdownRenderer, type MarkdownRendererProps, isSafeUrl, registerCodeLanguages, registerMarkdownComponents };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { CSSProperties } from 'react';
|
|
2
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
3
3
|
import { Registry } from '@pactor-app/core';
|
|
4
|
+
import { MDXEditorProps, MDXEditorMethods } from '@mdxeditor/editor';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 协议白名单检查:相对路径(无协议)放行;
|
|
@@ -52,10 +53,13 @@ declare function MarkdownDslComponent({ content, className, style, }: MarkdownDs
|
|
|
52
53
|
|
|
53
54
|
/** Markdown 组件的 DSL 类型名 */
|
|
54
55
|
declare const MARKDOWN_DSL_TYPE = "Markdown";
|
|
56
|
+
/** MarkdownEditor 组件的 DSL 类型名 */
|
|
57
|
+
declare const MARKDOWN_EDITOR_DSL_TYPE = "MarkdownEditor";
|
|
55
58
|
/**
|
|
56
|
-
* 把 Markdown DSL 组件注册到给定的 Component Registry
|
|
59
|
+
* 把 Markdown / MarkdownEditor DSL 组件注册到给定的 Component Registry
|
|
57
60
|
* (@pactor-app/runtime 的 `ComponentRegistry` 即 `Registry<unknown>`,
|
|
58
|
-
* @pactor-app/chat 的 Safe Registry
|
|
61
|
+
* @pactor-app/chat 的 Safe Registry 场景亦可复用——注意 Safe Registry
|
|
62
|
+
* 场景通常只需只读渲染,可不注册 MarkdownEditor)。
|
|
59
63
|
*/
|
|
60
64
|
declare function registerMarkdownComponents(registry: Registry<unknown>): void;
|
|
61
65
|
|
|
@@ -68,4 +72,81 @@ interface CodeBlockProps {
|
|
|
68
72
|
/** fenced 代码块高亮渲染(Prism Light + one-light / one-dark 随深浅模式切换) */
|
|
69
73
|
declare function CodeBlock({ language, children }: CodeBlockProps): react.JSX.Element;
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
/** 工具栏自定义渲染:拿到默认工具栏内容,可包裹 / 替换 */
|
|
76
|
+
type MarkdownEditorToolbarRender = (defaultContents: ReactNode) => ReactNode;
|
|
77
|
+
interface MarkdownEditorProps {
|
|
78
|
+
/** Markdown 源文本(受控) */
|
|
79
|
+
value?: string;
|
|
80
|
+
/** 内容变化回调(编辑器内部每次变更后触发) */
|
|
81
|
+
onChange?: (value: string) => void;
|
|
82
|
+
/** 失焦回调 */
|
|
83
|
+
onBlur?: (event: FocusEvent) => void;
|
|
84
|
+
/** 占位文本 */
|
|
85
|
+
placeholder?: ReactNode;
|
|
86
|
+
/** 只读模式(隐藏工具栏) */
|
|
87
|
+
readOnly?: boolean;
|
|
88
|
+
/** 自动聚焦 */
|
|
89
|
+
autoFocus?: boolean;
|
|
90
|
+
/** 附加 className,与基础 class `pactor-markdown-editor` 并存 */
|
|
91
|
+
className?: string;
|
|
92
|
+
/** 通用:根节点内联样式 */
|
|
93
|
+
style?: CSSProperties;
|
|
94
|
+
/** 是否渲染工具栏,默认 true(readOnly 时强制隐藏) */
|
|
95
|
+
toolbar?: boolean;
|
|
96
|
+
/** 自定义工具栏内容:参数为默认工具栏节点 */
|
|
97
|
+
renderToolbar?: MarkdownEditorToolbarRender;
|
|
98
|
+
/**
|
|
99
|
+
* 追加的 MDXEditor 插件(插在默认插件集之后)。
|
|
100
|
+
* 默认插件集:headings / lists / quote / thematicBreak / link(+dialog) /
|
|
101
|
+
* table / codeBlock(+codeMirror) / markdownShortcut,以及工具栏。
|
|
102
|
+
*/
|
|
103
|
+
plugins?: MDXEditorProps['plugins'];
|
|
104
|
+
/** 透传给底层 MDXEditor 的其余 props(会被上述显式 props 覆盖) */
|
|
105
|
+
editorProps?: Omit<MDXEditorProps, 'markdown' | 'onChange' | 'onBlur' | 'readOnly' | 'autoFocus' | 'placeholder' | 'plugins'>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Markdown 富文本编辑器:基于 @mdxeditor/editor(Lexical 内核)封装,
|
|
109
|
+
* 受控 `value` / `onChange`,默认带 GFM 常用插件与工具栏。
|
|
110
|
+
*
|
|
111
|
+
* 样式:使用方需引入 `@mdxeditor/editor/style.css`(编辑器自身样式),
|
|
112
|
+
* 容器外观令牌化样式见包根 `styles.css`
|
|
113
|
+
* (`import '@pactor-app/markdown/styles.css'`),可被宿主主题覆盖。
|
|
114
|
+
*
|
|
115
|
+
* 注意:编辑器体积较大且依赖浏览器 DOM API,SSR 场景请动态导入
|
|
116
|
+
* (如 next/dynamic 的 `ssr: false`)。
|
|
117
|
+
*/
|
|
118
|
+
declare const MarkdownEditor: react.ForwardRefExoticComponent<MarkdownEditorProps & react.RefAttributes<MDXEditorMethods>>;
|
|
119
|
+
|
|
120
|
+
interface MarkdownEditorDslComponentProps {
|
|
121
|
+
/**
|
|
122
|
+
* Markdown 内容(DSL props.value)。
|
|
123
|
+
* `${state.xxx}` 等表达式由 Renderer 在 props 下发前解析;
|
|
124
|
+
* 外部 value 变化经 setMarkdown 同步进编辑器(见 MarkdownEditor)。
|
|
125
|
+
*/
|
|
126
|
+
value?: string;
|
|
127
|
+
/** 占位文本 */
|
|
128
|
+
placeholder?: string;
|
|
129
|
+
/** 只读模式(隐藏工具栏;权限 denied 策略 readonly 注入同名字段) */
|
|
130
|
+
readOnly?: boolean;
|
|
131
|
+
/** 是否渲染工具栏,默认 true */
|
|
132
|
+
toolbar?: boolean;
|
|
133
|
+
/** 自动聚焦 */
|
|
134
|
+
autoFocus?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* 内容变化回调(由 DSL events.onChange 绑定注入):
|
|
137
|
+
* 事件对象为新的 Markdown 字符串,action 中以 `${event}` 引用。
|
|
138
|
+
*/
|
|
139
|
+
onChange?: (value: string) => void;
|
|
140
|
+
/** 通用:根节点 class(与基础 class 并存) */
|
|
141
|
+
className?: string;
|
|
142
|
+
/** 通用:根节点内联样式 */
|
|
143
|
+
style?: CSSProperties;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* DSL 组件适配:`{ type: 'MarkdownEditor', props: { value }, events: { onChange } }`。
|
|
147
|
+
* 仅做 props 透传,编辑能力全部委托给 MarkdownEditor。
|
|
148
|
+
* 典型用法:value 绑定 state 表达式,onChange action 把 `${event}` 写回同一 state。
|
|
149
|
+
*/
|
|
150
|
+
declare function MarkdownEditorDslComponent({ value, placeholder, readOnly, toolbar, autoFocus, onChange, className, style, }: MarkdownEditorDslComponentProps): react.JSX.Element;
|
|
151
|
+
|
|
152
|
+
export { CodeBlock, type CodeBlockProps, MARKDOWN_DSL_TYPE, MARKDOWN_EDITOR_DSL_TYPE, MarkdownDslComponent, type MarkdownDslComponentProps, MarkdownEditor, MarkdownEditorDslComponent, type MarkdownEditorDslComponentProps, type MarkdownEditorProps, type MarkdownEditorToolbarRender, MarkdownRenderer, type MarkdownRendererProps, isSafeUrl, registerCodeLanguages, registerMarkdownComponents };
|
package/dist/index.js
CHANGED
|
@@ -191,15 +191,171 @@ function MarkdownDslComponent({
|
|
|
191
191
|
return /* @__PURE__ */ jsx4(MarkdownRenderer, { content: content ?? "", className, style });
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
// src/editor.tsx
|
|
195
|
+
import { forwardRef, useEffect as useEffect2, useImperativeHandle, useRef } from "react";
|
|
196
|
+
import {
|
|
197
|
+
BlockTypeSelect,
|
|
198
|
+
BoldItalicUnderlineToggles,
|
|
199
|
+
codeBlockPlugin,
|
|
200
|
+
codeMirrorPlugin,
|
|
201
|
+
CodeToggle,
|
|
202
|
+
CreateLink,
|
|
203
|
+
headingsPlugin,
|
|
204
|
+
InsertCodeBlock,
|
|
205
|
+
InsertTable,
|
|
206
|
+
InsertThematicBreak,
|
|
207
|
+
linkDialogPlugin,
|
|
208
|
+
linkPlugin,
|
|
209
|
+
listsPlugin,
|
|
210
|
+
ListsToggle,
|
|
211
|
+
markdownShortcutPlugin,
|
|
212
|
+
MDXEditor,
|
|
213
|
+
quotePlugin,
|
|
214
|
+
Separator,
|
|
215
|
+
tablePlugin,
|
|
216
|
+
thematicBreakPlugin,
|
|
217
|
+
toolbarPlugin,
|
|
218
|
+
UndoRedo
|
|
219
|
+
} from "@mdxeditor/editor";
|
|
220
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs } from "react/jsx-runtime";
|
|
221
|
+
var defaultToolbarContents = /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
222
|
+
/* @__PURE__ */ jsx5(UndoRedo, {}),
|
|
223
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
224
|
+
/* @__PURE__ */ jsx5(BoldItalicUnderlineToggles, {}),
|
|
225
|
+
/* @__PURE__ */ jsx5(CodeToggle, {}),
|
|
226
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
227
|
+
/* @__PURE__ */ jsx5(ListsToggle, {}),
|
|
228
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
229
|
+
/* @__PURE__ */ jsx5(BlockTypeSelect, {}),
|
|
230
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
231
|
+
/* @__PURE__ */ jsx5(CreateLink, {}),
|
|
232
|
+
/* @__PURE__ */ jsx5(InsertTable, {}),
|
|
233
|
+
/* @__PURE__ */ jsx5(InsertThematicBreak, {}),
|
|
234
|
+
/* @__PURE__ */ jsx5(InsertCodeBlock, {})
|
|
235
|
+
] });
|
|
236
|
+
var MarkdownEditor = forwardRef(
|
|
237
|
+
function MarkdownEditor2({
|
|
238
|
+
value = "",
|
|
239
|
+
onChange,
|
|
240
|
+
onBlur,
|
|
241
|
+
placeholder,
|
|
242
|
+
readOnly = false,
|
|
243
|
+
autoFocus,
|
|
244
|
+
className,
|
|
245
|
+
style,
|
|
246
|
+
toolbar = true,
|
|
247
|
+
renderToolbar,
|
|
248
|
+
plugins,
|
|
249
|
+
editorProps
|
|
250
|
+
}, ref) {
|
|
251
|
+
const showToolbar = toolbar && !readOnly;
|
|
252
|
+
const containerClass = className ? `pactor-markdown-editor ${className}` : "pactor-markdown-editor";
|
|
253
|
+
const innerRef = useRef(null);
|
|
254
|
+
useImperativeHandle(ref, () => innerRef.current, []);
|
|
255
|
+
const lastEmittedRef = useRef(value);
|
|
256
|
+
useEffect2(() => {
|
|
257
|
+
if (value !== lastEmittedRef.current) {
|
|
258
|
+
lastEmittedRef.current = value;
|
|
259
|
+
innerRef.current?.setMarkdown(value);
|
|
260
|
+
}
|
|
261
|
+
}, [value]);
|
|
262
|
+
const handleChange = (next) => {
|
|
263
|
+
lastEmittedRef.current = next;
|
|
264
|
+
onChange?.(next);
|
|
265
|
+
};
|
|
266
|
+
return /* @__PURE__ */ jsx5("div", { className: containerClass, style, children: /* @__PURE__ */ jsx5(
|
|
267
|
+
MDXEditor,
|
|
268
|
+
{
|
|
269
|
+
ref: innerRef,
|
|
270
|
+
markdown: value,
|
|
271
|
+
onChange: handleChange,
|
|
272
|
+
onBlur,
|
|
273
|
+
readOnly,
|
|
274
|
+
autoFocus,
|
|
275
|
+
placeholder,
|
|
276
|
+
contentEditableClassName: "pactor-markdown-editor-content",
|
|
277
|
+
plugins: [
|
|
278
|
+
headingsPlugin(),
|
|
279
|
+
listsPlugin(),
|
|
280
|
+
quotePlugin(),
|
|
281
|
+
thematicBreakPlugin(),
|
|
282
|
+
linkPlugin(),
|
|
283
|
+
linkDialogPlugin(),
|
|
284
|
+
tablePlugin(),
|
|
285
|
+
codeBlockPlugin({ defaultCodeBlockLanguage: "" }),
|
|
286
|
+
codeMirrorPlugin({
|
|
287
|
+
codeBlockLanguages: {
|
|
288
|
+
"": "Plain text",
|
|
289
|
+
js: "JavaScript",
|
|
290
|
+
ts: "TypeScript",
|
|
291
|
+
tsx: "TSX",
|
|
292
|
+
css: "CSS",
|
|
293
|
+
html: "HTML",
|
|
294
|
+
json: "JSON",
|
|
295
|
+
yaml: "YAML",
|
|
296
|
+
bash: "Bash",
|
|
297
|
+
python: "Python",
|
|
298
|
+
go: "Go",
|
|
299
|
+
rust: "Rust",
|
|
300
|
+
sql: "SQL"
|
|
301
|
+
}
|
|
302
|
+
}),
|
|
303
|
+
markdownShortcutPlugin(),
|
|
304
|
+
...showToolbar ? [
|
|
305
|
+
toolbarPlugin({
|
|
306
|
+
toolbarClassName: "pactor-markdown-editor-toolbar",
|
|
307
|
+
toolbarContents: () => renderToolbar ? renderToolbar(defaultToolbarContents) : defaultToolbarContents
|
|
308
|
+
})
|
|
309
|
+
] : [],
|
|
310
|
+
...plugins ?? []
|
|
311
|
+
],
|
|
312
|
+
...editorProps
|
|
313
|
+
}
|
|
314
|
+
) });
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// src/dsl-editor-component.tsx
|
|
319
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
320
|
+
function MarkdownEditorDslComponent({
|
|
321
|
+
value,
|
|
322
|
+
placeholder,
|
|
323
|
+
readOnly,
|
|
324
|
+
toolbar,
|
|
325
|
+
autoFocus,
|
|
326
|
+
onChange,
|
|
327
|
+
className,
|
|
328
|
+
style
|
|
329
|
+
}) {
|
|
330
|
+
return /* @__PURE__ */ jsx6(
|
|
331
|
+
MarkdownEditor,
|
|
332
|
+
{
|
|
333
|
+
value,
|
|
334
|
+
placeholder,
|
|
335
|
+
readOnly,
|
|
336
|
+
toolbar,
|
|
337
|
+
autoFocus,
|
|
338
|
+
onChange,
|
|
339
|
+
className,
|
|
340
|
+
style
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
194
345
|
// src/register.ts
|
|
195
346
|
var MARKDOWN_DSL_TYPE = "Markdown";
|
|
347
|
+
var MARKDOWN_EDITOR_DSL_TYPE = "MarkdownEditor";
|
|
196
348
|
function registerMarkdownComponents(registry) {
|
|
197
349
|
registry.register(MARKDOWN_DSL_TYPE, MarkdownDslComponent);
|
|
350
|
+
registry.register(MARKDOWN_EDITOR_DSL_TYPE, MarkdownEditorDslComponent);
|
|
198
351
|
}
|
|
199
352
|
export {
|
|
200
353
|
CodeBlock,
|
|
201
354
|
MARKDOWN_DSL_TYPE,
|
|
355
|
+
MARKDOWN_EDITOR_DSL_TYPE,
|
|
202
356
|
MarkdownDslComponent,
|
|
357
|
+
MarkdownEditor,
|
|
358
|
+
MarkdownEditorDslComponent,
|
|
203
359
|
MarkdownRenderer,
|
|
204
360
|
isSafeUrl,
|
|
205
361
|
registerCodeLanguages,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pactor-app/markdown",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.59.0",
|
|
4
4
|
"description": "Pactor Markdown module: safe Markdown renderer (react-markdown AST, no raw HTML) and DSL Markdown component adapter",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pactor",
|
|
@@ -48,13 +48,14 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"@mdxeditor/editor": "^4.3.4",
|
|
51
52
|
"katex": "^0.18.4",
|
|
52
53
|
"react-markdown": "^10.1.0",
|
|
53
54
|
"react-syntax-highlighter": "^16.1.1",
|
|
54
55
|
"rehype-katex": "^7.0.1",
|
|
55
56
|
"remark-gfm": "^4.0.1",
|
|
56
57
|
"remark-math": "^6.0.0",
|
|
57
|
-
"@pactor-app/core": "1.
|
|
58
|
+
"@pactor-app/core": "1.59.0"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
60
61
|
"react": ">=18"
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
"jsdom": "^30.0.1",
|
|
69
70
|
"react": "^19.2.8",
|
|
70
71
|
"react-dom": "^19.2.8",
|
|
71
|
-
"@pactor-app/runtime": "1.
|
|
72
|
+
"@pactor-app/runtime": "1.59.0"
|
|
72
73
|
},
|
|
73
74
|
"scripts": {
|
|
74
75
|
"build": "tsup",
|
package/styles.css
CHANGED
|
@@ -259,3 +259,34 @@
|
|
|
259
259
|
overflow-x: auto;
|
|
260
260
|
overflow-y: hidden;
|
|
261
261
|
}
|
|
262
|
+
|
|
263
|
+
/* ---------- MarkdownEditor(@mdxeditor/editor 容器外观) ---------- */
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 编辑器自身排版样式来自 `@mdxeditor/editor/style.css`(需使用方引入),
|
|
267
|
+
* 这里只定义容器边框 / 圆角 / 内边距,令牌与 .pactor-markdown 保持一致。
|
|
268
|
+
*/
|
|
269
|
+
.pactor-markdown-editor {
|
|
270
|
+
--pactor-markdown-fg: var(--foreground, light-dark(#1f2328, #e6edf3));
|
|
271
|
+
--pactor-markdown-border: var(--border, light-dark(#d1d9e0, #3d444d));
|
|
272
|
+
--pactor-markdown-radius: var(--radius, 6px);
|
|
273
|
+
|
|
274
|
+
border: 1px solid var(--pactor-markdown-border);
|
|
275
|
+
border-radius: var(--pactor-markdown-radius);
|
|
276
|
+
color: var(--pactor-markdown-fg);
|
|
277
|
+
overflow: hidden;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.pactor-markdown-editor:focus-within {
|
|
281
|
+
border-color: var(--primary, light-dark(#0969da, #4493f8));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.pactor-markdown-editor .pactor-markdown-editor-toolbar {
|
|
285
|
+
border-bottom: 1px solid var(--pactor-markdown-border);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.pactor-markdown-editor .pactor-markdown-editor-content {
|
|
289
|
+
padding: 8px 12px;
|
|
290
|
+
min-height: 8em;
|
|
291
|
+
outline: none;
|
|
292
|
+
}
|