@pactor-app/markdown 1.58.0 → 1.60.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 +38 -1
- package/dist/index.cjs +186 -0
- package/dist/index.d.cts +89 -4
- package/dist/index.d.ts +89 -4
- package/dist/index.js +206 -0
- package/package.json +4 -3
- package/styles.css +70 -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,28 @@ 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`、`theme`(`'light' | 'dark'`)、`renderToolbar`(自定义工具栏内容)、`plugins`(追加 MDXEditor 插件)、`editorProps`(透传底层 props)。容器带基础 class `pactor-markdown-editor`。注意:编辑器体积较大且依赖浏览器 DOM API,SSR 场景请动态导入(如 `next/dynamic` 的 `ssr: false`)。
|
|
58
|
+
|
|
59
|
+
`value` 为受控语义:用户输入经 `onChange` 回流不重复导入(光标不跳动);外部变更(如 DSL state 回写)经 `setMarkdown` 同步进编辑器。
|
|
60
|
+
|
|
61
|
+
明暗模式:`theme` 缺省跟随 `documentElement` 上的 `data-pactor-theme`(pactor 换肤约定)并监听其变化自动切换,也可显式传 `'light' | 'dark'` 固定。主题 class 会同时应用到编辑根节点与 portal 弹层的 `.mdxeditor-popup-container`(工具栏下拉、链接弹层),且原地切换、不重建弹层容器——打开中的下拉不会因换肤丢失;容器上还带 `data-theme` 属性便于宿主样式覆盖。
|
|
62
|
+
|
|
41
63
|
## DSL 使用
|
|
42
64
|
|
|
43
65
|
```tsx
|
|
@@ -59,6 +81,21 @@ props:
|
|
|
59
81
|
|
|
60
82
|
`props.content` 中的表达式(`${data.xxx}` 等)由 Renderer 在下发前解析,组件拿到的已是最终文本。Chat 的 Safe Registry 场景可复用同一 `registerMarkdownComponents`。
|
|
61
83
|
|
|
84
|
+
`registerMarkdownComponents` 同时注册 `MarkdownEditor`(编辑器 DSL 类型)。`props.value` 支持表达式,`events.onChange` 的事件对象为新的 Markdown 字符串(`${event}`),典型写法是写回同一 state 形成受控回路:
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
type: MarkdownEditor
|
|
88
|
+
props:
|
|
89
|
+
value: '${state.draft}'
|
|
90
|
+
events:
|
|
91
|
+
onChange:
|
|
92
|
+
- action: setState
|
|
93
|
+
params:
|
|
94
|
+
draft: '${event}'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
支持 props:`value` / `placeholder` / `readOnly` / `toolbar` / `autoFocus` / `theme` / `className` / `style`。`theme` 与组件用法一致:缺省跟随 `data-pactor-theme`,也可在 YAML 中写 `theme: dark` 固定。注意:编辑器依赖浏览器 DOM API 与 `@mdxeditor/editor/style.css`,仅适合 Page DSL(浏览器页面),不要注册进 Chat 的 Safe Registry。
|
|
98
|
+
|
|
62
99
|
## 文档
|
|
63
100
|
|
|
64
101
|
完整文档见 <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,199 @@ 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
|
+
function usePactorTheme() {
|
|
244
|
+
const read = () => typeof document !== "undefined" && document.documentElement.getAttribute("data-pactor-theme") === "dark" ? "dark" : "light";
|
|
245
|
+
const [theme, setTheme] = (0, import_react3.useState)(read);
|
|
246
|
+
(0, import_react3.useEffect)(() => {
|
|
247
|
+
if (typeof document === "undefined") return;
|
|
248
|
+
const observer = new MutationObserver(() => setTheme(read()));
|
|
249
|
+
observer.observe(document.documentElement, {
|
|
250
|
+
attributes: true,
|
|
251
|
+
attributeFilter: ["data-pactor-theme"]
|
|
252
|
+
});
|
|
253
|
+
return () => observer.disconnect();
|
|
254
|
+
}, []);
|
|
255
|
+
return theme;
|
|
256
|
+
}
|
|
257
|
+
var defaultToolbarContents = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
258
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.UndoRedo, {}),
|
|
259
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
260
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.BoldItalicUnderlineToggles, {}),
|
|
261
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.CodeToggle, {}),
|
|
262
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
263
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.ListsToggle, {}),
|
|
264
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
265
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.BlockTypeSelect, {}),
|
|
266
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.Separator, {}),
|
|
267
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.CreateLink, {}),
|
|
268
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.InsertTable, {}),
|
|
269
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.InsertThematicBreak, {}),
|
|
270
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_editor.InsertCodeBlock, {})
|
|
271
|
+
] });
|
|
272
|
+
var MarkdownEditor = (0, import_react3.forwardRef)(
|
|
273
|
+
function MarkdownEditor2({
|
|
274
|
+
value = "",
|
|
275
|
+
onChange,
|
|
276
|
+
onBlur,
|
|
277
|
+
placeholder,
|
|
278
|
+
readOnly = false,
|
|
279
|
+
autoFocus,
|
|
280
|
+
className,
|
|
281
|
+
style,
|
|
282
|
+
theme,
|
|
283
|
+
toolbar = true,
|
|
284
|
+
renderToolbar,
|
|
285
|
+
plugins,
|
|
286
|
+
editorProps
|
|
287
|
+
}, ref) {
|
|
288
|
+
const showToolbar = toolbar && !readOnly;
|
|
289
|
+
const containerClass = className ? `pactor-markdown-editor ${className}` : "pactor-markdown-editor";
|
|
290
|
+
const pactorTheme = usePactorTheme();
|
|
291
|
+
const resolvedTheme = theme ?? pactorTheme;
|
|
292
|
+
const instanceId = (0, import_react3.useId)().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
293
|
+
const instanceClass = `pactor-markdown-editor-instance-${instanceId}`;
|
|
294
|
+
const mdxEditorClassName = [
|
|
295
|
+
"pactor-markdown-editor-root",
|
|
296
|
+
instanceClass,
|
|
297
|
+
editorProps?.className
|
|
298
|
+
].filter(Boolean).join(" ");
|
|
299
|
+
(0, import_react3.useEffect)(() => {
|
|
300
|
+
if (typeof document === "undefined") return;
|
|
301
|
+
const selector = `.${instanceClass}`;
|
|
302
|
+
const applyTheme = () => {
|
|
303
|
+
document.querySelectorAll(selector).forEach((element) => {
|
|
304
|
+
element.classList.toggle("dark", resolvedTheme === "dark");
|
|
305
|
+
element.classList.toggle("light", resolvedTheme === "light");
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
applyTheme();
|
|
309
|
+
const observer = new MutationObserver((records) => {
|
|
310
|
+
for (const record of records) {
|
|
311
|
+
for (const node of record.addedNodes) {
|
|
312
|
+
if (node instanceof Element && (node.matches(selector) || node.querySelector(selector))) {
|
|
313
|
+
applyTheme();
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
320
|
+
return () => observer.disconnect();
|
|
321
|
+
}, [instanceClass, resolvedTheme]);
|
|
322
|
+
const innerRef = (0, import_react3.useRef)(null);
|
|
323
|
+
(0, import_react3.useImperativeHandle)(ref, () => innerRef.current, []);
|
|
324
|
+
const lastEmittedRef = (0, import_react3.useRef)(value);
|
|
325
|
+
(0, import_react3.useEffect)(() => {
|
|
326
|
+
if (value !== lastEmittedRef.current) {
|
|
327
|
+
lastEmittedRef.current = value;
|
|
328
|
+
innerRef.current?.setMarkdown(value);
|
|
329
|
+
}
|
|
330
|
+
}, [value]);
|
|
331
|
+
const handleChange = (next) => {
|
|
332
|
+
lastEmittedRef.current = next;
|
|
333
|
+
onChange?.(next);
|
|
334
|
+
};
|
|
335
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: containerClass, style, "data-theme": resolvedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
336
|
+
import_editor.MDXEditor,
|
|
337
|
+
{
|
|
338
|
+
ref: innerRef,
|
|
339
|
+
markdown: value,
|
|
340
|
+
onChange: handleChange,
|
|
341
|
+
onBlur,
|
|
342
|
+
readOnly,
|
|
343
|
+
autoFocus,
|
|
344
|
+
placeholder,
|
|
345
|
+
contentEditableClassName: "pactor-markdown-editor-content",
|
|
346
|
+
plugins: [
|
|
347
|
+
(0, import_editor.headingsPlugin)(),
|
|
348
|
+
(0, import_editor.listsPlugin)(),
|
|
349
|
+
(0, import_editor.quotePlugin)(),
|
|
350
|
+
(0, import_editor.thematicBreakPlugin)(),
|
|
351
|
+
(0, import_editor.linkPlugin)(),
|
|
352
|
+
(0, import_editor.linkDialogPlugin)(),
|
|
353
|
+
(0, import_editor.tablePlugin)(),
|
|
354
|
+
(0, import_editor.codeBlockPlugin)({ defaultCodeBlockLanguage: "" }),
|
|
355
|
+
(0, import_editor.codeMirrorPlugin)({
|
|
356
|
+
codeBlockLanguages: {
|
|
357
|
+
"": "Plain text",
|
|
358
|
+
js: "JavaScript",
|
|
359
|
+
ts: "TypeScript",
|
|
360
|
+
tsx: "TSX",
|
|
361
|
+
css: "CSS",
|
|
362
|
+
html: "HTML",
|
|
363
|
+
json: "JSON",
|
|
364
|
+
yaml: "YAML",
|
|
365
|
+
bash: "Bash",
|
|
366
|
+
python: "Python",
|
|
367
|
+
go: "Go",
|
|
368
|
+
rust: "Rust",
|
|
369
|
+
sql: "SQL"
|
|
370
|
+
}
|
|
371
|
+
}),
|
|
372
|
+
(0, import_editor.markdownShortcutPlugin)(),
|
|
373
|
+
...showToolbar ? [
|
|
374
|
+
(0, import_editor.toolbarPlugin)({
|
|
375
|
+
toolbarClassName: "pactor-markdown-editor-toolbar",
|
|
376
|
+
toolbarContents: () => renderToolbar ? renderToolbar(defaultToolbarContents) : defaultToolbarContents
|
|
377
|
+
})
|
|
378
|
+
] : [],
|
|
379
|
+
...plugins ?? []
|
|
380
|
+
],
|
|
381
|
+
...editorProps,
|
|
382
|
+
className: mdxEditorClassName
|
|
383
|
+
}
|
|
384
|
+
) });
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
// src/dsl-editor-component.tsx
|
|
389
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
390
|
+
function MarkdownEditorDslComponent({
|
|
391
|
+
value,
|
|
392
|
+
placeholder,
|
|
393
|
+
readOnly,
|
|
394
|
+
toolbar,
|
|
395
|
+
autoFocus,
|
|
396
|
+
onChange,
|
|
397
|
+
className,
|
|
398
|
+
style,
|
|
399
|
+
theme
|
|
400
|
+
}) {
|
|
401
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
402
|
+
MarkdownEditor,
|
|
403
|
+
{
|
|
404
|
+
value,
|
|
405
|
+
placeholder,
|
|
406
|
+
readOnly,
|
|
407
|
+
toolbar,
|
|
408
|
+
autoFocus,
|
|
409
|
+
onChange,
|
|
410
|
+
className,
|
|
411
|
+
style,
|
|
412
|
+
theme
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
236
417
|
// src/register.ts
|
|
237
418
|
var MARKDOWN_DSL_TYPE = "Markdown";
|
|
419
|
+
var MARKDOWN_EDITOR_DSL_TYPE = "MarkdownEditor";
|
|
238
420
|
function registerMarkdownComponents(registry) {
|
|
239
421
|
registry.register(MARKDOWN_DSL_TYPE, MarkdownDslComponent);
|
|
422
|
+
registry.register(MARKDOWN_EDITOR_DSL_TYPE, MarkdownEditorDslComponent);
|
|
240
423
|
}
|
|
241
424
|
// Annotate the CommonJS export names for ESM import in node:
|
|
242
425
|
0 && (module.exports = {
|
|
243
426
|
CodeBlock,
|
|
244
427
|
MARKDOWN_DSL_TYPE,
|
|
428
|
+
MARKDOWN_EDITOR_DSL_TYPE,
|
|
245
429
|
MarkdownDslComponent,
|
|
430
|
+
MarkdownEditor,
|
|
431
|
+
MarkdownEditorDslComponent,
|
|
246
432
|
MarkdownRenderer,
|
|
247
433
|
isSafeUrl,
|
|
248
434
|
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,85 @@ 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
|
+
/** 主题:缺省跟随 documentElement 的 data-pactor-theme(与库换肤机制一致),可显式覆盖 */
|
|
95
|
+
theme?: 'light' | 'dark';
|
|
96
|
+
/** 是否渲染工具栏,默认 true(readOnly 时强制隐藏) */
|
|
97
|
+
toolbar?: boolean;
|
|
98
|
+
/** 自定义工具栏内容:参数为默认工具栏节点 */
|
|
99
|
+
renderToolbar?: MarkdownEditorToolbarRender;
|
|
100
|
+
/**
|
|
101
|
+
* 追加的 MDXEditor 插件(插在默认插件集之后)。
|
|
102
|
+
* 默认插件集:headings / lists / quote / thematicBreak / link(+dialog) /
|
|
103
|
+
* table / codeBlock(+codeMirror) / markdownShortcut,以及工具栏。
|
|
104
|
+
*/
|
|
105
|
+
plugins?: MDXEditorProps['plugins'];
|
|
106
|
+
/** 透传给底层 MDXEditor 的其余 props(会被上述显式 props 覆盖) */
|
|
107
|
+
editorProps?: Omit<MDXEditorProps, 'markdown' | 'onChange' | 'onBlur' | 'readOnly' | 'autoFocus' | 'placeholder' | 'plugins'>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Markdown 富文本编辑器:基于 @mdxeditor/editor(Lexical 内核)封装,
|
|
111
|
+
* 受控 `value` / `onChange`,默认带 GFM 常用插件与工具栏。
|
|
112
|
+
*
|
|
113
|
+
* 样式:使用方需引入 `@mdxeditor/editor/style.css`(编辑器自身样式),
|
|
114
|
+
* 容器外观令牌化样式见包根 `styles.css`
|
|
115
|
+
* (`import '@pactor-app/markdown/styles.css'`),可被宿主主题覆盖。
|
|
116
|
+
*
|
|
117
|
+
* 注意:编辑器体积较大且依赖浏览器 DOM API,SSR 场景请动态导入
|
|
118
|
+
* (如 next/dynamic 的 `ssr: false`)。
|
|
119
|
+
*/
|
|
120
|
+
declare const MarkdownEditor: react.ForwardRefExoticComponent<MarkdownEditorProps & react.RefAttributes<MDXEditorMethods>>;
|
|
121
|
+
|
|
122
|
+
interface MarkdownEditorDslComponentProps {
|
|
123
|
+
/**
|
|
124
|
+
* Markdown 内容(DSL props.value)。
|
|
125
|
+
* `${state.xxx}` 等表达式由 Renderer 在 props 下发前解析;
|
|
126
|
+
* 外部 value 变化经 setMarkdown 同步进编辑器(见 MarkdownEditor)。
|
|
127
|
+
*/
|
|
128
|
+
value?: string;
|
|
129
|
+
/** 占位文本 */
|
|
130
|
+
placeholder?: string;
|
|
131
|
+
/** 只读模式(隐藏工具栏;权限 denied 策略 readonly 注入同名字段) */
|
|
132
|
+
readOnly?: boolean;
|
|
133
|
+
/** 是否渲染工具栏,默认 true */
|
|
134
|
+
toolbar?: boolean;
|
|
135
|
+
/** 自动聚焦 */
|
|
136
|
+
autoFocus?: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* 内容变化回调(由 DSL events.onChange 绑定注入):
|
|
139
|
+
* 事件对象为新的 Markdown 字符串,action 中以 `${event}` 引用。
|
|
140
|
+
*/
|
|
141
|
+
onChange?: (value: string) => void;
|
|
142
|
+
/** 通用:根节点 class(与基础 class 并存) */
|
|
143
|
+
className?: string;
|
|
144
|
+
/** 通用:根节点内联样式 */
|
|
145
|
+
style?: CSSProperties;
|
|
146
|
+
/** 主题:缺省跟随 data-pactor-theme,可显式覆盖 */
|
|
147
|
+
theme?: 'light' | 'dark';
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* DSL 组件适配:`{ type: 'MarkdownEditor', props: { value }, events: { onChange } }`。
|
|
151
|
+
* 仅做 props 透传,编辑能力全部委托给 MarkdownEditor。
|
|
152
|
+
* 典型用法:value 绑定 state 表达式,onChange action 把 `${event}` 写回同一 state。
|
|
153
|
+
*/
|
|
154
|
+
declare function MarkdownEditorDslComponent({ value, placeholder, readOnly, toolbar, autoFocus, onChange, className, style, theme, }: MarkdownEditorDslComponentProps): react.JSX.Element;
|
|
155
|
+
|
|
156
|
+
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,85 @@ 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
|
+
/** 主题:缺省跟随 documentElement 的 data-pactor-theme(与库换肤机制一致),可显式覆盖 */
|
|
95
|
+
theme?: 'light' | 'dark';
|
|
96
|
+
/** 是否渲染工具栏,默认 true(readOnly 时强制隐藏) */
|
|
97
|
+
toolbar?: boolean;
|
|
98
|
+
/** 自定义工具栏内容:参数为默认工具栏节点 */
|
|
99
|
+
renderToolbar?: MarkdownEditorToolbarRender;
|
|
100
|
+
/**
|
|
101
|
+
* 追加的 MDXEditor 插件(插在默认插件集之后)。
|
|
102
|
+
* 默认插件集:headings / lists / quote / thematicBreak / link(+dialog) /
|
|
103
|
+
* table / codeBlock(+codeMirror) / markdownShortcut,以及工具栏。
|
|
104
|
+
*/
|
|
105
|
+
plugins?: MDXEditorProps['plugins'];
|
|
106
|
+
/** 透传给底层 MDXEditor 的其余 props(会被上述显式 props 覆盖) */
|
|
107
|
+
editorProps?: Omit<MDXEditorProps, 'markdown' | 'onChange' | 'onBlur' | 'readOnly' | 'autoFocus' | 'placeholder' | 'plugins'>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Markdown 富文本编辑器:基于 @mdxeditor/editor(Lexical 内核)封装,
|
|
111
|
+
* 受控 `value` / `onChange`,默认带 GFM 常用插件与工具栏。
|
|
112
|
+
*
|
|
113
|
+
* 样式:使用方需引入 `@mdxeditor/editor/style.css`(编辑器自身样式),
|
|
114
|
+
* 容器外观令牌化样式见包根 `styles.css`
|
|
115
|
+
* (`import '@pactor-app/markdown/styles.css'`),可被宿主主题覆盖。
|
|
116
|
+
*
|
|
117
|
+
* 注意:编辑器体积较大且依赖浏览器 DOM API,SSR 场景请动态导入
|
|
118
|
+
* (如 next/dynamic 的 `ssr: false`)。
|
|
119
|
+
*/
|
|
120
|
+
declare const MarkdownEditor: react.ForwardRefExoticComponent<MarkdownEditorProps & react.RefAttributes<MDXEditorMethods>>;
|
|
121
|
+
|
|
122
|
+
interface MarkdownEditorDslComponentProps {
|
|
123
|
+
/**
|
|
124
|
+
* Markdown 内容(DSL props.value)。
|
|
125
|
+
* `${state.xxx}` 等表达式由 Renderer 在 props 下发前解析;
|
|
126
|
+
* 外部 value 变化经 setMarkdown 同步进编辑器(见 MarkdownEditor)。
|
|
127
|
+
*/
|
|
128
|
+
value?: string;
|
|
129
|
+
/** 占位文本 */
|
|
130
|
+
placeholder?: string;
|
|
131
|
+
/** 只读模式(隐藏工具栏;权限 denied 策略 readonly 注入同名字段) */
|
|
132
|
+
readOnly?: boolean;
|
|
133
|
+
/** 是否渲染工具栏,默认 true */
|
|
134
|
+
toolbar?: boolean;
|
|
135
|
+
/** 自动聚焦 */
|
|
136
|
+
autoFocus?: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* 内容变化回调(由 DSL events.onChange 绑定注入):
|
|
139
|
+
* 事件对象为新的 Markdown 字符串,action 中以 `${event}` 引用。
|
|
140
|
+
*/
|
|
141
|
+
onChange?: (value: string) => void;
|
|
142
|
+
/** 通用:根节点 class(与基础 class 并存) */
|
|
143
|
+
className?: string;
|
|
144
|
+
/** 通用:根节点内联样式 */
|
|
145
|
+
style?: CSSProperties;
|
|
146
|
+
/** 主题:缺省跟随 data-pactor-theme,可显式覆盖 */
|
|
147
|
+
theme?: 'light' | 'dark';
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* DSL 组件适配:`{ type: 'MarkdownEditor', props: { value }, events: { onChange } }`。
|
|
151
|
+
* 仅做 props 透传,编辑能力全部委托给 MarkdownEditor。
|
|
152
|
+
* 典型用法:value 绑定 state 表达式,onChange action 把 `${event}` 写回同一 state。
|
|
153
|
+
*/
|
|
154
|
+
declare function MarkdownEditorDslComponent({ value, placeholder, readOnly, toolbar, autoFocus, onChange, className, style, theme, }: MarkdownEditorDslComponentProps): react.JSX.Element;
|
|
155
|
+
|
|
156
|
+
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,221 @@ 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, useId, useImperativeHandle, useRef, useState as useState2 } 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
|
+
function usePactorTheme() {
|
|
222
|
+
const read = () => typeof document !== "undefined" && document.documentElement.getAttribute("data-pactor-theme") === "dark" ? "dark" : "light";
|
|
223
|
+
const [theme, setTheme] = useState2(read);
|
|
224
|
+
useEffect2(() => {
|
|
225
|
+
if (typeof document === "undefined") return;
|
|
226
|
+
const observer = new MutationObserver(() => setTheme(read()));
|
|
227
|
+
observer.observe(document.documentElement, {
|
|
228
|
+
attributes: true,
|
|
229
|
+
attributeFilter: ["data-pactor-theme"]
|
|
230
|
+
});
|
|
231
|
+
return () => observer.disconnect();
|
|
232
|
+
}, []);
|
|
233
|
+
return theme;
|
|
234
|
+
}
|
|
235
|
+
var defaultToolbarContents = /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
236
|
+
/* @__PURE__ */ jsx5(UndoRedo, {}),
|
|
237
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
238
|
+
/* @__PURE__ */ jsx5(BoldItalicUnderlineToggles, {}),
|
|
239
|
+
/* @__PURE__ */ jsx5(CodeToggle, {}),
|
|
240
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
241
|
+
/* @__PURE__ */ jsx5(ListsToggle, {}),
|
|
242
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
243
|
+
/* @__PURE__ */ jsx5(BlockTypeSelect, {}),
|
|
244
|
+
/* @__PURE__ */ jsx5(Separator, {}),
|
|
245
|
+
/* @__PURE__ */ jsx5(CreateLink, {}),
|
|
246
|
+
/* @__PURE__ */ jsx5(InsertTable, {}),
|
|
247
|
+
/* @__PURE__ */ jsx5(InsertThematicBreak, {}),
|
|
248
|
+
/* @__PURE__ */ jsx5(InsertCodeBlock, {})
|
|
249
|
+
] });
|
|
250
|
+
var MarkdownEditor = forwardRef(
|
|
251
|
+
function MarkdownEditor2({
|
|
252
|
+
value = "",
|
|
253
|
+
onChange,
|
|
254
|
+
onBlur,
|
|
255
|
+
placeholder,
|
|
256
|
+
readOnly = false,
|
|
257
|
+
autoFocus,
|
|
258
|
+
className,
|
|
259
|
+
style,
|
|
260
|
+
theme,
|
|
261
|
+
toolbar = true,
|
|
262
|
+
renderToolbar,
|
|
263
|
+
plugins,
|
|
264
|
+
editorProps
|
|
265
|
+
}, ref) {
|
|
266
|
+
const showToolbar = toolbar && !readOnly;
|
|
267
|
+
const containerClass = className ? `pactor-markdown-editor ${className}` : "pactor-markdown-editor";
|
|
268
|
+
const pactorTheme = usePactorTheme();
|
|
269
|
+
const resolvedTheme = theme ?? pactorTheme;
|
|
270
|
+
const instanceId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
271
|
+
const instanceClass = `pactor-markdown-editor-instance-${instanceId}`;
|
|
272
|
+
const mdxEditorClassName = [
|
|
273
|
+
"pactor-markdown-editor-root",
|
|
274
|
+
instanceClass,
|
|
275
|
+
editorProps?.className
|
|
276
|
+
].filter(Boolean).join(" ");
|
|
277
|
+
useEffect2(() => {
|
|
278
|
+
if (typeof document === "undefined") return;
|
|
279
|
+
const selector = `.${instanceClass}`;
|
|
280
|
+
const applyTheme = () => {
|
|
281
|
+
document.querySelectorAll(selector).forEach((element) => {
|
|
282
|
+
element.classList.toggle("dark", resolvedTheme === "dark");
|
|
283
|
+
element.classList.toggle("light", resolvedTheme === "light");
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
applyTheme();
|
|
287
|
+
const observer = new MutationObserver((records) => {
|
|
288
|
+
for (const record of records) {
|
|
289
|
+
for (const node of record.addedNodes) {
|
|
290
|
+
if (node instanceof Element && (node.matches(selector) || node.querySelector(selector))) {
|
|
291
|
+
applyTheme();
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
298
|
+
return () => observer.disconnect();
|
|
299
|
+
}, [instanceClass, resolvedTheme]);
|
|
300
|
+
const innerRef = useRef(null);
|
|
301
|
+
useImperativeHandle(ref, () => innerRef.current, []);
|
|
302
|
+
const lastEmittedRef = useRef(value);
|
|
303
|
+
useEffect2(() => {
|
|
304
|
+
if (value !== lastEmittedRef.current) {
|
|
305
|
+
lastEmittedRef.current = value;
|
|
306
|
+
innerRef.current?.setMarkdown(value);
|
|
307
|
+
}
|
|
308
|
+
}, [value]);
|
|
309
|
+
const handleChange = (next) => {
|
|
310
|
+
lastEmittedRef.current = next;
|
|
311
|
+
onChange?.(next);
|
|
312
|
+
};
|
|
313
|
+
return /* @__PURE__ */ jsx5("div", { className: containerClass, style, "data-theme": resolvedTheme, children: /* @__PURE__ */ jsx5(
|
|
314
|
+
MDXEditor,
|
|
315
|
+
{
|
|
316
|
+
ref: innerRef,
|
|
317
|
+
markdown: value,
|
|
318
|
+
onChange: handleChange,
|
|
319
|
+
onBlur,
|
|
320
|
+
readOnly,
|
|
321
|
+
autoFocus,
|
|
322
|
+
placeholder,
|
|
323
|
+
contentEditableClassName: "pactor-markdown-editor-content",
|
|
324
|
+
plugins: [
|
|
325
|
+
headingsPlugin(),
|
|
326
|
+
listsPlugin(),
|
|
327
|
+
quotePlugin(),
|
|
328
|
+
thematicBreakPlugin(),
|
|
329
|
+
linkPlugin(),
|
|
330
|
+
linkDialogPlugin(),
|
|
331
|
+
tablePlugin(),
|
|
332
|
+
codeBlockPlugin({ defaultCodeBlockLanguage: "" }),
|
|
333
|
+
codeMirrorPlugin({
|
|
334
|
+
codeBlockLanguages: {
|
|
335
|
+
"": "Plain text",
|
|
336
|
+
js: "JavaScript",
|
|
337
|
+
ts: "TypeScript",
|
|
338
|
+
tsx: "TSX",
|
|
339
|
+
css: "CSS",
|
|
340
|
+
html: "HTML",
|
|
341
|
+
json: "JSON",
|
|
342
|
+
yaml: "YAML",
|
|
343
|
+
bash: "Bash",
|
|
344
|
+
python: "Python",
|
|
345
|
+
go: "Go",
|
|
346
|
+
rust: "Rust",
|
|
347
|
+
sql: "SQL"
|
|
348
|
+
}
|
|
349
|
+
}),
|
|
350
|
+
markdownShortcutPlugin(),
|
|
351
|
+
...showToolbar ? [
|
|
352
|
+
toolbarPlugin({
|
|
353
|
+
toolbarClassName: "pactor-markdown-editor-toolbar",
|
|
354
|
+
toolbarContents: () => renderToolbar ? renderToolbar(defaultToolbarContents) : defaultToolbarContents
|
|
355
|
+
})
|
|
356
|
+
] : [],
|
|
357
|
+
...plugins ?? []
|
|
358
|
+
],
|
|
359
|
+
...editorProps,
|
|
360
|
+
className: mdxEditorClassName
|
|
361
|
+
}
|
|
362
|
+
) });
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
// src/dsl-editor-component.tsx
|
|
367
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
368
|
+
function MarkdownEditorDslComponent({
|
|
369
|
+
value,
|
|
370
|
+
placeholder,
|
|
371
|
+
readOnly,
|
|
372
|
+
toolbar,
|
|
373
|
+
autoFocus,
|
|
374
|
+
onChange,
|
|
375
|
+
className,
|
|
376
|
+
style,
|
|
377
|
+
theme
|
|
378
|
+
}) {
|
|
379
|
+
return /* @__PURE__ */ jsx6(
|
|
380
|
+
MarkdownEditor,
|
|
381
|
+
{
|
|
382
|
+
value,
|
|
383
|
+
placeholder,
|
|
384
|
+
readOnly,
|
|
385
|
+
toolbar,
|
|
386
|
+
autoFocus,
|
|
387
|
+
onChange,
|
|
388
|
+
className,
|
|
389
|
+
style,
|
|
390
|
+
theme
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
194
395
|
// src/register.ts
|
|
195
396
|
var MARKDOWN_DSL_TYPE = "Markdown";
|
|
397
|
+
var MARKDOWN_EDITOR_DSL_TYPE = "MarkdownEditor";
|
|
196
398
|
function registerMarkdownComponents(registry) {
|
|
197
399
|
registry.register(MARKDOWN_DSL_TYPE, MarkdownDslComponent);
|
|
400
|
+
registry.register(MARKDOWN_EDITOR_DSL_TYPE, MarkdownEditorDslComponent);
|
|
198
401
|
}
|
|
199
402
|
export {
|
|
200
403
|
CodeBlock,
|
|
201
404
|
MARKDOWN_DSL_TYPE,
|
|
405
|
+
MARKDOWN_EDITOR_DSL_TYPE,
|
|
202
406
|
MarkdownDslComponent,
|
|
407
|
+
MarkdownEditor,
|
|
408
|
+
MarkdownEditorDslComponent,
|
|
203
409
|
MarkdownRenderer,
|
|
204
410
|
isSafeUrl,
|
|
205
411
|
registerCodeLanguages,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pactor-app/markdown",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.60.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.60.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.60.0"
|
|
72
73
|
},
|
|
73
74
|
"scripts": {
|
|
74
75
|
"build": "tsup",
|
package/styles.css
CHANGED
|
@@ -259,3 +259,73 @@
|
|
|
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
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 明暗主题:`pactor-markdown-editor-root` 由组件同时挂到 MDXEditor 的编辑
|
|
296
|
+
* 根节点与 portal 的 `.mdxeditor-popup-container`(下拉 / 链接弹层)。
|
|
297
|
+
* 组件按 data-pactor-theme(或显式 theme)追加 `.light` / `.dark`,
|
|
298
|
+
* 激活 MDXEditor 内置的 Radix 令牌组;这里再把其基础令牌接到宿主语义令牌,
|
|
299
|
+
* 使画布 / 工具栏 / 弹层与宿主主题同色(无宿主令牌时回退 Radix 色阶)。
|
|
300
|
+
* 选择器带元素名提升优先级,确保覆盖 MDXEditor 自身的 ._editorRoot_* 声明。
|
|
301
|
+
*/
|
|
302
|
+
div.pactor-markdown-editor-root.mdxeditor,
|
|
303
|
+
div.pactor-markdown-editor-root.mdxeditor-popup-container {
|
|
304
|
+
--basePageBg: var(--card, var(--slate-1));
|
|
305
|
+
--baseBase: var(--card, var(--slate-1));
|
|
306
|
+
--baseBgSubtle: color-mix(in srgb, var(--foreground, var(--slate-12)) 5%, var(--card, var(--slate-1)));
|
|
307
|
+
--baseBg: color-mix(in srgb, var(--foreground, var(--slate-12)) 8%, var(--card, var(--slate-1)));
|
|
308
|
+
--baseBgHover: color-mix(in srgb, var(--foreground, var(--slate-12)) 11%, var(--card, var(--slate-1)));
|
|
309
|
+
--baseBgActive: color-mix(in srgb, var(--foreground, var(--slate-12)) 15%, var(--card, var(--slate-1)));
|
|
310
|
+
--baseLine: var(--border, var(--slate-6));
|
|
311
|
+
--baseBorder: var(--border, var(--slate-7));
|
|
312
|
+
--baseBorderHover: color-mix(in srgb, var(--foreground, var(--slate-12)) 24%, var(--border, var(--slate-7)));
|
|
313
|
+
--baseSolid: var(--muted-foreground, var(--slate-9));
|
|
314
|
+
--baseSolidHover: var(--foreground, var(--slate-10));
|
|
315
|
+
--baseText: var(--muted-foreground, var(--slate-11));
|
|
316
|
+
--baseTextContrast: var(--foreground, var(--slate-12));
|
|
317
|
+
|
|
318
|
+
/* 弹层脱离编辑根节点,需自带强调色令牌(与 MDXEditor 默认同一色阶) */
|
|
319
|
+
--accentBase: var(--blue-1);
|
|
320
|
+
--accentBgSubtle: var(--blue-2);
|
|
321
|
+
--accentBg: var(--blue-3);
|
|
322
|
+
--accentBgHover: var(--blue-4);
|
|
323
|
+
--accentBgActive: var(--blue-5);
|
|
324
|
+
--accentLine: var(--blue-6);
|
|
325
|
+
--accentBorder: var(--blue-7);
|
|
326
|
+
--accentBorderHover: var(--blue-8);
|
|
327
|
+
--accentSolid: var(--blue-9);
|
|
328
|
+
--accentSolidHover: var(--blue-10);
|
|
329
|
+
--accentText: var(--blue-11);
|
|
330
|
+
--accentTextContrast: var(--blue-12);
|
|
331
|
+
}
|