remons-markdown-editor 1.0.0-beta.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/MarkdownEditor.tsx","../src/components/Editor/index.tsx","../src/components/Editor/codemirrorExtensions.ts","../src/components/Editor/containerWidget.ts","../src/utils/parseContainer.ts","../src/registry/componentRegistry.tsx","../src/components/Preview/index.tsx","../src/components/Panel/SplitPanels.tsx","../src/components/Toolbar/index.tsx","../src/components/Toolbar/hooks/useToolbar.ts","../src/components/Toolbar/config.ts","../src/components/Toolbar/buttons.tsx","../src/components/Toolbar/renderers/ToolbarItemRenderer.tsx","../src/components/Toolbar/renderers/TablePicker/TableDropdown.tsx","../src/components/Toolbar/renderers/TablePicker/index.tsx","../src/components/Toolbar/renderers/TablePicker/index.module.less","../src/components/Dialog/EditDialog.tsx","../src/utils/writeContainer.ts","../src/hooks/useRegisterToolbar.ts"],"sourcesContent":["// 核心组件\nexport { default as MarkdownEditor } from './MarkdownEditor'\nexport type { MarkdownEditorProps, MarkdownEditorRef } from './MarkdownEditor'\n\n// 注册容器到工具栏\nexport { useRegisterToolbar } from './hooks/useRegisterToolbar'\n\n// 类型\nexport type {\n ComponentSchema,\n DialogField,\n ToolbarButtonConfig,\n} from './types'\n","import { useState, useRef, useCallback, useEffect, forwardRef, useImperativeHandle } from 'react'\nimport Editor from './components/Editor'\nimport Preview from './components/Preview'\nimport SplitPanels from './components/Panel/SplitPanels'\nimport Toolbar from './components/Toolbar'\nimport EditDialog from './components/Dialog/EditDialog'\nimport componentRegistry from './registry/componentRegistry'\nimport { updateContainerProps, insertContainer } from './utils/writeContainer'\nimport type { ToolbarButtonConfig } from './types'\nimport { ReadOutlined } from '@ant-design/icons'\nimport './App.css'\n\nexport interface MarkdownEditorRef {\n getContent: () => string\n}\n\nexport interface MarkdownEditorProps {\n /** 初始内容 */\n defaultValue?: string\n /** 受控内容 */\n value?: string\n /** 内容变化回调 */\n onChange?: (value: string) => void\n /** 是否显示预览面板 */\n showPreview?: boolean\n /** 是否支持全屏 */\n fullscreen?: boolean\n}\n\nexport default forwardRef<MarkdownEditorRef, MarkdownEditorProps>(function MarkdownEditor({\n defaultValue = '',\n value,\n onChange,\n showPreview = true,\n fullscreen: fullscreenProp = false,\n}, ref) {\n const [content, setContent] = useState(value ?? defaultValue)\n const [editorView, setEditorView] = useState<any>(null)\n const scrollDOMRef = useRef<HTMLElement | null>(null)\n const previewElRef = useRef<HTMLDivElement | null>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n // 容器编辑弹窗状态\n const [dialogVisible, setDialogVisible] = useState(false)\n const [dialogTypeName, setDialogTypeName] = useState('')\n const [dialogProps, setDialogProps] = useState<Record<string, string>>({})\n const [dialogPropsRange, setDialogPropsRange] = useState<[number, number]>([0, 0])\n\n // 受控模式:外部 value 变化时同步\n useEffect(() => {\n if (value !== undefined) {\n setContent(value)\n }\n }, [value])\n\n const internalSetContent = useCallback((newContent: string) => {\n setContent(newContent)\n onChange?.(newContent)\n }, [onChange])\n\n // 暴露 getContent 方法\n useImperativeHandle(ref, () => ({\n getContent: () => content,\n }), [content])\n\n // 全屏切换\n const toggleFullscreen = useCallback(() => {\n if (document.fullscreenElement) {\n document.exitFullscreen().catch((e) => {\n console.error('退出全屏失败:', e)\n })\n } else {\n const el = document.documentElement\n const requestMethod = el.requestFullscreen ||\n (el as any).webkitRequestFullscreen ||\n (el as any).mozRequestFullScreen ||\n (el as any).msRequestFullscreen\n if (requestMethod) {\n requestMethod.call(el).catch((e) => {\n console.error('进入全屏失败:', e)\n })\n }\n }\n }, [])\n\n // Listen for fullscreen change events\n useEffect(() => {\n const handleFullscreenChange = () => {\n setFullscreen(!!document.fullscreenElement)\n }\n document.addEventListener('fullscreenchange', handleFullscreenChange)\n document.addEventListener('webkitfullscreenchange', handleFullscreenChange)\n return () => {\n document.removeEventListener('fullscreenchange', handleFullscreenChange)\n document.removeEventListener('webkitfullscreenchange', handleFullscreenChange)\n }\n }, [])\n\n // 从注册中心自动生成工具栏按钮\n const extraToolbarItems: ToolbarButtonConfig[] = componentRegistry.getAll().map(schema => {\n const defaultProps: Record<string, string> = {}\n for (const field of schema.fields) {\n defaultProps[field.key] = field.defaultValue ?? ''\n }\n const iconNode = schema.icon ?? <ReadOutlined />\n return {\n id: `container-${schema.name}`,\n label: schema.label,\n icon: () => iconNode,\n handler: (view, _state) => insertContainer(view, schema.name, defaultProps),\n }\n })\n\n // 滚动同步\n const handleScrollReady = useCallback((scrollDOM: HTMLElement) => {\n scrollDOMRef.current = scrollDOM\n }, [])\n\n // 设置滚动同步\n useEffect(() => {\n const source = scrollDOMRef.current\n const target = previewElRef.current\n if (!source || !target) return\n\n let rafId: number | null = null\n\n const listener = () => {\n if (rafId) return\n rafId = requestAnimationFrame(() => {\n const s = scrollDOMRef.current\n const t = previewElRef.current\n if (!s || !t) { rafId = null; return }\n const sourceScrollHeight = s.scrollHeight - s.clientHeight\n if (sourceScrollHeight <= 0) { rafId = null; return }\n const ratio = s.scrollTop / sourceScrollHeight\n const targetScrollHeight = t.scrollHeight - t.clientHeight\n if (targetScrollHeight > 0) {\n t.scrollTop = ratio * targetScrollHeight\n }\n rafId = null\n })\n }\n\n source.addEventListener('scroll', listener, { passive: true })\n\n return () => {\n source.removeEventListener('scroll', listener)\n if (rafId) cancelAnimationFrame(rafId)\n }\n }, [previewElRef.current])\n\n const handlePreviewRef = useCallback((node: HTMLDivElement | null) => {\n previewElRef.current = node\n }, [])\n\n const handleViewReady = useCallback((view: any) => {\n setEditorView(view)\n }, [])\n\n // 编辑容器属性时触发\n const handleEditContainer = useCallback((_view: any, typeName: string, propsLineFrom: number, propsLineTo: number, currentProps: Record<string, string>) => {\n setDialogTypeName(typeName)\n setDialogProps({ ...currentProps })\n setDialogPropsRange([propsLineFrom, propsLineTo])\n setDialogVisible(true)\n }, [])\n\n // 保存容器属性\n const handleDialogSave = useCallback((newProps: Record<string, string>) => {\n setDialogVisible(false)\n if (editorView) {\n updateContainerProps(editorView, dialogPropsRange[0], dialogPropsRange[1], newProps)\n }\n }, [editorView, dialogPropsRange])\n\n // 关闭弹窗\n const handleDialogClose = useCallback(() => {\n setDialogVisible(false)\n }, [])\n\n return (\n <div className={fullscreen ? 'app fullscreen' : 'app'}>\n <Toolbar\n editorView={editorView}\n fullscreen={fullscreen}\n onToggleFullscreen={fullscreenProp ? toggleFullscreen : undefined}\n extraItems={extraToolbarItems}\n />\n {showPreview ? (\n <SplitPanels\n leftPanel={\n <Editor\n value={content}\n onChange={internalSetContent}\n onScrollReady={handleScrollReady}\n onViewReady={handleViewReady}\n onEditContainer={handleEditContainer}\n />\n }\n rightPanel={\n <Preview\n content={content}\n ref={handlePreviewRef}\n />\n }\n />\n ) : (\n <Editor\n value={content}\n onChange={internalSetContent}\n onScrollReady={handleScrollReady}\n onViewReady={handleViewReady}\n onEditContainer={handleEditContainer}\n />\n )}\n {/* 容器属性编辑弹窗 */}\n <EditDialog\n visible={dialogVisible}\n typeName={dialogTypeName}\n currentProps={dialogProps}\n onSave={handleDialogSave}\n onClose={handleDialogClose}\n />\n </div>\n )\n})\n","import { useEffect, useRef } from 'react'\nimport { EditorView, basicSetup } from 'codemirror'\nimport { EditorState } from '@codemirror/state'\nimport { getCodeMirrorExtensions } from './codemirrorExtensions'\n\ninterface EditorProps {\n value: string\n onChange: (value: string) => void\n onScrollReady?: (scrollDOM: HTMLElement) => void\n onViewReady?: (view: EditorView) => void\n /** 编辑容器属性的回调 */\n onEditContainer?: (view: any, typeName: string, propsLineFrom: number, propsLineTo: number, currentProps: Record<string, string>) => void\n}\n\nexport default function Editor({ value, onChange, onScrollReady, onViewReady, onEditContainer }: EditorProps) {\n const editorRef = useRef<HTMLDivElement>(null)\n const viewRef = useRef<EditorView | null>(null)\n\n useEffect(() => {\n if (!editorRef.current) return\n\n const extensions = [\n basicSetup,\n ...getCodeMirrorExtensions({ onEditContainer }),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n onChange(update.state.doc.toString())\n }\n }),\n ]\n\n const state = EditorState.create({\n doc: value,\n extensions,\n })\n\n viewRef.current = new EditorView({\n state,\n parent: editorRef.current,\n })\n\n if (onScrollReady && viewRef.current) {\n onScrollReady(viewRef.current.scrollDOM)\n }\n\n if (onViewReady) {\n onViewReady(viewRef.current)\n }\n\n return () => {\n viewRef.current?.destroy()\n }\n }, [])\n\n // 外部 value 变化时更新编辑器内容\n useEffect(() => {\n if (viewRef.current && viewRef.current.state.doc.toString() !== value) {\n viewRef.current.dispatch({\n changes: { from: 0, to: viewRef.current.state.doc.length, insert: value },\n })\n }\n }, [value])\n\n return <div ref={editorRef} className=\"editor\" />\n}","import { EditorView, keymap } from '@codemirror/view'\r\nimport { defaultKeymap, indentWithTab } from '@codemirror/commands'\r\nimport { markdown } from '@codemirror/lang-markdown'\r\nimport { oneDark } from '@codemirror/theme-one-dark'\r\nimport { containerWidgetPlugin, containerEditCallback } from './containerWidget'\r\nimport type { OnEditContainer } from './containerWidget'\r\n\r\ninterface GetCodeMirrorExtensionsOptions {\r\n /** 编辑容器属性的回调函数 */\r\n onEditContainer?: OnEditContainer\r\n}\r\n\r\nexport const getCodeMirrorExtensions = (opts?: GetCodeMirrorExtensionsOptions) => {\r\n const extensions = [\r\n markdown(),\r\n oneDark,\r\n keymap.of([...defaultKeymap, indentWithTab]),\r\n EditorView.lineWrapping,\r\n EditorView.baseTheme({\r\n '.cm-scroller': {\r\n height: '100%',\r\n overflow: 'auto',\r\n },\r\n // 容器头行 Widget 样式\r\n '.container-widget-header': {\r\n display: 'inline',\r\n fontSize: 'inherit',\r\n fontFamily: 'inherit',\r\n },\r\n '.container-widget-text': {\r\n fontWeight: 'bold',\r\n color: '#79c0ff',\r\n },\r\n // 容器属性行 Widget 样式\r\n '.container-widget-props': {\r\n display: 'inline',\r\n fontSize: 'inherit',\r\n fontFamily: 'inherit',\r\n color: '#a5d6ff',\r\n },\r\n // 容器编辑按钮样式\r\n '.container-edit-btn': {\r\n display: 'inline-block',\r\n padding: '0 4px',\r\n fontSize: '12px',\r\n color: '#58a6ff',\r\n background: 'transparent',\r\n border: 'none',\r\n borderRadius: '4px',\r\n cursor: 'pointer',\r\n marginRight: '4px',\r\n verticalAlign: 'middle',\r\n },\r\n '.container-edit-btn:hover': {\r\n background: 'rgba(88, 166, 255, 0.16)',\r\n },\r\n }),\r\n ]\r\n\r\n // 添加容器 widget 插件\r\n extensions.push(containerWidgetPlugin)\r\n\r\n // 如果有编辑回调,注册 facet\r\n if (opts?.onEditContainer) {\r\n extensions.push(containerEditCallback.of(opts.onEditContainer))\r\n }\r\n\r\n return extensions\r\n}\r\n\r\nexport default getCodeMirrorExtensions","import { ViewPlugin, Decoration, WidgetType } from '@codemirror/view'\r\nimport type { DecorationSet } from '@codemirror/view'\r\nimport { EditorState, Facet } from '@codemirror/state'\r\nimport { findContainers } from '../../utils/parseContainer'\r\nimport componentRegistry from '../../registry/componentRegistry'\r\n\r\n/**\r\n * 编辑容器属性的回调函数类型\r\n */\r\nexport interface OnEditContainer {\r\n (view: any, typeName: string, propsLineFrom: number, propsLineTo: number, currentProps: Record<string, string>): void\r\n}\r\n\r\n/**\r\n * 自定义 facet 用于传递编辑回调\r\n */\r\nexport const containerEditCallback = Facet.define<any, OnEditContainer>({\r\n combine: (values) => values[0],\r\n})\r\n\r\n/**\r\n * 只读的容器头行 Widget(显示 :::typeName + 编辑按钮)\r\n * 使用 Decoration.replace 替换原始文本,使其不可直接编辑\r\n */\r\nclass ContainerHeaderWidget extends WidgetType {\r\n public typeName: string\r\n\r\n constructor(typeName: string) {\r\n super()\r\n this.typeName = typeName\r\n }\r\n\r\n public toDOM(): HTMLElement {\r\n const container = document.createElement('span')\r\n container.className = 'container-widget-header'\r\n\r\n const text = document.createElement('span')\r\n text.className = 'container-widget-text'\r\n text.textContent = `:::${this.typeName}`\r\n\r\n const button = document.createElement('button')\r\n button.className = 'container-edit-btn'\r\n button.title = `编辑 ${componentRegistry.get(this.typeName)?.label ?? this.typeName}`\r\n button.textContent = '✎'\r\n ;(button as any).__typeName = this.typeName\r\n\r\n container.appendChild(text)\r\n container.appendChild(button)\r\n return container\r\n }\r\n\r\n public eq(other: ContainerHeaderWidget): boolean {\r\n return this.typeName === other.typeName\r\n }\r\n\r\n public ignoreEvent(): boolean {\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * 只读的容器属性行 Widget(显示 { key: value, ... })\r\n * 使用 Decoration.replace 替换原始文本,使其不可直接编辑\r\n */\r\nclass ContainerPropsWidget extends WidgetType {\r\n public propsText: string\r\n\r\n constructor(propsText: string) {\r\n super()\r\n this.propsText = propsText\r\n }\r\n\r\n public toDOM(): HTMLElement {\r\n const span = document.createElement('span')\r\n span.className = 'container-widget-props'\r\n span.textContent = this.propsText\r\n return span\r\n }\r\n\r\n public eq(other: ContainerPropsWidget): boolean {\r\n return this.propsText === other.propsText\r\n }\r\n\r\n public ignoreEvent(): boolean {\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * 只读的容器结束行 Widget(显示 :::)\r\n * 使用 Decoration.replace 替换原始文本,使其不可直接编辑\r\n */\r\nclass ContainerCloseWidget extends WidgetType {\r\n public toDOM(): HTMLElement {\r\n const span = document.createElement('span')\r\n span.className = 'container-widget-text'\r\n span.textContent = ':::'\r\n return span\r\n }\r\n\r\n public eq(): boolean {\r\n return true\r\n }\r\n\r\n public ignoreEvent(): boolean {\r\n return false\r\n }\r\n}\r\n\r\n/**\r\n * 查找文档中的容器,创建 Decoration 装饰物\r\n * 将 ::: 头行、{} 属性行和 ::: 结束行替换为只读 widget,使其无法在编辑器中直接编辑\r\n */\r\nfunction findContainerDecorations(state: EditorState): Array<{ from: number; to: number; value: Decoration }> {\r\n const docText = state.doc.toString()\r\n const containers = findContainers(docText)\r\n const decorations: Array<{ from: number; to: number; value: Decoration }> = []\r\n\r\n for (const container of containers) {\r\n const headerLine = state.doc.lineAt(container.startPos)\r\n\r\n // 替换 :::typeName 头行为只读 widget\r\n decorations.push({\r\n from: headerLine.from,\r\n to: headerLine.to,\r\n value: Decoration.replace({\r\n widget: new ContainerHeaderWidget(container.typeName),\r\n }),\r\n })\r\n\r\n // 替换 {} 属性行为只读 widget\r\n if (container.propsLineFrom > 0 && container.propsLineTo > 0) {\r\n const propsLine = state.doc.lineAt(container.propsLineFrom)\r\n const propsText = propsLine.text\r\n\r\n decorations.push({\r\n from: propsLine.from,\r\n to: propsLine.to,\r\n value: Decoration.replace({\r\n widget: new ContainerPropsWidget(propsText),\r\n }),\r\n })\r\n }\r\n\r\n // 替换 ::: 结束行为只读 widget\r\n if (container.endPos > 0) {\r\n const endLine = state.doc.lineAt(container.endPos)\r\n decorations.push({\r\n from: endLine.from,\r\n to: endLine.to,\r\n value: Decoration.replace({\r\n widget: new ContainerCloseWidget(),\r\n }),\r\n })\r\n }\r\n }\r\n\r\n return decorations\r\n}\r\n\r\n/**\r\n * 容器编辑 ViewPlugin\r\n * 将容器的 ::: 头行和 {} 属性行替换为只读 widget,使其无法在编辑器中直接编辑\r\n */\r\nexport const containerWidgetPlugin = ViewPlugin.fromClass(\r\n class {\r\n public decorations: DecorationSet\r\n\r\n constructor(view: any) {\r\n this.decorations = Decoration.set(findContainerDecorations(view.state), true)\r\n }\r\n\r\n public update(update: any) {\r\n if (update.docChanged || update.selectionSet) {\r\n this.decorations = Decoration.set(findContainerDecorations(update.state), true)\r\n }\r\n }\r\n },\r\n {\r\n decorations: (value) => value.decorations,\r\n eventHandlers: {\r\n // 点击编辑按钮时触发回调\r\n click: (event: MouseEvent, view: any) => {\r\n const target = event.target as HTMLElement\r\n if (target.classList.contains('container-edit-btn')) {\r\n const typeName = (target as any).__typeName\r\n if (typeName) {\r\n const docText = view.state.doc.toString()\r\n const containers = findContainers(docText)\r\n const buttonPos = view.posAtDOM(target)\r\n\r\n // 通过 button 位置匹配最近的容器头行,避免多个同类型容器匹配错误\r\n for (const container of containers) {\r\n const headerLine = view.state.doc.lineAt(container.startPos)\r\n // 按钮位置在头行的范围内(允许一定偏差,因为 widget 替换后位置可能略有偏移)\r\n if (buttonPos >= headerLine.from - 5 && buttonPos <= headerLine.to + 5) {\r\n const callback = view.state.facet(containerEditCallback)\r\n if (callback) {\r\n callback(view, container.typeName, container.propsLineFrom, container.propsLineTo, { ...container.props })\r\n }\r\n return true\r\n }\r\n }\r\n }\r\n return false\r\n }\r\n return false\r\n },\r\n },\r\n }\r\n)","/**\n * 解析容器属性 JSON 字符串。\n * 支持标准 JSON 和宽松格式: { key: 'value', key2: 'value2' }\n */\nexport function parseContainerProps(jsonStr: string): Record<string, string> {\n try {\n const props: Record<string, string> = {}\n const trimmed = jsonStr.trim()\n if (trimmed.startsWith('{') && trimmed.endsWith('}')) {\n const inner = trimmed.slice(1, -1).trim()\n if (!inner) return props\n // 匹配 key: 'value' 或 key: \"value\" 格式\n const matches = inner.matchAll(/(?:[\"']?)(\\w+)(?:[\"']?)\\s*:\\s*(?:\"([^\"]*)\"|'([^']*)'|(\\d+))/g)\n for (const m of matches) {\n props[m[1]] = m[2] ?? m[3] ?? m[4] ?? ''\n }\n }\n return props\n } catch {\n return {}\n }\n}\n\n/**\n * 在编辑器文档中查找容器块。\n * 返回包含类型、属性和行位置的容器位置数组。\n */\nexport interface ContainerBlock {\n startPos: number // ::: 打开行位置\n typeName: string // 组件类型名称 (如 \"linkCard\")\n propsLineFrom: number // JSON 属性行起始位置\n propsLineTo: number // JSON 属性行结束位置\n props: Record<string, string>\n endPos: number // ::: 关闭行位置\n}\n\nexport function findContainers(\n docText: string\n): ContainerBlock[] {\n const containers: ContainerBlock[] = []\n const lines = docText.split('\\n')\n\n let i = 0\n while (i < lines.length) {\n const line = lines[i]\n const match = line.match(/^:::(\\w+)$/)\n if (match) {\n const typeName = match[1]\n // 计算字符位置\n let charPos = 0\n for (let l = 0; l < i; l++) {\n charPos += lines[l].length + 1 // +1 for newline\n }\n\n // 查找下一行的 JSON 属性\n let props: Record<string, string> = {}\n let propsLineFrom = 0\n let propsLineTo = 0\n let propsLineIndex = i + 1\n\n if (propsLineIndex < lines.length) {\n const propsLine = lines[propsLineIndex].trim()\n const propsMatch = propsLine.match(/^\\{[\\s\\S]*\\}$/)\n if (propsMatch) {\n props = parseContainerProps(propsLine)\n propsLineFrom = charPos + line.length + 1\n propsLineTo = propsLineFrom + lines[propsLineIndex].length\n }\n }\n\n // 查找闭合 :::\n let endPos = 0\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j].trim() === ':::') {\n let endCharPos = 0\n for (let l = 0; l < j; l++) {\n endCharPos += lines[l].length + 1\n }\n endPos = endCharPos\n i = j + 1\n break\n }\n }\n\n containers.push({\n startPos: charPos,\n typeName,\n propsLineFrom,\n propsLineTo,\n props,\n endPos,\n })\n }\n i++\n }\n\n return containers\n}","import { Input, Select } from 'antd'\nimport type { ComponentSchema } from '../types'\nimport React from 'react'\n\n/**\n * 组件注册中心,用于管理自定义组件的 schema。\n * 每个容器组件(如 :::linkCard)在此注册其可编辑字段。\n */\nclass ComponentRegistry {\n private schemas = new Map<string, ComponentSchema>()\n\n /**\n * 注册组件 schema\n */\n register(name: string, schema: ComponentSchema): void {\n this.schemas.set(name, schema)\n }\n\n /**\n * 根据组件名获取已注册的 schema\n */\n get(name: string): ComponentSchema | undefined {\n return this.schemas.get(name)\n }\n\n /**\n * 检查组件类型是否已注册\n */\n has(name: string): boolean {\n return this.schemas.has(name)\n }\n\n /**\n * 获取所有已注册的组件名\n */\n getAllNames(): string[] {\n return Array.from(this.schemas.keys())\n }\n\n /**\n * 获取所有已注册的 schema\n */\n getAll(): ComponentSchema[] {\n return Array.from(this.schemas.values())\n }\n\n /**\n * 移除组件注册\n */\n unregister(name: string): void {\n this.schemas.delete(name)\n }\n}\n\n// 单例实例\nconst componentRegistry = new ComponentRegistry()\n\n// 注册默认组件\ncomponentRegistry.register('linkCard', {\n name: 'linkCard',\n label: '链接卡片',\n fields: [\n { key: 'title', defaultValue: '链接标题' },\n { key: 'link', defaultValue: 'https://example.com' },\n { key: 'description', defaultValue: '链接描述' },\n { key: 'theme', defaultValue: 'default' },\n ],\n renderDialog: (props, onChange): React.ReactNode => (\n <>\n <div style={{ marginBottom: 12 }}>\n <div style={{ marginBottom: 4 }}>标题</div>\n <Input value={props.title} onChange={e => onChange('title', e.target.value)} />\n </div>\n <div style={{ marginBottom: 12 }}>\n <div style={{ marginBottom: 4 }}>链接</div>\n <Input value={props.link} onChange={e => onChange('link', e.target.value)} />\n </div>\n <div style={{ marginBottom: 12 }}>\n <div style={{ marginBottom: 4 }}>描述</div>\n <Input.TextArea rows={3} value={props.description} onChange={e => onChange('description', e.target.value)} />\n </div>\n <div>\n <div style={{ marginBottom: 4 }}>主题</div>\n <Select\n value={props.theme}\n onChange={v => onChange('theme', v)}\n options={[\n { label: '默认', value: 'default' },\n { label: '主要', value: 'primary' },\n { label: '警告', value: 'warning' },\n ]}\n />\n </div>\n </>\n ),\n})\n\ncomponentRegistry.register('note', {\n name: 'note',\n label: '注释',\n fields: [\n { key: 'type', defaultValue: 'info' },\n { key: 'title', defaultValue: '注意' },\n { key: 'content', defaultValue: '备注内容' },\n ],\n renderDialog: (props, onChange): React.ReactNode => (\n <>\n <div style={{ marginBottom: 12 }}>\n <div style={{ marginBottom: 4 }}>类型</div>\n <Select\n value={props.type}\n onChange={v => onChange('type', v)}\n options={[\n { label: '信息', value: 'info' },\n { label: '提示', value: 'tip' },\n { label: '警告', value: 'warning' },\n { label: '危险', value: 'danger' },\n ]}\n />\n </div>\n <div style={{ marginBottom: 12 }}>\n <div style={{ marginBottom: 4 }}>标题</div>\n <Input value={props.title} onChange={e => onChange('title', e.target.value)} />\n </div>\n <div>\n <div style={{ marginBottom: 4 }}>内容</div>\n <Input.TextArea rows={3} value={props.content} onChange={e => onChange('content', e.target.value)} />\n </div>\n </>\n ),\n})\n\nexport default componentRegistry","import { forwardRef } from 'react'\nimport { registerAll, excludedSelectors } from 'remons-markdown-plugins';\nimport RenderMarkdown, { languagesCommon, initHighlighter } from 'remons-render-markdown';\nimport 'remons-markdown-plugins/style.css';\nimport 'remons-render-markdown/dist/index.css';\n\ninitHighlighter(languagesCommon)\n\ninterface PreviewProps {\n content: string\n}\n\nconst Preview = forwardRef<HTMLDivElement, PreviewProps>(({ content }, ref) => {\n return (\n <div ref={ref} className=\"preview\">\n <RenderMarkdown content={content} isSlotMermaid excludedSelectors={excludedSelectors} customRenderers={[(md) => md.use(registerAll)]} />\n </div>\n )\n})\n\nPreview.displayName = 'Preview'\n\nexport default Preview","import React from 'react'\nimport { Splitter } from 'antd'\n\ninterface SplitPanelsProps {\n leftPanel: React.ReactNode\n rightPanel: React.ReactNode\n}\n\nexport default function SplitPanels({ leftPanel, rightPanel }: SplitPanelsProps) {\n return (\n <Splitter className=\"split-panels\">\n <Splitter.Panel defaultSize=\"50%\" min=\"20%\" max=\"80%\">\n {leftPanel}\n </Splitter.Panel>\n <Splitter.Panel>{rightPanel}</Splitter.Panel>\n </Splitter>\n )\n}","import { useCallback } from 'react'\nimport { EditorView } from '@codemirror/view'\nimport { Button } from 'antd'\nimport { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons'\nimport { useToolbar } from './hooks/useToolbar'\nimport { ToolbarItemRenderer } from './renderers/ToolbarItemRenderer'\nimport TableDropdown from './renderers/TablePicker/TableDropdown'\nimport type { ToolbarButtonConfig } from '../../types'\n\ninterface ToolbarProps {\n editorView: EditorView | null\n fullscreen?: boolean\n onToggleFullscreen?: () => void\n /** 第三方扩展的按钮配置,将追加到工具栏末尾 */\n extraItems?: ToolbarButtonConfig[]\n}\n\nexport default function Toolbar({ editorView, fullscreen, onToggleFullscreen, extraItems }: ToolbarProps) {\n const { config, handleClick } = useToolbar(editorView, extraItems)\n\n const renderFullscreen = useCallback(() => {\n if (!onToggleFullscreen) return null\n const Icon = fullscreen ? FullscreenExitOutlined : FullscreenOutlined\n return (\n <>\n <div className=\"toolbar-separator\" />\n <Button\n className=\"toolbar-button\"\n type=\"text\"\n size=\"small\"\n title={fullscreen ? '退出全屏' : '全屏'}\n onClick={() => {\n onToggleFullscreen()\n }}\n >\n <Icon />\n </Button>\n </>\n )\n }, [fullscreen, onToggleFullscreen])\n\n function isSeparator(item: ToolbarButtonConfig): boolean {\n return !!(!item.id && item.separatorBefore)\n }\n\n return (\n <div className=\"toolbar\">\n {config.map((item, idx) => {\n if (isSeparator(item)) {\n return <div key={`sep-${idx}`} className=\"toolbar-separator\" />\n }\n return (\n <ToolbarItemRenderer\n key={item.id}\n item={item}\n onClick={handleClick}\n />\n )\n })}\n <div className=\"toolbar-separator\" />\n <TableDropdown editorView={editorView} />\n {renderFullscreen()}\n </div>\n )\n}","import { useCallback, useMemo } from 'react'\nimport type { EditorView } from '@codemirror/view'\nimport type { ToolbarButtonConfig } from '../../../types'\nimport { createToolbarConfig } from '../config'\n\nexport interface UseToolbarReturn {\n config: ToolbarButtonConfig[]\n handleClick: (button: ToolbarButtonConfig) => void\n}\n\nexport function useToolbar(\n editorView: EditorView | null,\n extraItems?: ToolbarButtonConfig[]\n): UseToolbarReturn {\n const config = useMemo(() => {\n const base = createToolbarConfig()\n return extraItems ? [...base, { separatorBefore: true }, ...extraItems] : base\n }, [extraItems])\n\n const handleClick = useCallback((button: ToolbarButtonConfig) => {\n if (button.handler && editorView) {\n editorView.focus()\n button.handler(editorView, editorView.state)\n }\n }, [editorView])\n\n return { config, handleClick }\n}","import type { ToolbarButtonConfig } from '../../types'\nimport {\n BoldOutlined,\n ItalicOutlined,\n StrikethroughOutlined,\n UnderlineOutlined,\n UnorderedListOutlined,\n OrderedListOutlined,\n LinkOutlined,\n PictureOutlined,\n CodeOutlined,\n BlockOutlined,\n MinusOutlined,\n FieldStringOutlined,\n FontSizeOutlined,\n TagsOutlined,\n} from '@ant-design/icons'\nimport { insertAtCursor, insertAtLineStart, wrapSelection, toggleWrap, uploadImage } from './buttons'\n\nexport function createToolbarConfig(): ToolbarButtonConfig[] {\n return [\n {\n id: 'heading',\n icon: FontSizeOutlined,\n label: '标题',\n children: [\n { id: 'heading1', label: '标题 1', icon: FontSizeOutlined, handler: (v) => insertAtLineStart(v, '# ') },\n { id: 'heading2', label: '标题 2', icon: FontSizeOutlined, handler: (v) => insertAtLineStart(v, '## ') },\n { id: 'heading3', label: '标题 3', icon: FontSizeOutlined, handler: (v) => insertAtLineStart(v, '### ') },\n { id: 'heading4', label: '标题 4', icon: FontSizeOutlined, handler: (v) => insertAtLineStart(v, '#### ') },\n { id: 'heading5', label: '标题 5', icon: FontSizeOutlined, handler: (v) => insertAtLineStart(v, '##### ') },\n { id: 'heading6', label: '标题 6', icon: FontSizeOutlined, handler: (v) => insertAtLineStart(v, '###### ') },\n ],\n },\n { separatorBefore: true },\n { id: 'bold', label: '加粗', icon: BoldOutlined, handler: (v) => wrapSelection(v, '**', '**') },\n { id: 'italic', label: '斜体', icon: ItalicOutlined, handler: (v) => wrapSelection(v, '*', '*') },\n { id: 'strikethrough', label: '删除线', icon: StrikethroughOutlined, handler: (v) => wrapSelection(v, '~~', '~~') },\n { id: 'underline', label: '下划线', icon: UnderlineOutlined, handler: (v) => wrapSelection(v, '<u>', '</u>') },\n { separatorBefore: true },\n { id: 'ul', label: '无序列表', icon: UnorderedListOutlined, handler: (v) => insertAtLineStart(v, '- ') },\n { id: 'ol', label: '有序列表', icon: OrderedListOutlined, handler: (v) => insertAtLineStart(v, '1. ') },\n { separatorBefore: true },\n { id: 'link', label: '链接', icon: LinkOutlined, handler: (v) => insertAtCursor(v, '[', '](url)', '文本') },\n { id: 'image', label: '图片', icon: PictureOutlined, handler: (v) => uploadImage(v) },\n { separatorBefore: true },\n { id: 'code', label: '行内代码', icon: FieldStringOutlined, handler: (v) => wrapSelection(v, '`', '`') },\n {\n id: 'codeblock',\n label: '代码块',\n icon: CodeOutlined,\n handler: (v) => {\n const { from, to } = v.state.selection.main\n const selected = v.state.doc.sliceString(from, to)\n const insert = selected ? '```\\n' + selected + '\\n```' : '```\\n\\n```'\n v.dispatch({ changes: { from, to, insert } })\n },\n },\n { id: 'quote', label: '引用', icon: BlockOutlined, handler: (v) => insertAtLineStart(v, '> ') },\n { id: 'textContainer', label: '文字容器', icon: TagsOutlined, handler: (v) => toggleWrap(v, '<text-container>', '</text-container>') },\n {\n id: 'hr',\n label: '分割线',\n icon: MinusOutlined,\n handler: (v) => {\n const { from } = v.state.selection.main\n v.dispatch({ changes: { from, insert: '\\n---\\n' } })\n },\n },\n ]\n}","import { EditorView } from '@codemirror/view'\n\n/**\n * 在光标位置插入文本\n */\nexport function insertAtCursor(view: EditorView, before: string, after: string = '', placeholder: string = ''): void {\n const { from, to } = view.state.selection.main\n const selectedText = view.state.doc.sliceString(from, to)\n const insert = selectedText\n ? `${before}${selectedText}${after}`\n : `${before}${placeholder}${after}`\n view.dispatch({ changes: { from, to, insert } })\n if (!selectedText && placeholder) {\n view.dispatch({ selection: { anchor: from + before.length } })\n }\n}\n\n/**\n * 在当前行行首插入文本\n */\nexport function insertAtLineStart(view: EditorView, text: string): void {\n const { from } = view.state.selection.main\n const line = view.state.doc.lineAt(from)\n view.dispatch({ changes: { from: line.from, to: line.from, insert: text } })\n view.dispatch({ selection: { anchor: line.from + text.length } })\n}\n\n/**\n * 上传文件到服务器\n */\nasync function uploadFileToServer(file: File): Promise<string | null> {\n const formData = new FormData()\n formData.append('file', file)\n\n const response = await fetch('https://remons.cn:3008/content/uploadMarkdownImg', {\n method: 'POST',\n body: formData,\n })\n\n if (!response.ok) return null\n\n const result = await response.json()\n\n if (result.code === 200 && result.success && result.data?.path) {\n return `https://remons.cn:3008${result.data.path}`\n }\n\n return null\n}\n\n/**\n * 图片上传:创建隐藏的 file input 触发选择,上传后插入 markdown 图片语法\n */\nexport function uploadImage(view: EditorView): void {\n const input = document.createElement('input')\n input.type = 'file'\n input.accept = 'image/*'\n input.style.display = 'none'\n\n input.addEventListener('change', async () => {\n if (!input.files || input.files.length === 0) return\n\n const file = input.files[0]\n const { from, to } = view.state.selection.main\n const selectedText = view.state.doc.sliceString(from, to) || '描述'\n\n const url = await uploadFileToServer(file)\n if (url) {\n const insert = `![${selectedText}](${url})`\n view.dispatch({ changes: { from, to, insert } })\n }\n\n input.remove()\n })\n\n document.body.appendChild(input)\n input.click()\n}\n\n/**\n * 用前缀和后缀包裹选中文本\n */\nexport function wrapSelection(view: EditorView, prefix: string, suffix: string): void {\n const { from, to, empty } = view.state.selection.main\n if (empty) return\n const selected = view.state.doc.sliceString(from, to)\n view.dispatch({ changes: { from, to, insert: `${prefix}${selected}${suffix}` } })\n}\n\n/**\n * 切换包裹:选中的文本已包含标签则移除,否则添加\n */\nexport function toggleWrap(\n view: EditorView,\n openTag: string,\n closeTag: string\n): void {\n const { from, to, empty } = view.state.selection.main\n const doc = view.state.doc\n\n // 选区完整包含标签 → 移除\n if (!empty) {\n const selected = doc.sliceString(from, to)\n if (selected.startsWith(openTag) && selected.endsWith(closeTag)) {\n const inner = selected.slice(openTag.length, -closeTag.length)\n view.dispatch({ changes: { from, to, insert: inner } })\n return\n }\n }\n\n // 光标/选区在标签内部 → 查找当前行的标签并移除\n {\n const cursorPos = empty ? from : to\n const line = doc.lineAt(cursorPos)\n const lineText = doc.sliceString(line.from, line.to)\n const openIdx = lineText.indexOf(openTag)\n const closeIdx = lineText.lastIndexOf(closeTag)\n\n if (openIdx >= 0 && closeIdx > openIdx) {\n const absOpen = line.from + openIdx\n const absClose = line.from + closeIdx\n // 确认光标/选区在标签范围内\n if (cursorPos >= absOpen + openTag.length && cursorPos <= absClose) {\n const inner = doc.sliceString(absOpen + openTag.length, absClose)\n view.dispatch({\n changes: {\n from: absOpen,\n to: absClose + closeTag.length,\n insert: inner,\n },\n })\n return\n }\n }\n }\n\n // 默认:添加包裹\n wrapSelection(view, openTag, closeTag)\n}","import { Button, Dropdown } from 'antd'\nimport { DownOutlined } from '@ant-design/icons'\nimport type { ToolbarButtonConfig } from '../../../types'\n\ninterface Props {\n item: ToolbarButtonConfig\n onClick: (item: ToolbarButtonConfig) => void\n}\n\nexport function ToolbarItemRenderer({ item, onClick }: Props) {\n const Icon = item.icon!\n\n // 有 children 的渲染为下拉菜单\n if (item.children && item.children.length > 0) {\n return (\n <>\n {item.separatorBefore && <div className=\"toolbar-separator\" />}\n <div className=\"toolbar-group\">\n <Dropdown\n menu={{\n items: item.children.map(child => ({\n type: 'item',\n key: child.id ?? '',\n label: (\n <span onClick={() => onClick(child)}>\n {child.icon && (() => { const ChildIcon = child.icon; return <ChildIcon />; })()}\n {' '}{child.label}\n </span>\n ),\n })),\n }}\n placement=\"bottomLeft\"\n >\n <Button className=\"toolbar-button\" type=\"text\" size=\"small\" title={item.label}>\n <Icon />\n <DownOutlined style={{ marginLeft: 2, fontSize: 10 }} />\n </Button>\n </Dropdown>\n </div>\n </>\n )\n }\n\n // 普通按钮\n return (\n <>\n {item.separatorBefore && <div className=\"toolbar-separator\" />}\n <div className=\"toolbar-group\">\n <Button\n id={item.id}\n className=\"toolbar-button\"\n type=\"text\"\n size=\"small\"\n title={item.label}\n onClick={() => onClick(item)}\n >\n <Icon />\n </Button>\n </div>\n </>\n )\n}","import React, { useCallback, useRef } from 'react'\nimport { EditorView } from '@codemirror/view'\nimport { Button, Dropdown } from 'antd'\nimport { TableOutlined } from '@ant-design/icons'\nimport TablePicker from './index'\n\nexport default function TableDropdown({ editorView }: { editorView: EditorView | null }) {\n const [open, setOpen] = React.useState(false)\n const openCountRef = useRef(0)\n\n const handleTableSelect = useCallback((rows: number, cols: number) => {\n if (!editorView || rows <= 0 || cols <= 0) return\n editorView.focus()\n const headerCells = Array(cols).fill(null).map((_, i) => ` 列${i + 1} `).join('|')\n const separator = Array(cols).fill(' --- ').join('|')\n const dataCell = Array(cols).fill(' 内容 ').join('|')\n const dataRows = Array(rows).fill(`|${dataCell}|`).join('\\n')\n const table = `\\n|${headerCells}|\\n|${separator}|\\n${dataRows}\\n`\n const { from } = editorView.state.selection.main\n editorView.dispatch({ changes: { from, insert: table } })\n }, [editorView])\n\n return (\n <Dropdown\n trigger={['click']}\n open={open}\n onOpenChange={setOpen}\n popupRender={() => {\n openCountRef.current++\n return (\n <div style={{ padding: 4 }} onClick={(e) => e.stopPropagation()}>\n <TablePicker\n key={openCountRef.current}\n onSelect={(size) => {\n handleTableSelect(size.rows, size.cols)\n setOpen(false)\n }}\n />\n </div>\n )\n }}\n placement=\"bottomLeft\"\n >\n <Button className=\"toolbar-button\" type=\"text\" size=\"small\" title=\"表格\">\n <TableOutlined />\n </Button>\n </Dropdown>\n )\n}","import { useState, useRef, useEffect, useCallback } from 'react'\nimport styles from './index.module.less'\n\ninterface TableSize {\n rows: number\n cols: number\n}\n\ninterface TablePickerProps {\n onSelect: (size: TableSize) => void\n}\n\nconst MAX_ROW = 12\nconst MAX_COL = 12\nconst REFRESH_TIME = 200\n\nexport default function TablePicker({ onSelect }: TablePickerProps) {\n const [hoverSize, setHoverSize] = useState({ row: 0, col: 0 })\n const [prevHoverSize, setPrevHoverSize] = useState({ row: 0, col: 0 })\n const [confirmedSize, setConfirmedSize] = useState({ row: 0, col: 0 })\n const maxColRef = useRef(0)\n const timerIdRef = useRef<any>(null)\n const refreshTimerRef = useRef<any>(null)\n const commitRef = useRef<{ cols: number; rows: number } | null>(null)\n\n const clearTimer = useCallback((flushFn?: () => void) => {\n if (timerIdRef.current) {\n timerIdRef.current = null\n }\n if (flushFn && maxColRef.current > 0) {\n timerIdRef.current = setTimeout(flushFn, REFRESH_TIME)\n }\n }, [])\n\n useEffect(\n () => () => {\n // Cleanup on unmount\n if (timerIdRef.current) clearTimeout(timerIdRef.current)\n if (refreshTimerRef.current) clearInterval(refreshTimerRef.current)\n },\n []\n )\n\n /**\n * Extend selection to (col, row)\n */\n function extend(col: number, row: number) {\n if (col > maxColRef.current) {\n maxColRef.current = col\n setHoverSize({ row, col })\n setConfirmedSize({ row, col })\n } else {\n setHoverSize({ row, col })\n if (row !== confirmedSize.row) {\n setConfirmedSize({ row, col: maxColRef.current })\n }\n if (row === 1) {\n commitRef.current = { cols: col, rows: 1 }\n setConfirmedSize({ row: 1, col })\n }\n }\n }\n\n /**\n * Flash animation for jumps\n */\n function flash(\n col: number,\n dir: 1 | -1,\n limit: number,\n finalCallback: (finalCol: number, finalRow: number) => void\n ) {\n let current = dir === 1 ? 1 : limit\n const step = dir === 1 ? 1 : -1\n let timer = 0\n\n refreshTimerRef.current = setInterval(() => {\n setHoverSize({ row: current, col })\n if (current > confirmedSize.row || dir === -1) {\n maxColRef.current = Math.min(MAX_COL, col)\n }\n current += step\n if ((dir === 1 && current > limit) || (dir === -1 && current < 1)) {\n clearInterval(refreshTimerRef.current)\n setHoverSize({ row: current - step, col })\n finalCallback(col, current - step)\n }\n }, 50)\n\n clearTimeout(timer)\n return () => {\n clearInterval(refreshTimerRef.current)\n }\n }\n\n /**\n * Core logic: mouse enters a cell at index in the grid\n */\n function onCellEnterCheck(index: number) {\n clearTimer()\n const row = Math.floor(index / MAX_COL) + 1\n const col = (index % MAX_COL) + 1\n\n const isDown = row > hoverSize.row\n\n if (Math.abs(row - hoverSize.row) <= 1) {\n // Normal hover - adjacent row\n extend(col, row)\n } else {\n // Jump - fast mouse movement\n if (isDown && (col > maxColRef.current || maxColRef.current >= MAX_COL)) {\n const finalRow = Math.min(row, MAX_ROW)\n flash(col, 1, finalRow, (c, r) => {\n setConfirmedSize({ row: r, col: c })\n })\n } else if (!isDown) {\n flash(col, -1, row, (c, r) => {\n setConfirmedSize({ row: r, col: c })\n if (r === 1) {\n commitRef.current = { cols: Math.min(MAX_COL, col), rows: 1 }\n setConfirmedSize({ row: 1, col: Math.min(MAX_COL, col) })\n }\n })\n }\n }\n\n // When column reaches max and current row exceeds previous, reset for next round\n if (col >= MAX_COL && row >= prevHoverSize.row) {\n if (row !== prevHoverSize.row) {\n maxColRef.current = 0\n setPrevHoverSize({ row: 0, col: 0 })\n }\n }\n setPrevHoverSize({ row, col })\n }\n\n function onMouseDown() {\n const size = commitRef.current || { cols: maxColRef.current, rows: confirmedSize.row }\n if (size.rows > 0 && size.cols > 0) {\n onSelect(size)\n }\n }\n\n return (\n <div\n className={styles.container}\n onMouseDown={onMouseDown}\n >\n <div\n className={styles.grid}\n style={{ gridTemplateColumns: `repeat(${MAX_COL}, 24px)` }}\n >\n {Array.from({ length: MAX_ROW * MAX_COL }).map((_, i) => {\n const row = Math.floor(i / MAX_COL) + 1\n const col = (i % MAX_COL) + 1\n const isSelected = row <= hoverSize.row && col <= maxColRef.current\n return (\n <div\n key={i}\n className={`${styles.cell} ${isSelected ? styles.cellActive : ''}`}\n onMouseEnter={() => onCellEnterCheck(i)}\n />\n )\n })}\n </div>\n <div className={styles.label}>\n {hoverSize.row} x {maxColRef.current || 1}\n </div>\n </div>\n )\n}\n",".container {\n padding: 4px;\n display: block;\n min-width: 200px;\n background: #fff;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n.grid {\n display: grid;\n gap: 2px;\n transition: all 0.1s ease;\n justify-content: flex-start;\n}\n.grid .cell {\n width: 24px;\n height: 24px;\n border: 1px solid #e8e8e8;\n border-radius: 2px;\n background: transparent;\n cursor: pointer;\n transition: all 0.1s ease;\n}\n.grid .cell.cellActive {\n border-color: #91caff;\n background: #ddf4ff;\n}\n.hint {\n padding: 12px 16px;\n color: #999;\n font-size: 12px;\n text-align: center;\n cursor: pointer;\n}\n.label {\n margin-top: 8px;\n font-size: 13px;\n color: #666;\n font-weight: 500;\n min-height: 20px;\n text-align: center;\n}\n","import { useState, useMemo } from 'react'\nimport { Modal } from 'antd'\nimport componentRegistry from '../../registry/componentRegistry'\n\n/**\n * 弹窗编辑容器属性的组件\n */\ninterface EditDialogProps {\n /** 弹窗是否显示 */\n visible: boolean\n /** 容器类型名称 */\n typeName: string\n /** 当前属性值 */\n currentProps: Record<string, string>\n /** 保存回调 */\n onSave: (props: Record<string, string>) => void\n /** 关闭回调 */\n onClose: () => void\n}\n\n/**\n * 属性编辑弹窗组件\n * 根据组件 schema 的 renderDialog 函数渲染弹窗内容\n */\nexport default function EditDialog({ visible, typeName, currentProps, onSave, onClose }: EditDialogProps) {\n const schema = componentRegistry.get(typeName)\n const [formProps, setFormProps] = useState<Record<string, string>>({})\n\n // 当弹窗打开时,初始化表单值\n const mergedProps = useMemo(() => {\n if (!schema) return currentProps\n const defaults: Record<string, string> = {}\n for (const field of schema.fields) {\n defaults[field.key] = field.defaultValue ?? ''\n }\n return { ...defaults, ...currentProps }\n }, [schema, currentProps])\n\n const handleDialogChange = (key: string, value: string) => {\n setFormProps(prev => ({ ...prev, [key]: value }))\n }\n\n const handleOk = () => {\n const finalProps = visible ? { ...mergedProps, ...formProps } : mergedProps\n onSave(finalProps)\n setFormProps({})\n }\n\n const handleClose = () => {\n setFormProps({})\n onClose()\n }\n\n if (!schema) {\n return null\n }\n\n return (\n <Modal\n title={`${schema.label} 属性编辑`}\n open={visible}\n onOk={handleOk}\n onCancel={handleClose}\n okText=\"保存\"\n cancelText=\"取消\"\n >\n {schema.renderDialog(visible ? { ...mergedProps, ...formProps } : mergedProps, handleDialogChange)}\n </Modal>\n )\n}","import type { EditorView } from '@codemirror/view'\n\n/**\n * 从属性对象生成 JSON 属性行。\n * 格式: { key: 'value', key2: 'value2' }\n */\nexport function writeContainerProps(props: Record<string, string>): string {\n const entries = Object.entries(props)\n .map(([k, v]) => `${k}: '${v}'`)\n .join(', ')\n return entries ? `{ ${entries} }` : '{}'\n}\n\n/**\n * 更新编辑器文档中的容器属性。\n * 替换 JSON 属性行(::: 打开行之后的一行)为新属性。\n */\nexport function updateContainerProps(\n view: EditorView,\n propsLineFrom: number,\n propsLineTo: number,\n newProps: Record<string, string>\n): void {\n const newPropsLine = writeContainerProps(newProps)\n view.dispatch({\n changes: {\n from: propsLineFrom,\n to: propsLineTo,\n insert: newPropsLine,\n },\n })\n}\n\n/**\n * 在当前光标位置插入新容器。\n * 创建 :::type 打开行 + JSON 属性 + ::: 关闭行的结构。\n */\nexport function insertContainer(\n view: EditorView,\n typeName: string,\n defaultProps: Record<string, string>\n): void {\n const { from } = view.state.selection.main\n const propsLine = writeContainerProps(defaultProps)\n const container = `:::${typeName}\\n${propsLine}\\n:::\\n`\n view.dispatch({ changes: { from, insert: container } })\n}\n\n/**\n * 在光标位置插入默认的 linkCard 容器。\n */\nexport function insertLinkCard(view: EditorView): void {\n insertContainer(view, 'linkCard', {\n title: '链接标题',\n link: 'https://example.com',\n description: '链接描述',\n theme: 'default',\n })\n}\n\n/**\n * 在光标位置插入默认的 note 容器。\n */\nexport function insertNote(view: EditorView): void {\n insertContainer(view, 'note', {\n type: 'info',\n title: '注意',\n content: '备注内容',\n })\n}","import { useEffect } from 'react'\nimport componentRegistry from '../registry/componentRegistry'\nimport type { ComponentSchema } from '../types'\n\n/**\n * 注册自定义容器组件到工具栏。\n * 支持 linkCard 形式的 schema(name + fields + renderDialog)。\n * fields 定义 Markdown 属性字段,renderDialog 定义弹窗内容。\n * \n * @example\n * ```tsx\n * const schema: ComponentSchema = {\n * name: 'alert',\n * label: '警告框',\n * icon: <AlertOutlined />,\n * fields: [{ key: 'level', defaultValue: 'info' }],\n * renderDialog: (props, onChange) => (\n * <Input value={props.level} onChange={e => onChange('level', e.target.value)} />\n * ),\n * }\n * \n * function MyPage() {\n * useRegisterToolbar(schema)\n * return <MarkdownEditor />\n * }\n * ```\n */\nexport function useRegisterToolbar(schema: ComponentSchema) {\n useEffect(() => {\n componentRegistry.register(schema.name, schema)\n return () => {\n componentRegistry.unregister(schema.name)\n }\n }, [schema])\n}\n"],"mappings":"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","names":["import_react","import_state","import_view","import_jsx_runtime","import_react","import_jsx_runtime","RenderMarkdown","import_antd","import_jsx_runtime","import_react","import_antd","import_icons","import_react","import_antd","import_icons","import_jsx_runtime","import_react","import_antd","import_icons","import_react","import_jsx_runtime","import_jsx_runtime","React","import_jsx_runtime","import_react","import_antd","import_jsx_runtime","import_icons","import_jsx_runtime","import_react"]}
package/dist/index.css ADDED
@@ -0,0 +1,265 @@
1
+ @import "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;600;700&display=swap";
2
+
3
+ /* src/components/Toolbar/renderers/TablePicker/index.module.less */
4
+ .container {
5
+ padding: 4px;
6
+ display: block;
7
+ min-width: 200px;
8
+ background: #fff;
9
+ border: 1px solid #e5e7eb;
10
+ border-radius: 6px;
11
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
12
+ }
13
+ .grid {
14
+ display: grid;
15
+ gap: 2px;
16
+ transition: all 0.1s ease;
17
+ justify-content: flex-start;
18
+ }
19
+ .grid .cell {
20
+ width: 24px;
21
+ height: 24px;
22
+ border: 1px solid #e8e8e8;
23
+ border-radius: 2px;
24
+ background: transparent;
25
+ cursor: pointer;
26
+ transition: all 0.1s ease;
27
+ }
28
+ .grid .cell.cellActive {
29
+ border-color: #91caff;
30
+ background: #ddf4ff;
31
+ }
32
+ .hint {
33
+ padding: 12px 16px;
34
+ color: #999;
35
+ font-size: 12px;
36
+ text-align: center;
37
+ cursor: pointer;
38
+ }
39
+ .label {
40
+ margin-top: 8px;
41
+ font-size: 13px;
42
+ color: #666;
43
+ font-weight: 500;
44
+ min-height: 20px;
45
+ text-align: center;
46
+ }
47
+
48
+ /* src/App.css */
49
+ .app {
50
+ height: 100vh;
51
+ display: flex;
52
+ flex-direction: column;
53
+ }
54
+ .app.fullscreen {
55
+ position: fixed;
56
+ top: 0;
57
+ left: 0;
58
+ right: 0;
59
+ bottom: 0;
60
+ z-index: 9999;
61
+ background: #fff;
62
+ width: 100vw;
63
+ height: 100vh;
64
+ }
65
+ .toolbar {
66
+ flex-shrink: 0;
67
+ display: flex;
68
+ align-items: center;
69
+ padding: 4px 8px;
70
+ background: #f6f8fa;
71
+ border-bottom: 1px solid #d0d7de;
72
+ gap: 4px;
73
+ overflow-x: auto;
74
+ }
75
+ .toolbar-group {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 2px;
79
+ }
80
+ .toolbar-button {
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ min-width: 32px;
85
+ height: 32px;
86
+ padding: 0 8px;
87
+ border: none;
88
+ border-radius: 6px;
89
+ background: transparent;
90
+ cursor: pointer;
91
+ font-size: 16px;
92
+ font-weight: 500;
93
+ color: #24292f;
94
+ transition: background-color 0.15s, color 0.15s;
95
+ white-space: nowrap;
96
+ }
97
+ .toolbar-button:hover {
98
+ background: #e1e4e8;
99
+ color: #0969da;
100
+ }
101
+ .toolbar-button:active {
102
+ background: #d0d7de;
103
+ }
104
+ .toolbar-button.ant-btn {
105
+ min-width: 32px;
106
+ height: 32px;
107
+ padding: 0 8px;
108
+ border-color: transparent !important;
109
+ box-shadow: none !important;
110
+ }
111
+ .toolbar-button.ant-btn:hover {
112
+ background: #e1e4e8;
113
+ color: #0969da;
114
+ border-color: transparent !important;
115
+ box-shadow: none !important;
116
+ }
117
+ .toolbar-button.ant-btn:active {
118
+ border-color: transparent !important;
119
+ box-shadow: none !important;
120
+ }
121
+ :where(.css-dev-only-do-not-override-u6izyw).ant-btn:not(:disabled):not(.ant-btn-disabled):hover {
122
+ border: none !important;
123
+ }
124
+ .toolbar-separator {
125
+ width: 1px;
126
+ height: 24px;
127
+ background: #d0d7de;
128
+ margin: 0 4px;
129
+ }
130
+ .split-panels {
131
+ flex: 1;
132
+ min-height: 0;
133
+ overflow: hidden;
134
+ }
135
+ .split-panels .ant-splitter {
136
+ height: 100%;
137
+ }
138
+ .split-panels .ant-splitter-panel {
139
+ min-height: 0;
140
+ }
141
+ .editor {
142
+ height: 100%;
143
+ }
144
+ .cm-scroller {
145
+ font-family:
146
+ Consolas,
147
+ Inter,
148
+ Noto Sans SC,
149
+ -apple-system,
150
+ BlinkMacSystemFont,
151
+ Segoe UI,
152
+ PingFang SC,
153
+ Microsoft YaHei,
154
+ sans-serif !important;
155
+ }
156
+ .cm-editor {
157
+ height: 100%;
158
+ }
159
+ .preview {
160
+ height: 100%;
161
+ overflow: auto;
162
+ }
163
+ .md-container {
164
+ margin: 16px 0;
165
+ border-radius: 6px;
166
+ border: 1px solid #d0d7de;
167
+ overflow: hidden;
168
+ }
169
+ .link-card {
170
+ background: #f6f8fa;
171
+ }
172
+ .link-card-content {
173
+ padding: 16px;
174
+ }
175
+ .link-card-title {
176
+ margin: 0 0 4px;
177
+ font-size: 16px;
178
+ font-weight: 600;
179
+ }
180
+ .link-card-title a {
181
+ color: #0969da;
182
+ text-decoration: none;
183
+ }
184
+ .link-card-title a:hover {
185
+ text-decoration: underline;
186
+ }
187
+ .link-card-desc {
188
+ margin: 0;
189
+ font-size: 14px;
190
+ color: #57606a;
191
+ line-height: 1.5;
192
+ }
193
+ .link-card-primary {
194
+ background: #ddf4ff;
195
+ border-color: #54aeff;
196
+ }
197
+ .link-card-primary .link-card-title a {
198
+ color: #0550ae;
199
+ }
200
+ .link-card-warning {
201
+ background: #fff8c5;
202
+ border-color: #d4a72c;
203
+ }
204
+ .link-card-warning .link-card-title a {
205
+ color: #7d4e00;
206
+ }
207
+ .note {
208
+ padding: 12px 16px;
209
+ }
210
+ .note-info {
211
+ background: #ddf4ff;
212
+ border-color: #54aeff;
213
+ }
214
+ .note-tip {
215
+ background: #dafbe1;
216
+ border-color: #4ac26b;
217
+ }
218
+ .note-warning {
219
+ background: #fff8c5;
220
+ border-color: #d4a72c;
221
+ }
222
+ .note-danger {
223
+ background: #ffebe9;
224
+ border-color: #fb857b;
225
+ }
226
+ .note-title {
227
+ margin: 0 0 4px;
228
+ font-size: 14px;
229
+ font-weight: 600;
230
+ }
231
+ .note-text {
232
+ font-size: 14px;
233
+ line-height: 1.5;
234
+ }
235
+ .edit-dialog {
236
+ padding: 8px 0;
237
+ }
238
+ .edit-dialog-field {
239
+ margin-bottom: 16px;
240
+ }
241
+ .edit-dialog-label {
242
+ display: block;
243
+ margin-bottom: 6px;
244
+ font-size: 14px;
245
+ font-weight: 500;
246
+ color: #24292f;
247
+ }
248
+ .edit-dialog-input {
249
+ width: 100%;
250
+ padding: 6px 10px;
251
+ font-size: 14px;
252
+ border: 1px solid #d0d7de;
253
+ border-radius: 6px;
254
+ outline: none;
255
+ transition: border-color 0.15s;
256
+ }
257
+ .edit-dialog-input:focus {
258
+ border-color: #0969da;
259
+ box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.1);
260
+ }
261
+ .edit-dialog-textarea {
262
+ resize: vertical;
263
+ font-family: inherit;
264
+ }
265
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Toolbar/renderers/TablePicker/index.module.less","../src/App.css"],"sourcesContent":[".container {\n padding: 4px;\n display: block;\n min-width: 200px;\n background: #fff;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n.grid {\n display: grid;\n gap: 2px;\n transition: all 0.1s ease;\n justify-content: flex-start;\n}\n.grid .cell {\n width: 24px;\n height: 24px;\n border: 1px solid #e8e8e8;\n border-radius: 2px;\n background: transparent;\n cursor: pointer;\n transition: all 0.1s ease;\n}\n.grid .cell.cellActive {\n border-color: #91caff;\n background: #ddf4ff;\n}\n.hint {\n padding: 12px 16px;\n color: #999;\n font-size: 12px;\n text-align: center;\n cursor: pointer;\n}\n.label {\n margin-top: 8px;\n font-size: 13px;\n color: #666;\n font-weight: 500;\n min-height: 20px;\n text-align: center;\n}\n","@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;600;700&display=swap');\n\n\n.app {\n height: 100vh;\n display: flex;\n flex-direction: column;\n}\n\n.app.fullscreen {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 9999;\n background: #fff;\n width: 100vw;\n height: 100vh;\n}\n\n.toolbar {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n padding: 4px 8px;\n background: #f6f8fa;\n border-bottom: 1px solid #d0d7de;\n gap: 4px;\n overflow-x: auto;\n}\n\n.toolbar-group {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n padding: 0 8px;\n border: none;\n border-radius: 6px;\n background: transparent;\n cursor: pointer;\n font-size: 16px;\n font-weight: 500;\n color: #24292f;\n transition: background-color 0.15s, color 0.15s;\n white-space: nowrap;\n}\n\n.toolbar-button:hover {\n background: #e1e4e8;\n color: #0969da;\n}\n\n.toolbar-button:active {\n background: #d0d7de;\n}\n\n.toolbar-button.ant-btn {\n min-width: 32px;\n height: 32px;\n padding: 0 8px;\n border-color: transparent !important;\n box-shadow: none !important;\n}\n\n.toolbar-button.ant-btn:hover {\n background: #e1e4e8;\n color: #0969da;\n border-color: transparent !important;\n box-shadow: none !important;\n}\n\n.toolbar-button.ant-btn:active {\n border-color: transparent !important;\n box-shadow: none !important;\n}\n\n:where(.css-dev-only-do-not-override-u6izyw).ant-btn:not(:disabled):not(.ant-btn-disabled):hover {\n border: none !important\n}\n\n.toolbar-separator {\n width: 1px;\n height: 24px;\n background: #d0d7de;\n margin: 0 4px;\n}\n\n.split-panels {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n.split-panels .ant-splitter {\n height: 100%;\n}\n\n.split-panels .ant-splitter-panel {\n min-height: 0;\n}\n\n.editor {\n height: 100%;\n \n}\n\n.cm-scroller {\n font-family: Consolas,Inter,Noto Sans SC,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Microsoft YaHei,sans-serif !important;\n}\n\n.cm-editor {\n height: 100%;\n}\n\n.preview {\n height: 100%;\n overflow: auto;\n}\n\n\n/* 通用容器基类 */\n.md-container {\n margin: 16px 0;\n border-radius: 6px;\n border: 1px solid #d0d7de;\n overflow: hidden;\n}\n\n/* 链接卡片 */\n.link-card {\n background: #f6f8fa;\n}\n\n.link-card-content {\n padding: 16px;\n}\n\n.link-card-title {\n margin: 0 0 4px;\n font-size: 16px;\n font-weight: 600;\n}\n\n.link-card-title a {\n color: #0969da;\n text-decoration: none;\n}\n\n.link-card-title a:hover {\n text-decoration: underline;\n}\n\n.link-card-desc {\n margin: 0;\n font-size: 14px;\n color: #57606a;\n line-height: 1.5;\n}\n\n.link-card-primary {\n background: #ddf4ff;\n border-color: #54aeff;\n}\n\n.link-card-primary .link-card-title a {\n color: #0550ae;\n}\n\n.link-card-warning {\n background: #fff8c5;\n border-color: #d4a72c;\n}\n\n.link-card-warning .link-card-title a {\n color: #7d4e00;\n}\n\n/* 注释组件 */\n.note {\n padding: 12px 16px;\n}\n\n.note-info {\n background: #ddf4ff;\n border-color: #54aeff;\n}\n\n.note-tip {\n background: #dafbe1;\n border-color: #4ac26b;\n}\n\n.note-warning {\n background: #fff8c5;\n border-color: #d4a72c;\n}\n\n.note-danger {\n background: #ffebe9;\n border-color: #fb857b;\n}\n\n.note-title {\n margin: 0 0 4px;\n font-size: 14px;\n font-weight: 600;\n}\n\n.note-text {\n font-size: 14px;\n line-height: 1.5;\n}\n\n/* 编辑弹窗样式 */\n.edit-dialog {\n padding: 8px 0;\n}\n\n.edit-dialog-field {\n margin-bottom: 16px;\n}\n\n.edit-dialog-label {\n display: block;\n margin-bottom: 6px;\n font-size: 14px;\n font-weight: 500;\n color: #24292f;\n}\n\n.edit-dialog-input {\n width: 100%;\n padding: 6px 10px;\n font-size: 14px;\n border: 1px solid #d0d7de;\n border-radius: 6px;\n outline: none;\n transition: border-color 0.15s;\n}\n\n.edit-dialog-input:focus {\n border-color: #0969da;\n box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.1);\n}\n\n.edit-dialog-textarea {\n resize: vertical;\n font-family: inherit;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,90 @@
1
+ import * as React from 'react';
2
+ import React__default from 'react';
3
+ import { EditorView } from '@codemirror/view';
4
+ import { EditorState } from '@codemirror/state';
5
+
6
+ interface MarkdownEditorRef {
7
+ getContent: () => string;
8
+ }
9
+ interface MarkdownEditorProps {
10
+ /** 初始内容 */
11
+ defaultValue?: string;
12
+ /** 受控内容 */
13
+ value?: string;
14
+ /** 内容变化回调 */
15
+ onChange?: (value: string) => void;
16
+ /** 是否显示预览面板 */
17
+ showPreview?: boolean;
18
+ /** 是否支持全屏 */
19
+ fullscreen?: boolean;
20
+ }
21
+ declare const _default: React.ForwardRefExoticComponent<MarkdownEditorProps & React.RefAttributes<MarkdownEditorRef>>;
22
+
23
+ interface ToolbarButtonConfig {
24
+ id?: string;
25
+ icon?: React__default.ComponentType;
26
+ label?: string;
27
+ children?: ToolbarButtonConfig[];
28
+ handler?: (view: EditorView, state: EditorState) => void;
29
+ separatorBefore?: boolean;
30
+ customRender?: () => React__default.ReactNode;
31
+ }
32
+ /**
33
+ * 弹窗属性配置
34
+ * 用于定义 Markdown 容器中需要填充的属性字段
35
+ */
36
+ interface DialogField {
37
+ /** 字段键名,用于 Markdown 属性填充 */
38
+ key: string;
39
+ /** 默认值 */
40
+ defaultValue?: string;
41
+ }
42
+ /**
43
+ * 组件 Schema
44
+ * 每个容器组件(如 :::linkCard)定义可编辑字段和弹窗渲染逻辑
45
+ */
46
+ interface ComponentSchema {
47
+ /** 容器名称,对应 :::name 语法 */
48
+ name: string;
49
+ /** 工具栏按钮显示的标签 */
50
+ label: string;
51
+ /** 工具栏按钮显示的图标 */
52
+ icon?: React__default.ReactNode;
53
+ /** 弹窗属性配置 — 用于定义 Markdown 内容中需要填充的字段 */
54
+ fields: DialogField[];
55
+ /**
56
+ * 弹窗内容渲染函数(必填)
57
+ * 接收当前属性值和回调函数,由用户完全自定义弹窗内容的渲染
58
+ * @param props 当前属性值,用于回显
59
+ * @param onChange 属性变更回调,用于更新表单值
60
+ * @returns 弹窗内容的 ReactNode
61
+ */
62
+ renderDialog: (props: Record<string, string>, onChange: (key: string, value: string) => void) => React__default.ReactNode;
63
+ }
64
+
65
+ /**
66
+ * 注册自定义容器组件到工具栏。
67
+ * 支持 linkCard 形式的 schema(name + fields + renderDialog)。
68
+ * fields 定义 Markdown 属性字段,renderDialog 定义弹窗内容。
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * const schema: ComponentSchema = {
73
+ * name: 'alert',
74
+ * label: '警告框',
75
+ * icon: <AlertOutlined />,
76
+ * fields: [{ key: 'level', defaultValue: 'info' }],
77
+ * renderDialog: (props, onChange) => (
78
+ * <Input value={props.level} onChange={e => onChange('level', e.target.value)} />
79
+ * ),
80
+ * }
81
+ *
82
+ * function MyPage() {
83
+ * useRegisterToolbar(schema)
84
+ * return <MarkdownEditor />
85
+ * }
86
+ * ```
87
+ */
88
+ declare function useRegisterToolbar(schema: ComponentSchema): void;
89
+
90
+ export { type ComponentSchema, type DialogField, _default as MarkdownEditor, type MarkdownEditorProps, type MarkdownEditorRef, type ToolbarButtonConfig, useRegisterToolbar };
@@ -0,0 +1,90 @@
1
+ import * as React from 'react';
2
+ import React__default from 'react';
3
+ import { EditorView } from '@codemirror/view';
4
+ import { EditorState } from '@codemirror/state';
5
+
6
+ interface MarkdownEditorRef {
7
+ getContent: () => string;
8
+ }
9
+ interface MarkdownEditorProps {
10
+ /** 初始内容 */
11
+ defaultValue?: string;
12
+ /** 受控内容 */
13
+ value?: string;
14
+ /** 内容变化回调 */
15
+ onChange?: (value: string) => void;
16
+ /** 是否显示预览面板 */
17
+ showPreview?: boolean;
18
+ /** 是否支持全屏 */
19
+ fullscreen?: boolean;
20
+ }
21
+ declare const _default: React.ForwardRefExoticComponent<MarkdownEditorProps & React.RefAttributes<MarkdownEditorRef>>;
22
+
23
+ interface ToolbarButtonConfig {
24
+ id?: string;
25
+ icon?: React__default.ComponentType;
26
+ label?: string;
27
+ children?: ToolbarButtonConfig[];
28
+ handler?: (view: EditorView, state: EditorState) => void;
29
+ separatorBefore?: boolean;
30
+ customRender?: () => React__default.ReactNode;
31
+ }
32
+ /**
33
+ * 弹窗属性配置
34
+ * 用于定义 Markdown 容器中需要填充的属性字段
35
+ */
36
+ interface DialogField {
37
+ /** 字段键名,用于 Markdown 属性填充 */
38
+ key: string;
39
+ /** 默认值 */
40
+ defaultValue?: string;
41
+ }
42
+ /**
43
+ * 组件 Schema
44
+ * 每个容器组件(如 :::linkCard)定义可编辑字段和弹窗渲染逻辑
45
+ */
46
+ interface ComponentSchema {
47
+ /** 容器名称,对应 :::name 语法 */
48
+ name: string;
49
+ /** 工具栏按钮显示的标签 */
50
+ label: string;
51
+ /** 工具栏按钮显示的图标 */
52
+ icon?: React__default.ReactNode;
53
+ /** 弹窗属性配置 — 用于定义 Markdown 内容中需要填充的字段 */
54
+ fields: DialogField[];
55
+ /**
56
+ * 弹窗内容渲染函数(必填)
57
+ * 接收当前属性值和回调函数,由用户完全自定义弹窗内容的渲染
58
+ * @param props 当前属性值,用于回显
59
+ * @param onChange 属性变更回调,用于更新表单值
60
+ * @returns 弹窗内容的 ReactNode
61
+ */
62
+ renderDialog: (props: Record<string, string>, onChange: (key: string, value: string) => void) => React__default.ReactNode;
63
+ }
64
+
65
+ /**
66
+ * 注册自定义容器组件到工具栏。
67
+ * 支持 linkCard 形式的 schema(name + fields + renderDialog)。
68
+ * fields 定义 Markdown 属性字段,renderDialog 定义弹窗内容。
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * const schema: ComponentSchema = {
73
+ * name: 'alert',
74
+ * label: '警告框',
75
+ * icon: <AlertOutlined />,
76
+ * fields: [{ key: 'level', defaultValue: 'info' }],
77
+ * renderDialog: (props, onChange) => (
78
+ * <Input value={props.level} onChange={e => onChange('level', e.target.value)} />
79
+ * ),
80
+ * }
81
+ *
82
+ * function MyPage() {
83
+ * useRegisterToolbar(schema)
84
+ * return <MarkdownEditor />
85
+ * }
86
+ * ```
87
+ */
88
+ declare function useRegisterToolbar(schema: ComponentSchema): void;
89
+
90
+ export { type ComponentSchema, type DialogField, _default as MarkdownEditor, type MarkdownEditorProps, type MarkdownEditorRef, type ToolbarButtonConfig, useRegisterToolbar };