@yanglingfeng/md-web 0.1.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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/MarkdownEditor.d.ts +2 -0
- package/dist/MarkdownPreview.d.ts +6 -0
- package/dist/Toolbar.d.ts +10 -0
- package/dist/editorCommands.d.ts +10 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1480 -0
- package/dist/index.js.map +1 -0
- package/dist/legacyDiagrams.d.ts +8 -0
- package/dist/remarkPlugins.d.ts +9 -0
- package/dist/style.css +2 -0
- package/dist/types.d.ts +43 -0
- package/dist/useScrollSync.d.ts +3 -0
- package/package.json +106 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/components/MarkdownEditor/editorCommands.ts","../src/components/MarkdownEditor/legacyDiagrams.ts","../src/components/MarkdownEditor/remarkPlugins.ts","../src/components/MarkdownEditor/MarkdownPreview.tsx","../src/components/MarkdownEditor/Toolbar.tsx","../src/components/MarkdownEditor/useScrollSync.ts","../src/components/MarkdownEditor/MarkdownEditor.tsx"],"sourcesContent":["import type { EditorView } from '@codemirror/view'\nimport type { EditorCommand } from './types'\n\ninterface Transformation {\n text: string\n selectionFrom: number\n selectionTo: number\n}\n\nconst wrappedCommands: Partial<\n Record<EditorCommand, { before: string; after: string; placeholder: string }>\n> = {\n bold: { before: '**', after: '**', placeholder: '加粗文字' },\n italic: { before: '*', after: '*', placeholder: '斜体文字' },\n inlineCode: { before: '`', after: '`', placeholder: '代码' },\n link: { before: '[', after: '](https://)', placeholder: '链接文字' },\n image: { before: '', placeholder: '图片描述' },\n formula: { before: '$', after: '$', placeholder: 'E = mc^2' },\n}\n\nconst linePrefixes: Partial<Record<EditorCommand, string>> = {\n heading1: '# ',\n heading2: '## ',\n heading3: '### ',\n heading4: '#### ',\n heading5: '##### ',\n quote: '> ',\n bulletList: '- ',\n orderedList: '1. ',\n task: '- [ ] ',\n}\n\nexport function transformSelection(\n command: EditorCommand,\n selectedText: string,\n): Transformation {\n const wrapped = wrappedCommands[command]\n if (wrapped) {\n if (\n selectedText.startsWith(wrapped.before) &&\n selectedText.endsWith(wrapped.after)\n ) {\n const content = selectedText.slice(\n wrapped.before.length,\n selectedText.length - wrapped.after.length,\n )\n return {\n text: content,\n selectionFrom: 0,\n selectionTo: content.length,\n }\n }\n const content = selectedText || wrapped.placeholder\n return {\n text: `${wrapped.before}${content}${wrapped.after}`,\n selectionFrom: wrapped.before.length,\n selectionTo: wrapped.before.length + content.length,\n }\n }\n\n const prefix = linePrefixes[command]\n if (prefix) {\n const content = selectedText || '文本'\n const text = content\n .split('\\n')\n .map((line, index) => {\n if (command === 'orderedList') return `${index + 1}. ${line}`\n return `${prefix}${line}`\n })\n .join('\\n')\n return {\n text,\n selectionFrom: prefix.length,\n selectionTo: text.length,\n }\n }\n\n if (command === 'codeBlock') {\n const content = selectedText || '在这里输入代码'\n return {\n text: `\\`\\`\\`\\n${content}\\n\\`\\`\\``,\n selectionFrom: 4,\n selectionTo: 4 + content.length,\n }\n }\n\n if (command === 'divider') {\n return { text: '\\n---\\n', selectionFrom: 5, selectionTo: 5 }\n }\n\n if (command === 'table') {\n const text =\n '| 列 1 | 列 2 | 列 3 |\\n| --- | --- | --- |\\n| 内容 | 内容 | 内容 |'\n return { text, selectionFrom: 2, selectionTo: 5 }\n }\n\n return { text: selectedText, selectionFrom: 0, selectionTo: selectedText.length }\n}\n\nexport function runEditorCommand(\n view: EditorView | null,\n command: EditorCommand,\n): boolean {\n if (!view) return false\n\n const { from, to } = view.state.selection.main\n const selectedText = view.state.sliceDoc(from, to)\n const wrapped = wrappedCommands[command]\n if (wrapped && selectedText) {\n const beforeFrom = from - wrapped.before.length\n const afterTo = to + wrapped.after.length\n if (\n beforeFrom >= 0 &&\n view.state.sliceDoc(beforeFrom, from) === wrapped.before &&\n view.state.sliceDoc(to, afterTo) === wrapped.after\n ) {\n view.dispatch({\n changes: [\n { from: beforeFrom, to: from, insert: '' },\n { from: to, to: afterTo, insert: '' },\n ],\n selection: {\n anchor: beforeFrom,\n head: to - wrapped.before.length,\n },\n })\n view.focus()\n return true\n }\n }\n\n const linePrefix = linePrefixes[command]\n if (linePrefix && from === to) {\n const line = view.state.doc.lineAt(from)\n const isHeading = command.startsWith('heading')\n const existingPrefix = isHeading\n ? /^#{1,6}\\s*/.exec(line.text)?.[0]\n : line.text.startsWith(linePrefix)\n ? linePrefix\n : undefined\n const withoutPrefix = existingPrefix\n ? line.text.slice(existingPrefix.length)\n : line.text\n const nextText =\n existingPrefix === linePrefix ? withoutPrefix : `${linePrefix}${withoutPrefix}`\n const cursorOffset = Math.max(\n 0,\n from - line.from - (existingPrefix?.length ?? 0),\n )\n const nextCursor =\n line.from +\n (existingPrefix === linePrefix ? 0 : linePrefix.length) +\n cursorOffset\n view.dispatch({\n changes: { from: line.from, to: line.to, insert: nextText },\n selection: { anchor: nextCursor },\n })\n view.focus()\n return true\n }\n\n const result = transformSelection(command, selectedText)\n\n view.dispatch({\n changes: { from, to, insert: result.text },\n selection: {\n anchor: from + result.selectionFrom,\n head: from + result.selectionTo,\n },\n scrollIntoView: true,\n })\n view.focus()\n return true\n}\n","const flowShapes: Record<string, [string, string]> = {\n start: ['([', '])'],\n end: ['([', '])'],\n operation: ['[', ']'],\n parallel: ['[', ']'],\n inputoutput: ['[/', '/]'],\n subroutine: ['[[', ']]'],\n condition: ['{', '}'],\n}\n\nconst directions = new Set(['top', 'right', 'bottom', 'left'])\n\nfunction quoteLabel(text: string) {\n return `\"${text.replace(/\"/g, '#quot;').replace(/\\\\n/g, '<br/>')}\"`\n}\n\nfunction safeId(id: string, index: number) {\n return /^[A-Za-z_][\\w-]*$/.test(id) ? id : `n${index}`\n}\n\ninterface FlowEdgeEnd {\n id: string\n label?: string\n}\n\nfunction parseFlowEdgeEnd(raw: string): FlowEdgeEnd | null {\n const match = /^([^(\\s]+)\\s*(?:\\(([^)]*)\\))?$/.exec(raw.trim())\n if (!match) return null\n const label = (match[2] ?? '')\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part && !directions.has(part.toLowerCase()))\n .join(' ')\n return { id: match[1], label: label || undefined }\n}\n\n/**\n * flowchart.js 的节点定义与连线语法与 Mermaid 不兼容,这里做一次等价改写。\n */\nexport function flowToMermaid(source: string): string {\n const ids = new Map<string, string>()\n const nodes: string[] = []\n const edges: string[] = []\n\n const resolve = (id: string) => {\n const existing = ids.get(id)\n if (existing) return existing\n const generated = safeId(id, ids.size + 1)\n ids.set(id, generated)\n return generated\n }\n\n for (const rawLine of source.split('\\n')) {\n const line = rawLine.trim()\n if (!line || line.startsWith('//')) continue\n\n const definition = /^([^=\\s]+)\\s*=>\\s*([a-z]+)\\s*(?::([\\s\\S]*))?$/i.exec(line)\n if (definition) {\n const type = definition[2].toLowerCase()\n const shape = flowShapes[type] ?? flowShapes.operation\n const label = (definition[3] ?? definition[2])\n .replace(/:>[^|]*$/, '')\n .split('|')[0]\n .trim()\n nodes.push(\n ` ${resolve(definition[1].trim())}${shape[0]}${quoteLabel(label || type)}${shape[1]}`,\n )\n continue\n }\n\n if (!line.includes('->')) continue\n\n const segments = line.split('->')\n for (let index = 0; index < segments.length - 1; index += 1) {\n const from = parseFlowEdgeEnd(segments[index])\n const to = parseFlowEdgeEnd(segments[index + 1])\n if (!from || !to) continue\n const arrow = from.label ? `-->|${quoteLabel(from.label)}|` : '-->'\n edges.push(` ${resolve(from.id)} ${arrow} ${resolve(to.id)}`)\n }\n }\n\n return ['flowchart TB', ...nodes, ...edges].join('\\n')\n}\n\nconst arrowMap: Record<string, string> = {\n '->': '->>',\n '-->': '-->>',\n '->>': '->>',\n '-->>': '-->>',\n}\n\n/**\n * js-sequence-diagrams 的箭头与参与者写法需要转成 Mermaid 时序图。\n */\nexport function sequenceToMermaid(source: string): string {\n const participants = new Map<string, string>()\n const declared: string[] = []\n const statements: string[] = []\n\n const resolve = (name: string) => {\n const trimmed = name.trim()\n const existing = participants.get(trimmed)\n if (existing) return existing\n const id = /^[A-Za-z_][\\w-]*$/.test(trimmed)\n ? trimmed\n : `P${participants.size + 1}`\n participants.set(trimmed, id)\n declared.push(id === trimmed ? ` participant ${id}` : ` participant ${id} as ${trimmed}`)\n return id\n }\n\n const normalizeText = (text: string) => text.trim().replace(/\\\\n/g, '<br/>')\n\n for (const rawLine of source.split('\\n')) {\n const line = rawLine.trim()\n if (!line || line.startsWith('#')) continue\n\n const title = /^title:?\\s+(.+)$/i.exec(line)\n if (title) {\n statements.push(` title: ${normalizeText(title[1])}`)\n continue\n }\n\n const participant = /^participant\\s+(.+?)(?:\\s+as\\s+(.+))?$/i.exec(line)\n if (participant) {\n resolve(participant[2] ?? participant[1])\n continue\n }\n\n const note = /^note\\s+(left of|right of|over)\\s+([^:]+):\\s*(.*)$/i.exec(line)\n if (note) {\n const targets = note[2]\n .split(',')\n .map((name) => resolve(name))\n .join(',')\n statements.push(\n ` Note ${note[1].toLowerCase()} ${targets}: ${normalizeText(note[3])}`,\n )\n continue\n }\n\n const message = /^(.+?)\\s*(-{1,2}>{1,2})\\s*([^:]+):\\s*(.*)$/.exec(line)\n if (message) {\n const arrow = arrowMap[message[2]] ?? '->>'\n statements.push(\n ` ${resolve(message[1])}${arrow}${resolve(message[3])}: ${normalizeText(message[4])}`,\n )\n }\n }\n\n return ['sequenceDiagram', ...declared, ...statements].join('\\n')\n}\n","import GithubSlugger from 'github-slugger'\nimport type { Blockquote, Heading, ListItem, Paragraph, PhrasingContent, Root } from 'mdast'\nimport { toString } from 'mdast-util-to-string'\nimport { visit } from 'unist-util-visit'\n\nconst NOTE_META = /@\\(([^()\\n]{1,120})\\)\\[([^\\][\\n]{0,240})\\]/g\nconst TOC_MARKER = /^\\[toc\\]$/i\n\nfunction chip(className: string, value: string): PhrasingContent {\n return {\n type: 'emphasis',\n data: { hName: 'span', hProperties: { className: [className] } },\n children: [{ type: 'text', value }],\n }\n}\n\n/**\n * 马克飞象扩展语法:`@(笔记本)[标签A|标签B]` 用于指定笔记本与标签。\n */\nexport function remarkNoteMeta() {\n return (tree: Root) => {\n visit(tree, 'text', (node, index, parent) => {\n if (!parent || index === null || index === undefined) return\n if (parent.type === 'link' || parent.type === 'linkReference') return\n\n NOTE_META.lastIndex = 0\n if (!NOTE_META.test(node.value)) return\n NOTE_META.lastIndex = 0\n\n const replacement: PhrasingContent[] = []\n let cursor = 0\n let match: RegExpExecArray | null\n\n while ((match = NOTE_META.exec(node.value)) !== null) {\n if (match.index > cursor) {\n replacement.push({ type: 'text', value: node.value.slice(cursor, match.index) })\n }\n const tags = match[2]\n .split('|')\n .map((tag) => tag.trim())\n .filter(Boolean)\n replacement.push({\n type: 'emphasis',\n data: { hName: 'span', hProperties: { className: ['md-note-meta'] } },\n children: [\n chip('md-note-meta__book', match[1].trim()),\n ...tags.map((tag) => chip('md-note-meta__tag', tag)),\n ],\n })\n cursor = match.index + match[0].length\n }\n\n if (cursor < node.value.length) {\n replacement.push({ type: 'text', value: node.value.slice(cursor) })\n }\n\n parent.children.splice(index, 1, ...replacement)\n return index + replacement.length\n })\n }\n}\n\nfunction tocEntry(slugger: GithubSlugger, heading: Heading): ListItem {\n const text = toString(heading)\n return {\n type: 'listItem',\n spread: false,\n data: { hProperties: { className: [`md-doc-toc__item--${heading.depth}`] } },\n children: [\n {\n type: 'paragraph',\n children: [\n {\n type: 'link',\n url: `#${slugger.slug(text)}`,\n children: [{ type: 'text', value: text }],\n },\n ],\n },\n ],\n }\n}\n\n/**\n * 将独占一段的 `[TOC]` 展开为文内目录,锚点与 rehype-slug 生成的 id 保持一致。\n */\nexport function remarkTocMarker() {\n return (tree: Root) => {\n const markers: number[] = []\n tree.children.forEach((child, index) => {\n if (child.type === 'paragraph' && TOC_MARKER.test(toString(child).trim())) {\n markers.push(index)\n }\n })\n if (markers.length === 0) return\n\n const slugger = new GithubSlugger()\n const headings: Heading[] = []\n visit(tree, 'heading', (heading) => {\n headings.push(heading)\n })\n\n const entries = headings\n .filter((heading) => heading.depth >= 2 && heading.depth <= 4)\n .map((heading) => tocEntry(slugger, heading))\n\n const title: Paragraph = {\n type: 'paragraph',\n data: { hName: 'span', hProperties: { className: ['md-doc-toc__title'] } },\n children: [{ type: 'text', value: '目录' }],\n }\n\n const nav: Blockquote = {\n type: 'blockquote',\n data: {\n hName: 'nav',\n hProperties: { className: ['md-doc-toc'], 'aria-label': '文档目录' },\n },\n children:\n entries.length > 0\n ? [title, { type: 'list', ordered: false, spread: false, children: entries }]\n : [title],\n }\n\n for (const index of markers.reverse()) {\n tree.children.splice(index, 1, nav)\n }\n }\n}\n","import { useEffect, useId, useRef, useState, type CSSProperties } from 'react'\nimport { createPortal } from 'react-dom'\nimport type { Element } from 'hast'\nimport { Maximize2, X } from 'lucide-react'\nimport ReactMarkdown, { type Components, defaultUrlTransform } from 'react-markdown'\nimport rehypeHighlight from 'rehype-highlight'\nimport rehypeKatex from 'rehype-katex'\nimport rehypeSlug from 'rehype-slug'\nimport remarkBreaks from 'remark-breaks'\nimport remarkGfm from 'remark-gfm'\nimport remarkMath from 'remark-math'\nimport 'katex/dist/katex.min.css'\nimport { flowToMermaid, sequenceToMermaid } from './legacyDiagrams'\nimport { remarkNoteMeta, remarkTocMarker } from './remarkPlugins'\n\ninterface MarkdownPreviewProps {\n source: string\n onError?: (error: Error) => void\n}\n\ninterface MermaidBlockProps {\n chart: string\n label?: string\n cache: Map<string, MermaidDocument>\n onError?: (error: Error) => void\n}\n\ninterface MermaidDocument {\n source: string\n aspectRatio: number\n naturalWidth: number\n}\n\nfunction createSandboxDocument(svg: string) {\n return `<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; img-src data: https:\">\n <style>\n html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; background: transparent; }\n body { box-sizing: border-box; display: grid; padding: 10px; place-items: center; }\n /* Mermaid 会写入内联 max-width,需要覆盖后图形才能填满容器 */\n svg { display: block; width: 100% !important; height: 100% !important; max-width: none !important; }\n </style>\n </head>\n <body>${svg}</body>\n</html>`\n}\n\n/** 用 viewBox 还原图表的原始尺寸,让内联展示尽量贴近 SVG 的自然大小。 */\nfunction getSvgMetrics(svg: string) {\n const fallback = { aspectRatio: 2, naturalWidth: 640 }\n const viewBox = /\\bviewBox=[\"']([^\"']+)[\"']/i.exec(svg)?.[1]\n if (!viewBox) return fallback\n const values = viewBox.trim().split(/[\\s,]+/).map(Number)\n const width = values[2]\n const height = values[3]\n if (!Number.isFinite(width) || !Number.isFinite(height) || height <= 0) {\n return fallback\n }\n return {\n aspectRatio: Math.min(Math.max(width / height, 0.2), 8),\n // 允许略微放大,但不做无限拉伸,避免细长图表糊成一片\n naturalWidth: Math.min(Math.max(width * 1.25, 200), 1200),\n }\n}\n\nfunction MermaidBlock({\n chart,\n label = 'Mermaid 图表',\n cache,\n onError,\n}: MermaidBlockProps) {\n const reactId = useId()\n const [diagram, setDiagram] = useState<MermaidDocument | null>(\n () => cache.get(chart) ?? null,\n )\n const [error, setError] = useState('')\n const [enlarged, setEnlarged] = useState(false)\n\n useEffect(() => {\n if (!enlarged) return\n const closeOnEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') setEnlarged(false)\n }\n window.addEventListener('keydown', closeOnEscape)\n return () => window.removeEventListener('keydown', closeOnEscape)\n }, [enlarged])\n\n useEffect(() => {\n let active = true\n const render = async () => {\n try {\n const { default: mermaid } = await import('mermaid')\n mermaid.initialize({\n startOnLoad: false,\n securityLevel: 'strict',\n theme: 'base',\n fontFamily: '\"Helvetica Neue\", Helvetica, \"PingFang SC\", sans-serif',\n themeVariables: {\n fontSize: '16px',\n primaryColor: '#ffffff',\n primaryTextColor: '#2f3d4a',\n primaryBorderColor: '#2f3d4a',\n secondaryColor: '#ffffff',\n tertiaryColor: '#ffffff',\n lineColor: '#2f3d4a',\n textColor: '#2f3d4a',\n noteBkgColor: '#ffffff',\n noteBorderColor: '#2f3d4a',\n noteTextColor: '#2f3d4a',\n actorBkg: '#ffffff',\n actorBorder: '#2f3d4a',\n actorTextColor: '#2f3d4a',\n actorLineColor: '#2f3d4a',\n signalColor: '#2f3d4a',\n signalTextColor: '#2f3d4a',\n },\n flowchart: {\n htmlLabels: false,\n useMaxWidth: true,\n curve: 'linear',\n padding: 14,\n nodeSpacing: 46,\n rankSpacing: 56,\n },\n sequence: {\n useMaxWidth: true,\n mirrorActors: true,\n actorMargin: 120,\n boxMargin: 14,\n noteMargin: 12,\n messageMargin: 42,\n },\n })\n const id = `mermaid-${reactId.replace(/[^a-zA-Z0-9]/g, '')}-${Date.now()}`\n const cached = cache.get(chart)\n if (cached) {\n if (!active) return\n setDiagram(cached)\n setError('')\n return\n }\n const result = await mermaid.render(id, chart)\n if (!active) return\n const nextDiagram = {\n source: createSandboxDocument(result.svg),\n ...getSvgMetrics(result.svg),\n }\n cache.set(chart, nextDiagram)\n if (cache.size > 32) {\n const oldest = cache.keys().next().value\n if (oldest) cache.delete(oldest)\n }\n setDiagram(nextDiagram)\n setError('')\n } catch (reason) {\n if (!active) return\n const nextError =\n reason instanceof Error ? reason : new Error('Mermaid 图表渲染失败')\n setError(nextError.message)\n setDiagram(null)\n onError?.(nextError)\n }\n }\n\n void render()\n return () => {\n active = false\n }\n }, [cache, chart, onError, reactId])\n\n if (error) {\n return (\n <div className=\"md-mermaid-error\" role=\"alert\">\n <strong>图表未能渲染</strong>\n <span>{error}</span>\n </div>\n )\n }\n\n if (!diagram) {\n return <div className=\"md-mermaid-loading\">正在绘制图表…</div>\n }\n\n return (\n <>\n <figure className=\"md-mermaid\" aria-label={label}>\n <div\n className=\"md-mermaid__stage\"\n style={\n {\n '--md-diagram-ratio': diagram.aspectRatio,\n '--md-diagram-width': `${diagram.naturalWidth}px`,\n } as CSSProperties\n }\n >\n <iframe\n title={label}\n sandbox=\"\"\n srcDoc={diagram.source}\n loading=\"lazy\"\n />\n <button\n type=\"button\"\n className=\"md-mermaid__enlarge\"\n aria-label=\"放大查看图表\"\n onClick={() => setEnlarged(true)}\n >\n <span className=\"md-mermaid__enlarge-chip\">\n <Maximize2 size={12} strokeWidth={2} />\n 放大\n </span>\n </button>\n </div>\n </figure>\n\n {enlarged &&\n createPortal(\n <div\n className=\"md-diagram-viewer\"\n role=\"presentation\"\n onMouseDown={() => setEnlarged(false)}\n >\n <section\n className=\"md-diagram-viewer__card\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={`${label}放大视图`}\n onMouseDown={(event) => event.stopPropagation()}\n >\n <header>\n <span>{label}</span>\n <button\n type=\"button\"\n aria-label=\"关闭放大视图\"\n onClick={() => setEnlarged(false)}\n >\n <X size={16} />\n </button>\n </header>\n <div\n className=\"md-diagram-viewer__stage\"\n style={\n { '--md-diagram-ratio': diagram.aspectRatio } as CSSProperties\n }\n >\n <iframe\n title={`${label}放大视图`}\n sandbox=\"\"\n srcDoc={diagram.source}\n />\n </div>\n <footer>点按空白处或按 Esc 关闭</footer>\n </section>\n </div>,\n document.body,\n )}\n </>\n )\n}\n\nconst sourceLine = (\n node:\n | { position?: { start: { line: number } | undefined } | undefined }\n | undefined,\n) => node?.position?.start?.line\n\nconst components: Components = {\n h1: ({ node, ...props }) => (\n <h1 data-source-line={sourceLine(node)} {...props} />\n ),\n h2: ({ node, ...props }) => (\n <h2 data-source-line={sourceLine(node)} {...props} />\n ),\n h3: ({ node, ...props }) => (\n <h3 data-source-line={sourceLine(node)} {...props} />\n ),\n h4: ({ node, ...props }) => (\n <h4 data-source-line={sourceLine(node)} {...props} />\n ),\n h5: ({ node, ...props }) => (\n <h5 data-source-line={sourceLine(node)} {...props} />\n ),\n h6: ({ node, ...props }) => (\n <h6 data-source-line={sourceLine(node)} {...props} />\n ),\n p: ({ node, ...props }) => (\n <p data-source-line={sourceLine(node)} {...props} />\n ),\n blockquote: ({ node, ...props }) => (\n <blockquote data-source-line={sourceLine(node)} {...props} />\n ),\n ul: ({ node, ...props }) => (\n <ul data-source-line={sourceLine(node)} {...props} />\n ),\n ol: ({ node, ...props }) => (\n <ol data-source-line={sourceLine(node)} {...props} />\n ),\n table: ({ node, ...props }) => (\n <table data-source-line={sourceLine(node)} {...props} />\n ),\n}\n\nfunction safeUrlTransform(url: string, key: string) {\n if (\n key === 'src' &&\n /^data:image\\/(?:png|jpeg|gif|webp);base64,/i.test(url)\n ) {\n return url\n }\n if (!/^[a-z][a-z0-9+.-]*:/i.test(url)) {\n return url\n }\n try {\n const parsed = new URL(url)\n if (['http:', 'https:', 'mailto:'].includes(parsed.protocol)) return url\n } catch {\n return undefined\n }\n return defaultUrlTransform(url)\n}\n\nconst diagramLanguages: Record<\n string,\n { label: string; toMermaid: (source: string) => string }\n> = {\n mermaid: { label: 'Mermaid 图表', toMermaid: (source) => source },\n flow: { label: '流程图', toMermaid: flowToMermaid },\n flowchart: { label: '流程图', toMermaid: flowToMermaid },\n sequence: { label: '时序图', toMermaid: sequenceToMermaid },\n}\n\nfunction fenceLanguage(className: unknown) {\n const names = Array.isArray(className) ? className : [className]\n for (const name of names) {\n const language = /^language-(\\w+)$/.exec(String(name ?? ''))?.[1]\n if (language) return language\n }\n return undefined\n}\n\n/** 图表围栏不能沿用代码块的深色 pre 外壳,需要提前从 hast 节点判断。 */\nfunction isDiagramFence(node: Element | undefined) {\n const code = node?.children.find(\n (child): child is Element => child.type === 'element' && child.tagName === 'code',\n )\n const language = fenceLanguage(code?.properties.className)\n return Boolean(language && diagramLanguages[language])\n}\n\nexport function MarkdownPreview({ source, onError }: MarkdownPreviewProps) {\n const diagramCache = useRef(new Map<string, MermaidDocument>())\n const previewComponents: Components = {\n ...components,\n code: ({ className, children, node: _node, ...props }) => {\n void _node\n const language = /language-(\\w+)/.exec(className ?? '')?.[1]\n const diagram = language ? diagramLanguages[language] : undefined\n if (diagram) {\n const raw = String(children).replace(/\\n$/, '')\n return (\n <MermaidBlock\n chart={diagram.toMermaid(raw)}\n label={diagram.label}\n cache={diagramCache.current}\n onError={onError}\n />\n )\n }\n return (\n <code className={className} {...props}>\n {children}\n </code>\n )\n },\n pre: ({ node, children, ...props }) => {\n if (isDiagramFence(node)) {\n return (\n <div data-source-line={sourceLine(node)} className=\"md-mermaid-block\">\n {children}\n </div>\n )\n }\n return (\n <pre data-source-line={sourceLine(node)} {...props}>\n {children}\n </pre>\n )\n },\n a: ({ children, node: _node, ...props }) => {\n void _node\n return (\n <a {...props} rel=\"noopener noreferrer\">\n {children}\n </a>\n )\n },\n }\n\n return (\n <article className=\"md-preview-document\" aria-label=\"Markdown 预览\">\n <ReactMarkdown\n remarkPlugins={[\n remarkGfm,\n remarkMath,\n remarkBreaks,\n remarkNoteMeta,\n remarkTocMarker,\n ]}\n rehypePlugins={[\n [rehypeKatex, { trust: false, maxExpand: 1000, maxSize: 20 }],\n [rehypeHighlight, { detect: false }],\n rehypeSlug,\n ]}\n remarkRehypeOptions={{\n footnoteLabel: '脚注',\n footnoteLabelProperties: { className: ['md-footnotes-title'] },\n footnoteBackLabel: '返回正文',\n }}\n components={previewComponents}\n urlTransform={safeUrlTransform}\n >\n {source}\n </ReactMarkdown>\n </article>\n )\n}\n","import {\n Bold,\n Braces,\n Code2,\n Columns2,\n Download,\n Eye,\n FileCode2,\n Heading1,\n Heading2,\n Image,\n Italic,\n Link,\n List,\n ListOrdered,\n Maximize2,\n Minus,\n Quote,\n Sigma,\n SquareCheck,\n Table2,\n} from 'lucide-react'\nimport type { EditorCommand, EditorMode } from './types'\n\ninterface ToolbarProps {\n mode: EditorMode\n readOnly: boolean\n onCommand: (command: EditorCommand) => void\n onModeChange: (mode: EditorMode) => void\n onDownload: () => void\n}\n\nconst commands: Array<{\n command: EditorCommand\n label: string\n icon: typeof Bold\n}> = [\n { command: 'heading1', label: '一级标题', icon: Heading1 },\n { command: 'heading2', label: '二级标题', icon: Heading2 },\n { command: 'bold', label: '加粗(Ctrl/Cmd+B)', icon: Bold },\n { command: 'italic', label: '斜体', icon: Italic },\n { command: 'link', label: '链接(Ctrl/Cmd+L)', icon: Link },\n { command: 'quote', label: '引用', icon: Quote },\n { command: 'inlineCode', label: '行内代码', icon: Code2 },\n { command: 'codeBlock', label: '代码块', icon: Braces },\n { command: 'bulletList', label: '无序列表', icon: List },\n { command: 'orderedList', label: '有序列表', icon: ListOrdered },\n { command: 'task', label: '任务列表', icon: SquareCheck },\n { command: 'table', label: '表格', icon: Table2 },\n { command: 'formula', label: '数学公式', icon: Sigma },\n { command: 'divider', label: '分隔线', icon: Minus },\n { command: 'image', label: '图片(Ctrl/Cmd+G)', icon: Image },\n]\n\nexport function Toolbar({\n mode,\n readOnly,\n onCommand,\n onModeChange,\n onDownload,\n}: ToolbarProps) {\n return (\n <div className=\"md-toolbar\" role=\"toolbar\" aria-label=\"Markdown 格式工具\">\n <div className=\"md-toolbar__brand\" aria-label=\"Markdown 编辑器\">\n <FileCode2 size={17} />\n <span>Markdown</span>\n </div>\n <div className=\"md-toolbar__scroll\">\n <div className=\"md-toolbar__group\">\n {commands.map(({ command, label, icon: Icon }) => (\n <button\n key={command}\n type=\"button\"\n className=\"md-icon-button\"\n aria-label={label}\n title={label}\n disabled={readOnly}\n onClick={() => onCommand(command)}\n >\n <Icon size={15} strokeWidth={1.8} />\n </button>\n ))}\n </div>\n </div>\n <div className=\"md-toolbar__group md-toolbar__modes\" aria-label=\"查看模式\">\n <button\n type=\"button\"\n className={`md-icon-button ${mode === 'edit' ? 'is-active' : ''}`}\n aria-label=\"最大化编辑器\"\n title=\"最大化编辑器(Ctrl/Cmd+Alt+Enter)\"\n onClick={() => onModeChange('edit')}\n >\n <Maximize2 size={15} />\n </button>\n <button\n type=\"button\"\n className={`md-icon-button ${mode === 'split' ? 'is-active' : ''}`}\n aria-label=\"分屏编辑\"\n title=\"分屏编辑\"\n onClick={() => onModeChange('split')}\n >\n <Columns2 size={15} />\n </button>\n <button\n type=\"button\"\n className={`md-icon-button ${mode === 'preview' ? 'is-active' : ''}`}\n aria-label=\"预览文档\"\n title=\"预览文档(Ctrl/Cmd+Enter)\"\n onClick={() => onModeChange('preview')}\n >\n <Eye size={15} />\n </button>\n <button\n type=\"button\"\n className=\"md-icon-button\"\n aria-label=\"下载 Markdown\"\n title=\"下载 Markdown\"\n onClick={onDownload}\n >\n <Download size={15} />\n </button>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useRef, type RefObject } from 'react'\nimport { EditorView } from '@codemirror/view'\n\ninterface Anchor {\n line: number\n top: number\n}\n\nfunction interpolate(items: Anchor[], value: number, valueKey: 'line' | 'top') {\n if (items.length === 0) return 0\n const outputKey = valueKey === 'line' ? 'top' : 'line'\n if (value <= items[0][valueKey]) return items[0][outputKey]\n\n for (let index = 1; index < items.length; index += 1) {\n const previous = items[index - 1]\n const next = items[index]\n if (value <= next[valueKey]) {\n const distance = next[valueKey] - previous[valueKey] || 1\n const ratio = (value - previous[valueKey]) / distance\n return previous[outputKey] + ratio * (next[outputKey] - previous[outputKey])\n }\n }\n\n return items.at(-1)?.[outputKey] ?? 0\n}\n\nexport function useScrollSync(\n editorView: EditorView | null,\n previewRef: RefObject<HTMLDivElement | null>,\n enabled: boolean,\n) {\n const lockRef = useRef<'editor' | 'preview' | null>(null)\n const unlockTimer = useRef<number | undefined>(undefined)\n const previewUserScroll = useRef(false)\n const previewUserTimer = useRef<number | undefined>(undefined)\n\n const setLock = useCallback((owner: 'editor' | 'preview') => {\n lockRef.current = owner\n window.clearTimeout(unlockTimer.current)\n unlockTimer.current = window.setTimeout(() => {\n lockRef.current = null\n }, 180)\n }, [])\n\n const markPreviewUserScroll = useCallback(() => {\n previewUserScroll.current = true\n window.clearTimeout(previewUserTimer.current)\n previewUserTimer.current = window.setTimeout(() => {\n previewUserScroll.current = false\n }, 180)\n }, [])\n\n const getAnchors = useCallback(() => {\n const preview = previewRef.current\n if (!preview) return []\n const previewRect = preview.getBoundingClientRect()\n return Array.from(\n preview.querySelectorAll<HTMLElement>('[data-source-line]'),\n )\n .map((element) => ({\n line: Number(element.dataset.sourceLine),\n top:\n element.getBoundingClientRect().top -\n previewRect.top +\n preview.scrollTop,\n }))\n .filter((anchor) => Number.isFinite(anchor.line))\n .sort((a, b) => a.line - b.line)\n }, [previewRef])\n\n useEffect(() => {\n const preview = previewRef.current\n if (!editorView || !preview || !enabled) return\n\n let frame = 0\n const syncFromEditor = () => {\n if (lockRef.current === 'preview') return\n cancelAnimationFrame(frame)\n frame = requestAnimationFrame(() => {\n const anchors = getAnchors()\n if (!anchors.length) return\n const lineBlock = editorView.lineBlockAtHeight(editorView.scrollDOM.scrollTop)\n const line = editorView.state.doc.lineAt(lineBlock.from).number\n setLock('editor')\n preview.scrollTop = interpolate(anchors, line, 'line')\n })\n }\n\n const syncFromPreview = () => {\n if (lockRef.current === 'editor') return\n // 预览重绘会改变高度并触发 scroll,这时不能回写编辑器,否则输入时会上下跳。\n if (!previewUserScroll.current) return\n cancelAnimationFrame(frame)\n frame = requestAnimationFrame(() => {\n const anchors = getAnchors()\n if (!anchors.length) return\n const line = Math.round(interpolate(anchors, preview.scrollTop, 'top'))\n const safeLine = Math.min(Math.max(line, 1), editorView.state.doc.lines)\n setLock('preview')\n const block = editorView.lineBlockAt(\n editorView.state.doc.line(safeLine).from,\n )\n editorView.scrollDOM.scrollTop = block.top\n })\n }\n\n const userScrollOptions = { passive: true } as const\n editorView.scrollDOM.addEventListener('scroll', syncFromEditor, {\n passive: true,\n })\n preview.addEventListener('scroll', syncFromPreview, { passive: true })\n preview.addEventListener('wheel', markPreviewUserScroll, userScrollOptions)\n preview.addEventListener('touchstart', markPreviewUserScroll, userScrollOptions)\n preview.addEventListener('pointerdown', markPreviewUserScroll)\n\n return () => {\n cancelAnimationFrame(frame)\n window.clearTimeout(unlockTimer.current)\n window.clearTimeout(previewUserTimer.current)\n editorView.scrollDOM.removeEventListener('scroll', syncFromEditor)\n preview.removeEventListener('scroll', syncFromPreview)\n preview.removeEventListener('wheel', markPreviewUserScroll)\n preview.removeEventListener('touchstart', markPreviewUserScroll)\n preview.removeEventListener('pointerdown', markPreviewUserScroll)\n }\n }, [editorView, enabled, getAnchors, markPreviewUserScroll, previewRef, setLock])\n\n return useCallback(\n (line: number) => {\n const preview = previewRef.current\n if (!preview) return\n const safeLine = editorView\n ? Math.min(Math.max(line, 1), editorView.state.doc.lines)\n : Math.max(line, 1)\n const anchor = getAnchors().find((item) => item.line >= safeLine)\n if (editorView?.dom.isConnected) {\n editorView.dispatch({\n selection: { anchor: editorView.state.doc.line(safeLine).from },\n effects: EditorView.scrollIntoView(\n editorView.state.doc.line(safeLine).from,\n { y: 'center' },\n ),\n })\n editorView.focus()\n }\n if (anchor) preview.scrollTo({ top: anchor.top, behavior: 'smooth' })\n },\n [editorView, getAnchors, previewRef],\n )\n}\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type PointerEvent as ReactPointerEvent,\n} from 'react'\nimport CodeMirror from '@uiw/react-codemirror'\nimport { defaultKeymap, historyKeymap, indentWithTab } from '@codemirror/commands'\nimport { markdown, markdownKeymap } from '@codemirror/lang-markdown'\nimport { HighlightStyle, syntaxHighlighting } from '@codemirror/language'\nimport { EditorView, keymap, type KeyBinding, type ViewUpdate } from '@codemirror/view'\nimport { tags } from '@lezer/highlight'\nimport GithubSlugger from 'github-slugger'\nimport {\n CircleHelp,\n Clipboard,\n Eye,\n GripVertical,\n ListTree,\n X,\n} from 'lucide-react'\nimport { Group, Panel, Separator } from 'react-resizable-panels'\nimport { runEditorCommand } from './editorCommands'\nimport { MarkdownPreview } from './MarkdownPreview'\nimport { Toolbar } from './Toolbar'\nimport type {\n EditorCommand,\n EditorMode,\n MarkdownEditorProps,\n TocItem,\n} from './types'\nimport { useScrollSync } from './useScrollSync'\nimport './styles.css'\n\nconst earthsongTheme = EditorView.theme(\n {\n '&': {\n height: '100%',\n backgroundColor: '#302c27',\n color: '#e9ddcc',\n fontSize: '15px',\n },\n '.cm-scroller': {\n fontFamily:\n '\"SFMono-Regular\", \"Cascadia Code\", \"Liberation Mono\", Menlo, monospace',\n lineHeight: '1.72',\n padding: '25px 20px 80px',\n },\n '.cm-content': { caretColor: '#ffc94a' },\n '.cm-cursor, .cm-dropCursor': { borderLeftColor: '#ffc94a' },\n '.cm-activeLine': { backgroundColor: 'rgba(15, 13, 11, .18)' },\n '.cm-selectionBackground, &.cm-focused .cm-selectionBackground': {\n backgroundColor: 'rgba(14, 12, 10, .5)',\n },\n '.cm-gutters': {\n backgroundColor: '#302c27',\n color: '#66635e',\n border: 'none',\n paddingRight: '7px',\n },\n '.cm-activeLineGutter': {\n backgroundColor: 'transparent',\n color: '#9b958b',\n },\n '.cm-focused': { outline: 'none' },\n },\n { dark: true },\n)\n\nconst lightTheme = EditorView.theme({\n '&': { height: '100%', backgroundColor: '#f9fafb', color: '#27303b' },\n '.cm-scroller': {\n fontFamily:\n '\"SFMono-Regular\", \"Cascadia Code\", \"Liberation Mono\", Menlo, monospace',\n lineHeight: '1.72',\n padding: '25px 20px 80px',\n },\n '.cm-gutters': {\n backgroundColor: '#f9fafb',\n color: '#a0a6ad',\n border: 'none',\n },\n '.cm-activeLine': { backgroundColor: 'rgba(29, 78, 216, .035)' },\n '.cm-focused': { outline: 'none' },\n})\n\nconst earthsongHighlight = HighlightStyle.define([\n { tag: tags.heading, color: '#ffc643', fontWeight: '700' },\n { tag: tags.strong, color: '#e38352', fontWeight: '700' },\n { tag: tags.emphasis, color: '#e8d9c5', fontStyle: 'italic' },\n { tag: tags.link, color: '#d7bd58', textDecoration: 'underline' },\n { tag: tags.url, color: '#718394' },\n { tag: tags.monospace, color: '#98bd62' },\n { tag: tags.quote, color: '#d47b53', fontStyle: 'normal' },\n { tag: tags.list, color: '#7b8793' },\n { tag: tags.processingInstruction, color: '#606a74' },\n { tag: tags.punctuation, color: '#6c747c' },\n { tag: tags.contentSeparator, color: '#77716a' },\n { tag: tags.comment, color: '#78846f', fontStyle: 'italic' },\n])\n\nconst lightHighlight = HighlightStyle.define([\n { tag: tags.heading, color: '#9a5b22', fontWeight: '700' },\n { tag: tags.strong, color: '#1f2937', fontWeight: '700' },\n { tag: tags.emphasis, color: '#8a4b77', fontStyle: 'italic' },\n { tag: tags.link, color: '#2563a8', textDecoration: 'underline' },\n { tag: tags.url, color: '#397d89' },\n { tag: tags.monospace, color: '#8d3f64' },\n { tag: tags.quote, color: '#6b7280', fontStyle: 'italic' },\n { tag: tags.list, color: '#b45309' },\n])\n\nconst defaultShortcuts = {\n bold: 'Mod-b',\n link: 'Mod-l',\n image: 'Mod-g',\n preview: 'Mod-Enter',\n maximize: 'Mod-Alt-Enter',\n help: 'Mod-/',\n}\n\nconst basicSetupOptions = {\n lineNumbers: true,\n highlightActiveLine: true,\n highlightActiveLineGutter: true,\n foldGutter: false,\n autocompletion: false,\n}\n\nfunction extractToc(source: string): TocItem[] {\n const slugger = new GithubSlugger()\n const items: TocItem[] = []\n let inFence = false\n source.split('\\n').forEach((line, index) => {\n if (/^\\s*```/.test(line)) {\n inFence = !inFence\n return\n }\n if (inFence) return\n const match = /^(#{1,6})\\s+(.+?)\\s*#*$/.exec(line)\n if (!match) return\n const text = match[2].replace(/[*_`[\\]]/g, '').trim()\n items.push({\n depth: match[1].length,\n text,\n slug: slugger.slug(text),\n line: index + 1,\n })\n })\n return items\n}\n\nfunction countWords(source: string) {\n const chinese = source.match(/[\\u3400-\\u9fff]/g)?.length ?? 0\n const latin = source\n .replace(/[\\u3400-\\u9fff]/g, ' ')\n .match(/[A-Za-z0-9]+(?:['’-][A-Za-z0-9]+)*/g)?.length ?? 0\n return chinese + latin\n}\n\nexport function MarkdownEditor({\n value,\n defaultValue = '',\n onChange,\n mode: modeProp,\n onModeChange,\n readOnly = false,\n height = '100vh',\n theme = 'earthsong',\n className = '',\n ariaLabel = 'Markdown 源码编辑器',\n shortcuts,\n onImageUpload,\n onError,\n renderStatus,\n}: MarkdownEditorProps) {\n const controlled = value !== undefined\n const [internalValue, setInternalValue] = useState(defaultValue)\n const source = controlled ? value : internalValue\n const [previewSource, setPreviewSource] = useState(source)\n const [internalMode, setInternalMode] = useState<EditorMode>(\n modeProp ?? 'split',\n )\n const mode = modeProp ?? internalMode\n const [editorView, setEditorView] = useState<EditorView | null>(null)\n const editorViewRef = useRef<EditorView | null>(null)\n const previewRef = useRef<HTMLDivElement>(null)\n const [cursor, setCursor] = useState({ line: 1, column: 1 })\n const [tocOpen, setTocOpen] = useState(false)\n const [helpOpen, setHelpOpen] = useState(false)\n const [copied, setCopied] = useState(false)\n const [toolOffset, setToolOffset] = useState({ x: 0, y: 0 })\n const dragOrigin = useRef({ x: 0, y: 0, offsetX: 0, offsetY: 0 })\n const onChangeRef = useRef(onChange)\n const onModeChangeRef = useRef(onModeChange)\n const onImageUploadRef = useRef(onImageUpload)\n const onErrorRef = useRef(onError)\n const modeRef = useRef(mode)\n\n useEffect(() => {\n onChangeRef.current = onChange\n onModeChangeRef.current = onModeChange\n onImageUploadRef.current = onImageUpload\n onErrorRef.current = onError\n modeRef.current = mode\n }, [mode, onChange, onError, onImageUpload, onModeChange])\n\n useEffect(() => {\n const timer = window.setTimeout(() => setPreviewSource(source), 220)\n return () => window.clearTimeout(timer)\n }, [source])\n\n const updateMode = useCallback((nextMode: EditorMode) => {\n if (modeProp === undefined) setInternalMode(nextMode)\n onModeChangeRef.current?.(nextMode)\n }, [modeProp])\n\n const commitChange = useCallback(\n (nextValue: string) => {\n if (!controlled) setInternalValue(nextValue)\n onChangeRef.current?.(nextValue)\n },\n [controlled],\n )\n\n const uploadImage = useCallback(async (file: File, view: EditorView) => {\n if (!onImageUploadRef.current) return\n const id =\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `${Date.now()}-${Math.random()}`\n const token = ``\n const position = view.state.selection.main.from\n view.dispatch({ changes: { from: position, insert: token } })\n const controller = new AbortController()\n\n try {\n const result = await onImageUploadRef.current(file, controller.signal)\n const normalized =\n typeof result === 'string' ? { url: result, alt: file.name } : result\n const current = view.state.doc.toString()\n const tokenPosition = current.indexOf(token)\n if (tokenPosition < 0) return\n view.dispatch({\n changes: {\n from: tokenPosition,\n to: tokenPosition + token.length,\n insert: ``,\n },\n })\n } catch (reason) {\n const error =\n reason instanceof Error ? reason : new Error('图片上传失败')\n onErrorRef.current?.(error)\n const current = view.state.doc.toString()\n const tokenPosition = current.indexOf(token)\n if (tokenPosition >= 0) {\n view.dispatch({\n changes: {\n from: tokenPosition,\n to: tokenPosition + token.length,\n insert: `<!-- ${file.name} 上传失败,请重试 -->`,\n },\n })\n }\n }\n }, [])\n\n const shortcutKeys = useMemo(\n () => ({ ...defaultShortcuts, ...shortcuts }),\n [shortcuts],\n )\n // 扩展只依赖稳定配置。回调通过 ref 读取最新值,避免每次输入都触发 CodeMirror 重配置。\n const extensions = useMemo(() => {\n const run = (command: EditorCommand) => (view: EditorView) =>\n readOnly ? false : runEditorCommand(view, command)\n const customKeymap: KeyBinding[] = [\n { key: shortcutKeys.bold, run: run('bold') },\n { key: shortcutKeys.link, run: run('link') },\n { key: shortcutKeys.image, run: run('image') },\n {\n key: shortcutKeys.preview,\n run: () => {\n updateMode(modeRef.current === 'preview' ? 'split' : 'preview')\n return true\n },\n },\n {\n key: shortcutKeys.maximize,\n run: () => {\n updateMode(modeRef.current === 'edit' ? 'split' : 'edit')\n return true\n },\n },\n {\n key: shortcutKeys.help,\n run: () => {\n setHelpOpen((open) => !open)\n return true\n },\n },\n ...([1, 2, 3, 4, 5] as const).map((level) => ({\n key: `Mod-${level}`,\n run: run(`heading${level}` as EditorCommand),\n })),\n ]\n\n return [\n markdown(),\n // keymap / 粘贴处理只在用户操作时读 ref,不会在渲染期求值。\n // eslint-disable-next-line react-hooks/refs -- CodeMirror 工厂不会在渲染时调用这些回调\n keymap.of([\n ...customKeymap,\n ...markdownKeymap,\n indentWithTab,\n ...defaultKeymap,\n ...historyKeymap,\n ]),\n syntaxHighlighting(\n theme === 'earthsong' ? earthsongHighlight : lightHighlight,\n ),\n EditorView.lineWrapping,\n // eslint-disable-next-line react-hooks/refs -- 粘贴与拖放回调只在事件触发时读取 ref\n EditorView.domEventHandlers({\n paste: (event, view) => {\n if (!onImageUploadRef.current) return false\n const image = Array.from(event.clipboardData?.files ?? []).find((file) =>\n file.type.startsWith('image/'),\n )\n if (!image) return false\n event.preventDefault()\n void uploadImage(image, view)\n return true\n },\n drop: (event, view) => {\n if (!onImageUploadRef.current) return false\n const image = Array.from(event.dataTransfer?.files ?? []).find((file) =>\n file.type.startsWith('image/'),\n )\n if (!image) return false\n event.preventDefault()\n const position = view.posAtCoords({\n x: event.clientX,\n y: event.clientY,\n })\n if (position !== null) {\n view.dispatch({ selection: { anchor: position } })\n }\n void uploadImage(image, view)\n return true\n },\n }),\n ]\n }, [readOnly, shortcutKeys, theme, updateMode, uploadImage])\n\n const handleEditorUpdate = useCallback((update: ViewUpdate) => {\n if (!update.selectionSet && !update.docChanged) return\n const position = update.state.selection.main.head\n const line = update.state.doc.lineAt(position)\n setCursor({ line: line.number, column: position - line.from + 1 })\n }, [])\n\n const handleCreateEditor = useCallback((view: EditorView) => {\n editorViewRef.current = view\n setEditorView(view)\n }, [setEditorView])\n\n const scrollToLine = useScrollSync(editorView, previewRef, mode === 'split')\n const toc = useMemo(() => extractToc(previewSource), [previewSource])\n const stats = useMemo(\n () => ({\n words: countWords(source),\n characters: source.length,\n line: cursor.line,\n column: cursor.column,\n }),\n [cursor, source],\n )\n\n const downloadMarkdown = useCallback(() => {\n const title = toc[0]?.text.replace(/[^\\p{L}\\p{N}\\-_]+/gu, '-') || 'document'\n const blob = new Blob([source], { type: 'text/markdown;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = `${title}.md`\n anchor.click()\n URL.revokeObjectURL(url)\n }, [source, toc])\n\n const copyHtml = useCallback(async () => {\n const html =\n previewRef.current?.querySelector('.md-preview-document')?.innerHTML ?? ''\n try {\n await navigator.clipboard.writeText(html)\n setCopied(true)\n window.setTimeout(() => setCopied(false), 1400)\n } catch (reason) {\n onError?.(reason instanceof Error ? reason : new Error('复制 HTML 失败'))\n }\n }, [onError, setCopied])\n\n const editorPane = (\n <div className=\"md-editor-pane\">\n <CodeMirror\n value={source}\n onChange={commitChange}\n onUpdate={handleEditorUpdate}\n onCreateEditor={handleCreateEditor}\n extensions={extensions}\n theme={theme === 'earthsong' ? earthsongTheme : lightTheme}\n editable={!readOnly}\n basicSetup={basicSetupOptions}\n aria-label={ariaLabel}\n height=\"100%\"\n />\n </div>\n )\n\n const previewPane = (\n <div className=\"md-preview-pane\" ref={previewRef}>\n <MarkdownPreview source={previewSource} onError={onError} />\n </div>\n )\n\n const handleDragStart = (event: ReactPointerEvent<HTMLButtonElement>) => {\n dragOrigin.current = {\n x: event.clientX,\n y: event.clientY,\n offsetX: toolOffset.x,\n offsetY: toolOffset.y,\n }\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n\n const handleDrag = (event: ReactPointerEvent<HTMLButtonElement>) => {\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return\n setToolOffset({\n x: dragOrigin.current.offsetX + event.clientX - dragOrigin.current.x,\n y: dragOrigin.current.offsetY + event.clientY - dragOrigin.current.y,\n })\n }\n\n const containerStyle = {\n '--md-editor-height':\n typeof height === 'number' ? `${height}px` : height,\n } as CSSProperties\n\n return (\n <section\n className={`markdown-editor markdown-editor--${theme} ${className}`}\n style={containerStyle}\n data-mode={mode}\n >\n <Toolbar\n mode={mode}\n readOnly={readOnly}\n onCommand={(command) => runEditorCommand(editorViewRef.current, command)}\n onModeChange={updateMode}\n onDownload={downloadMarkdown}\n />\n\n <div className=\"md-workspace\">\n {mode === 'split' ? (\n <Group orientation=\"horizontal\" className=\"md-panel-group\">\n <Panel\n id=\"editor\"\n className=\"md-editor-panel\"\n defaultSize=\"50%\"\n minSize=\"28%\"\n >\n {editorPane}\n </Panel>\n <Separator className=\"md-resize-handle\" />\n <Panel\n id=\"preview\"\n className=\"md-preview-panel\"\n defaultSize=\"50%\"\n minSize=\"28%\"\n >\n {previewPane}\n </Panel>\n </Group>\n ) : mode === 'edit' ? (\n editorPane\n ) : (\n previewPane\n )}\n </div>\n\n <div\n className=\"md-mini-tools\"\n style={{ transform: `translate(${toolOffset.x}px, ${toolOffset.y}px)` }}\n >\n {tocOpen && (\n <nav className=\"md-popover md-toc\" aria-label=\"文档目录\">\n <div className=\"md-popover__title\">\n <span>文档目录</span>\n <button\n type=\"button\"\n aria-label=\"关闭目录\"\n onClick={() => setTocOpen(false)}\n >\n <X size={14} />\n </button>\n </div>\n {toc.length ? (\n toc.map((item) => (\n <button\n type=\"button\"\n key={`${item.slug}-${item.line}`}\n style={{ paddingLeft: `${12 + (item.depth - 1) * 12}px` }}\n onClick={() => {\n scrollToLine(item.line)\n setTocOpen(false)\n }}\n >\n {item.text}\n </button>\n ))\n ) : (\n <p>添加标题后会在这里生成目录。</p>\n )}\n </nav>\n )}\n <button\n type=\"button\"\n className=\"md-mini-tools__drag\"\n aria-label=\"拖动迷你工具条\"\n title=\"拖动工具条\"\n onPointerDown={handleDragStart}\n onPointerMove={handleDrag}\n >\n <GripVertical size={13} />\n </button>\n <button\n type=\"button\"\n aria-label=\"打开目录\"\n title=\"文档目录\"\n onClick={() => setTocOpen((open) => !open)}\n >\n <ListTree size={14} />\n </button>\n <span className=\"md-word-count\" title={`${stats.characters} 个字符`}>\n {stats.words} 字\n </span>\n <button\n type=\"button\"\n aria-label=\"预览文档\"\n title=\"切换预览\"\n onClick={() => updateMode(mode === 'preview' ? 'split' : 'preview')}\n >\n <Eye size={14} />\n </button>\n <button\n type=\"button\"\n aria-label=\"复制 HTML\"\n title={copied ? '已复制' : '复制 HTML'}\n onClick={() => void copyHtml()}\n >\n <Clipboard size={14} />\n </button>\n <button\n type=\"button\"\n aria-label=\"语法帮助\"\n title=\"语法帮助(Ctrl/Cmd+/)\"\n onClick={() => setHelpOpen(true)}\n >\n <CircleHelp size={14} />\n </button>\n </div>\n\n <div className=\"md-status\" aria-live=\"polite\">\n {renderStatus?.(stats) ?? (\n <>\n <span>Ln {stats.line}, Col {stats.column}</span>\n <span>{stats.characters} 字符</span>\n {copied && <span>HTML 已复制</span>}\n </>\n )}\n </div>\n\n {helpOpen && (\n <div\n className=\"md-dialog-backdrop\"\n role=\"presentation\"\n onMouseDown={() => setHelpOpen(false)}\n >\n <section\n className=\"md-help-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"md-help-title\"\n onMouseDown={(event) => event.stopPropagation()}\n >\n <header>\n <div>\n <span className=\"md-help-dialog__eyebrow\">QUICK REFERENCE</span>\n <h2 id=\"md-help-title\">Markdown 语法帮助</h2>\n </div>\n <button\n type=\"button\"\n aria-label=\"关闭帮助\"\n onClick={() => setHelpOpen(false)}\n >\n <X size={18} />\n </button>\n </header>\n <div className=\"md-help-grid\">\n <dl>\n <dt>标题</dt>\n <dd><code># 一级标题</code></dd>\n <dt>强调</dt>\n <dd><code>**加粗** · *斜体*</code></dd>\n <dt>链接</dt>\n <dd><code>[文字](https://)</code></dd>\n <dt>图片</dt>\n <dd><code></code></dd>\n <dt>引用</dt>\n <dd><code>> 引用内容</code></dd>\n <dt>分隔线</dt>\n <dd><code>---</code></dd>\n </dl>\n <dl>\n <dt>任务</dt>\n <dd><code>- [ ] 待办事项</code></dd>\n <dt>公式</dt>\n <dd><code>$E = mc^2$</code></dd>\n <dt>图表</dt>\n <dd><code>```mermaid ```flow ```sequence</code></dd>\n <dt>目录</dt>\n <dd><code>[TOC]</code></dd>\n <dt>脚注</dt>\n <dd><code>正文[^1] 与 [^1]: 注释</code></dd>\n <dt>笔记本</dt>\n <dd><code>@(笔记本)[标签A|标签B]</code></dd>\n </dl>\n </div>\n </section>\n </div>\n )}\n </section>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Root } from 'mdast';
|
|
2
|
+
/**
|
|
3
|
+
* 马克飞象扩展语法:`@(笔记本)[标签A|标签B]` 用于指定笔记本与标签。
|
|
4
|
+
*/
|
|
5
|
+
export declare function remarkNoteMeta(): (tree: Root) => void;
|
|
6
|
+
/**
|
|
7
|
+
* 将独占一段的 `[TOC]` 展开为文内目录,锚点与 rehype-slug 生成的 id 保持一致。
|
|
8
|
+
*/
|
|
9
|
+
export declare function remarkTocMarker(): (tree: Root) => void;
|
package/dist/style.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.markdown-editor{--md-toolbar-height:39px;--md-status-height:23px;--md-border:#d7d5cf;--md-toolbar-bg:#f3f2ef;--md-toolbar-text:#5d5a55;--md-preview-text:#33373b;--md-link:#2e6f9e;width:100%;height:var(--md-editor-height);color:#343434;border:1px solid var(--md-border);background:#fff;flex-direction:column;min-height:420px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif;display:flex;position:relative;overflow:hidden}.md-toolbar{z-index:8;height:var(--md-toolbar-height);flex:0 0 var(--md-toolbar-height);color:var(--md-toolbar-text);background:linear-gradient(#ffffff94, #fff0), var(--md-toolbar-bg);border-bottom:1px solid var(--md-border);align-items:center;gap:7px;display:flex;position:relative;box-shadow:0 1px 2px #2c271f0a}.md-toolbar__brand{color:#4b4842;letter-spacing:.03em;border-right:1px solid #dedcd7;flex:none;align-items:center;gap:6px;height:100%;padding:0 13px;font-family:Georgia,Times New Roman,serif;font-size:13px;font-weight:700;display:flex}.md-toolbar__scroll{scrollbar-width:none;flex:1;min-width:0;overflow-x:auto}.md-toolbar__scroll::-webkit-scrollbar{display:none}.md-toolbar__group{white-space:nowrap;align-items:center;gap:1px;display:flex}.md-toolbar__modes{border-left:1px solid #dedcd7;flex:none;padding:0 7px 0 9px}.md-icon-button{color:#66615a;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:2px;place-items:center;width:28px;height:27px;padding:0;display:inline-grid}.md-icon-button:hover:not(:disabled),.md-icon-button.is-active{color:#332f29;background:#fff;border-color:#d5d1ca;box-shadow:0 1px 1px #211d160f}.md-icon-button:focus-visible,.md-mini-tools button:focus-visible,.md-popover button:focus-visible,.md-help-dialog button:focus-visible{outline-offset:1px;outline:2px solid #9e6f36}.md-icon-button:disabled{cursor:not-allowed;opacity:.35}.md-workspace{min-height:0;padding-bottom:var(--md-status-height);flex:1}.md-panel-group,.md-editor-pane,.md-preview-pane,.cm-theme{height:100%}.md-editor-pane{contain:layout paint;background:#302c27;min-width:0;overflow:hidden}.markdown-editor--light .md-editor-pane{background:#f9fafb}.md-editor-pane .cm-editor{height:100%}.md-editor-pane .cm-scroller{overflow-anchor:none;overscroll-behavior:contain}.md-resize-handle{z-index:4;cursor:col-resize;background:#dad8d3;border-inline:1px solid #fffc;width:5px;transition:background-color .12s;position:relative}.md-resize-handle:after{content:"";background:#a8a59e;width:1px;height:32px;position:absolute;top:50%;left:1px;transform:translateY(-50%);box-shadow:2px 0 #f5f4f1}.md-resize-handle:hover,.md-resize-handle[data-resize-handle-active]{background:#bdb9b1}.md-preview-pane{contain:layout paint;overflow-anchor:none;overscroll-behavior:contain;scroll-behavior:auto;background:#fff;min-width:0;overflow:auto}.md-preview-document{box-sizing:border-box;max-width:840px;min-height:100%;color:var(--md-preview-text);text-align:left;overflow-wrap:anywhere;margin:0 auto;padding:42px 54px 110px;font-family:Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif;font-size:16px;line-height:1.78}.md-preview-document>:first-child{margin-top:0}.md-preview-document h1,.md-preview-document h2,.md-preview-document h3,.md-preview-document h4,.md-preview-document h5,.md-preview-document h6{color:#2b2f33;scroll-margin-top:24px;font-weight:600;line-height:1.28}.md-preview-document h1{border-bottom:1px solid #ddd;margin:0 0 1.1em;padding-bottom:.38em;font-size:2em}.md-preview-document h2{border-bottom:1px solid #ececec;margin:1.65em 0 .7em;padding-bottom:.3em;font-size:1.55em}.md-preview-document h3{margin:1.45em 0 .6em;font-size:1.25em}.md-preview-document h4,.md-preview-document h5,.md-preview-document h6{margin:1.3em 0 .5em}.md-preview-document p,.md-preview-document ul,.md-preview-document ol,.md-preview-document blockquote,.md-preview-document pre,.md-preview-document table{margin:0 0 1.15em}.md-preview-document a{color:var(--md-link);border-bottom:1px solid #2e6f9e40;text-decoration:none}.md-preview-document a:hover{border-bottom-color:currentColor}.md-preview-document blockquote{color:#6c747b;background:#fafafa;border-left:4px solid #d8d8d8;padding:.15em 1em}.md-preview-document blockquote>:last-child{margin-bottom:0}.md-preview-document code{color:#704b33;background:#f1eee9;border-radius:2px;padding:.14em .38em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:.88em}.md-preview-document pre{--md-code-text:#d5d8de;--md-code-muted:#7c828d;--md-code-keyword:#e668a7;--md-code-callable:#4ea9d8;--md-code-type:#dcc06a;--md-code-string:#a8c76f;--md-code-number:#a882e0;color:var(--md-code-text);-webkit-print-color-adjust:exact;print-color-adjust:exact;background:#22252a;border:0;border-radius:6px;padding:19px 22px;line-height:1.68;overflow:auto}.md-preview-document pre code{color:inherit;background:0 0;padding:0;font-size:.9em}.md-preview-document pre .hljs-comment,.md-preview-document pre .hljs-quote,.md-preview-document pre .hljs-meta{color:var(--md-code-muted)}.md-preview-document pre .hljs-keyword,.md-preview-document pre .hljs-literal,.md-preview-document pre .hljs-selector-tag,.md-preview-document pre .hljs-tag,.md-preview-document pre .hljs-doctag,.md-preview-document pre .hljs-deletion{color:var(--md-code-keyword)}.md-preview-document pre .hljs-title,.md-preview-document pre .hljs-title.function_,.md-preview-document pre .hljs-built_in,.md-preview-document pre .hljs-name,.md-preview-document pre .hljs-section{color:var(--md-code-callable)}.md-preview-document pre .hljs-title.class_,.md-preview-document pre .hljs-type,.md-preview-document pre .hljs-attr,.md-preview-document pre .hljs-attribute,.md-preview-document pre .hljs-property{color:var(--md-code-type)}.md-preview-document pre .hljs-title.class_,.md-preview-document pre .hljs-emphasis{font-style:italic}.md-preview-document pre .hljs-string,.md-preview-document pre .hljs-regexp,.md-preview-document pre .hljs-addition,.md-preview-document pre .hljs-symbol,.md-preview-document pre .hljs-bullet,.md-preview-document pre .hljs-link{color:var(--md-code-string)}.md-preview-document pre .hljs-number,.md-preview-document pre .hljs-variable.constant_,.md-preview-document pre .hljs-template-variable{color:var(--md-code-number)}.md-preview-document pre .hljs-strong{font-weight:700}.md-preview-document table{border-spacing:0;border-collapse:collapse;width:max-content;max-width:100%;display:block;overflow-x:auto}.md-preview-document th,.md-preview-document td{border:1px solid #d8d8d8;padding:7px 13px}.md-preview-document th{background:#f7f7f7;font-weight:600}.md-preview-document tr:nth-child(2n) td{background:#fbfbfb}.md-preview-document img{max-width:100%;height:auto;margin:1.5em auto;display:block}.md-preview-document hr{background:#ddd;border:0;height:1px;margin:2em 0}.md-preview-document input[type=checkbox]{accent-color:#56764b;margin-right:.45em}.md-note-meta{vertical-align:middle;flex-wrap:wrap;align-items:center;gap:6px;font-style:normal;display:inline-flex}.md-note-meta__book,.md-note-meta__tag{white-space:nowrap;border-radius:999px;padding:1px 8px;font-size:12px;font-style:normal;line-height:1.7}.md-note-meta__book{color:#4a5b41;background:#e8efe2;border:1px solid #cddcc2}.md-note-meta__tag{color:#6a6a6a;background:#f2f1ee;border:1px solid #e0ded8}.md-doc-toc{color:inherit;background:#fbfbfa;border:1px solid #e7e5e1;border-radius:6px;margin:1.6em 0;padding:14px 18px}.md-doc-toc__title{letter-spacing:.04em;color:#8a8578;margin-bottom:6px;font-size:13px;font-style:normal;font-weight:600;display:block}.md-doc-toc ul{margin:0;padding:0;list-style:none}.md-doc-toc li{margin:2px 0;font-size:14px;line-height:1.8}.md-doc-toc li:before{content:none}.md-doc-toc a{color:#4a4a4a;border-bottom:0;text-decoration:none}.md-doc-toc a:hover{color:#56764b;text-decoration:underline}.md-doc-toc__item--3{padding-left:1.2em}.md-doc-toc__item--4{padding-left:2.4em}.md-preview-document .footnotes{color:#6a6a6a;border-top:1px solid #e5e3de;margin-top:2.4em;padding-top:1em;font-size:14px}.md-preview-document .footnotes h2{color:#8a8578;border:0;margin:0 0 .6em;padding:0;font-size:14px;font-weight:600}.md-preview-document .footnotes ol{margin:0;padding-left:1.6em}.md-preview-document [data-footnote-ref]{padding:0 2px;font-size:12px;text-decoration:none}.md-preview-document [data-footnote-backref]{margin-left:4px;text-decoration:none}.md-mermaid{text-align:center;margin:1.6em 0;padding:0;overflow-x:auto}.md-mermaid-loading,.md-mermaid-error{text-align:center;background:#fbfbfa;border:1px solid #e7e5e1;border-radius:6px;margin:1.3em 0;padding:8px}.md-mermaid__stage{width:min(100%, var(--md-diagram-width,640px), calc(520px * var(--md-diagram-ratio)));aspect-ratio:var(--md-diagram-ratio);margin:0 auto;position:relative}.md-mermaid iframe{background:0 0;border:0;width:100%;height:100%;display:block}.md-mermaid__enlarge{cursor:zoom-in;background:0 0;border:0;border-radius:4px;padding:0;position:absolute;inset:0}.md-mermaid__enlarge-chip{color:#5f5b55;opacity:0;background:#ffffffed;border:1px solid #dcd9d3;border-radius:3px;align-items:center;gap:4px;padding:3px 7px;font-size:11px;transition:opacity .12s;display:inline-flex;position:absolute;bottom:6px;right:6px}.md-mermaid__stage:hover .md-mermaid__enlarge-chip,.md-mermaid__enlarge:focus-visible .md-mermaid__enlarge-chip{opacity:1}.md-mermaid__enlarge:focus-visible{outline-offset:2px;outline:2px solid #9e6f36}.md-diagram-viewer{z-index:60;background:#1c191594;place-items:center;padding:28px;display:grid;position:fixed;inset:0}.md-diagram-viewer__card{color:#37342f;background:#fff;border:1px solid #c8c3b9;border-radius:6px;flex-direction:column;max-width:100%;max-height:100%;display:flex;box-shadow:0 20px 60px #14110c52}.md-diagram-viewer__card header{border-bottom:1px solid #eae7e1;justify-content:space-between;align-items:center;gap:20px;padding:9px 10px 9px 14px;font-size:12px;font-weight:600;display:flex}.md-diagram-viewer__card header button{color:#6e6961;cursor:pointer;background:0 0;border:0;border-radius:3px;place-items:center;padding:4px;display:grid}.md-diagram-viewer__card header button:hover{color:#2b2823;background:#f2f0ec}.md-diagram-viewer__card header button:focus-visible{outline-offset:1px;outline:2px solid #9e6f36}.md-diagram-viewer__stage{width:min(88vw, calc((100vh - 200px) * var(--md-diagram-ratio)));aspect-ratio:var(--md-diagram-ratio)}.md-diagram-viewer__stage iframe{background:0 0;border:0;width:100%;height:100%;display:block}.md-diagram-viewer__card footer{color:#8d8880;border-top:1px solid #eae7e1;padding:8px 14px 10px;font-size:11px}.md-mermaid-loading{color:#8b8983;font-size:13px}.md-mermaid-error{color:#8c3d35;text-align:left;background:#fff7f5;border-color:#e7c4bd;gap:4px;font-size:13px;display:grid}.md-mini-tools{z-index:12;color:#6a665f;background:#f7f6f3f5;border:1px solid #cfcac1;border-radius:2px;align-items:center;height:29px;display:flex;position:absolute;bottom:34px;right:18px;box-shadow:0 2px 8px #2c261d29}.md-mini-tools>button{width:28px;height:100%;color:inherit;cursor:pointer;background:0 0;border:0;border-left:1px solid #dedbd5;place-items:center;padding:0;display:grid}.md-mini-tools>button:hover{color:#27241f;background:#fff}.md-mini-tools>.md-mini-tools__drag{color:#9c978f;cursor:move;touch-action:none;border-left:0;width:20px}.md-word-count{text-align:center;white-space:nowrap;border-left:1px solid #dedbd5;min-width:48px;padding:0 7px;font-size:11px;line-height:28px}.md-popover{color:#4a4742;background:#fff;border:1px solid #cac6be;width:min(310px,100vw - 36px);max-height:min(430px,100vh - 120px);position:absolute;bottom:36px;right:0;overflow:auto;box-shadow:0 8px 26px #27221a2e}.md-popover__title{background:#f4f2ee;border-bottom:1px solid #ddd9d1;justify-content:space-between;align-items:center;padding:9px 11px;font-size:12px;font-weight:700;display:flex;position:sticky;top:0}.md-popover__title button{color:inherit;cursor:pointer;background:0 0;border:0;place-items:center;padding:3px;display:grid}.md-toc>button{color:#4f4b44;width:100%;font:inherit;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;background:#fff;border:0;border-bottom:1px solid #efede9;padding-block:7px;font-size:12px;display:block;overflow:hidden}.md-toc>button:hover{color:#8b552a;background:#faf8f5}.md-toc>p{color:#8a867f;margin:0;padding:18px;font-size:12px}.md-status{z-index:7;height:var(--md-status-height);color:#89857e;letter-spacing:.02em;background:#f3f2ef;border-top:1px solid #d8d5cf;justify-content:flex-end;align-items:center;gap:14px;padding:0 12px;font-size:10px;display:flex;position:absolute;bottom:0;left:0;right:0}.md-dialog-backdrop{z-index:30;inset:var(--md-toolbar-height) 0 var(--md-status-height);background:#1f1c1880;place-items:center;padding:24px;display:grid;position:absolute}.md-help-dialog{color:#37342f;background:#fff;border:1px solid #c8c3b9;width:min(620px,100%);max-height:90%;overflow:auto;box-shadow:0 18px 60px #14110c47}.md-help-dialog header{border-bottom:1px solid #e2dfd9;justify-content:space-between;align-items:flex-start;padding:23px 26px 18px;display:flex}.md-help-dialog h2{margin:3px 0 0;font-family:Georgia,Times New Roman,serif;font-size:24px;font-weight:500}.md-help-dialog__eyebrow{color:#9b7650;letter-spacing:.15em;font-size:9px;font-weight:700}.md-help-dialog header button{color:#6e6961;cursor:pointer;background:0 0;border:0;place-items:center;padding:5px;display:grid}.md-help-grid{grid-template-columns:1fr 1fr;gap:30px;padding:22px 26px 28px;display:grid}.md-help-grid dl{grid-template-columns:68px 1fr;align-items:baseline;gap:10px;margin:0;display:grid}.md-help-grid dt{color:#817b72;font-size:12px}.md-help-grid dd{margin:0}.md-help-grid code{font-family:SFMono-Regular,Consolas,monospace;font-size:12px}@media (width<=760px){.markdown-editor{min-height:360px}.md-toolbar__brand span{display:none}.md-toolbar__brand{padding-inline:10px}.md-toolbar__modes{padding-inline:4px}.md-toolbar__modes .md-icon-button:first-child{display:none}.markdown-editor[data-mode=split] .md-panel-group{display:block}.markdown-editor[data-mode=split] .md-editor-panel{height:100%;width:100%!important}.markdown-editor[data-mode=split] .md-preview-panel,.markdown-editor[data-mode=split] .md-resize-handle{display:none}.md-preview-document{padding:30px 23px 95px;font-size:15px}.md-mini-tools{bottom:31px;right:9px}.md-word-count{display:none}.md-help-grid{grid-template-columns:1fr;gap:10px}}@media (prefers-reduced-motion:reduce){.markdown-editor *,.markdown-editor :before,.markdown-editor :after{scroll-behavior:auto!important;transition-duration:.01ms!important}}@media print{.md-toolbar,.md-editor-pane,.md-resize-handle,.md-mini-tools,.md-mermaid__enlarge,.md-status{display:none!important}.markdown-editor,.md-workspace,.md-preview-pane{border:0;height:auto!important;overflow:visible!important}.md-preview-document{max-width:none;padding:0}}
|
|
2
|
+
/*$vite$:1*/
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export type EditorMode = 'split' | 'edit' | 'preview';
|
|
3
|
+
export type EditorTheme = 'earthsong' | 'light';
|
|
4
|
+
export type EditorCommand = 'heading1' | 'heading2' | 'heading3' | 'heading4' | 'heading5' | 'bold' | 'italic' | 'quote' | 'bulletList' | 'orderedList' | 'task' | 'link' | 'image' | 'inlineCode' | 'codeBlock' | 'divider' | 'formula' | 'table';
|
|
5
|
+
export interface MarkdownEditorShortcuts {
|
|
6
|
+
bold?: string;
|
|
7
|
+
link?: string;
|
|
8
|
+
image?: string;
|
|
9
|
+
preview?: string;
|
|
10
|
+
maximize?: string;
|
|
11
|
+
help?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ImageUploadResult {
|
|
14
|
+
url: string;
|
|
15
|
+
alt?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface MarkdownEditorProps {
|
|
18
|
+
value?: string;
|
|
19
|
+
defaultValue?: string;
|
|
20
|
+
onChange?: (value: string) => void;
|
|
21
|
+
mode?: EditorMode;
|
|
22
|
+
onModeChange?: (mode: EditorMode) => void;
|
|
23
|
+
readOnly?: boolean;
|
|
24
|
+
height?: number | string;
|
|
25
|
+
theme?: EditorTheme;
|
|
26
|
+
className?: string;
|
|
27
|
+
ariaLabel?: string;
|
|
28
|
+
shortcuts?: MarkdownEditorShortcuts;
|
|
29
|
+
onImageUpload?: (file: File, signal: AbortSignal) => Promise<string | ImageUploadResult>;
|
|
30
|
+
onError?: (error: Error) => void;
|
|
31
|
+
renderStatus?: (details: {
|
|
32
|
+
words: number;
|
|
33
|
+
characters: number;
|
|
34
|
+
line: number;
|
|
35
|
+
column: number;
|
|
36
|
+
}) => ReactNode;
|
|
37
|
+
}
|
|
38
|
+
export interface TocItem {
|
|
39
|
+
depth: number;
|
|
40
|
+
text: string;
|
|
41
|
+
slug: string;
|
|
42
|
+
line: number;
|
|
43
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yanglingfeng/md-web",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "接近马克飞象书写体验的 React Markdown 编辑器组件",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "ylfeng250",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ylfeng250/md-web.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/ylfeng250/md-web#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ylfeng250/md-web/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"markdown",
|
|
18
|
+
"editor",
|
|
19
|
+
"react",
|
|
20
|
+
"codemirror",
|
|
21
|
+
"gfm",
|
|
22
|
+
"mermaid",
|
|
23
|
+
"katex"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": [
|
|
26
|
+
"**/*.css"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"main": "./dist/index.js",
|
|
35
|
+
"module": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"import": "./dist/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./style.css": "./dist/style.css"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"dev": "vite",
|
|
46
|
+
"build": "vite build --mode lib",
|
|
47
|
+
"build:demo": "tsc -b && vite build",
|
|
48
|
+
"lint": "eslint . && oxlint",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"typecheck": "tsc -b --pretty false",
|
|
52
|
+
"preview": "vite preview --outDir dist-demo",
|
|
53
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"react": ">=18",
|
|
57
|
+
"react-dom": ">=18"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@codemirror/commands": "^6.11.0",
|
|
61
|
+
"@codemirror/lang-markdown": "^6.5.2",
|
|
62
|
+
"@codemirror/language": "^6.12.4",
|
|
63
|
+
"@codemirror/state": "^6.7.1",
|
|
64
|
+
"@codemirror/view": "^6.43.9",
|
|
65
|
+
"@lezer/highlight": "^1.2.3",
|
|
66
|
+
"@uiw/react-codemirror": "^4.25.11",
|
|
67
|
+
"github-slugger": "^2.0.0",
|
|
68
|
+
"highlight.js": "^11.12.0",
|
|
69
|
+
"katex": "^0.18.4",
|
|
70
|
+
"lucide-react": "^1.34.0",
|
|
71
|
+
"mdast-util-to-string": "^4.0.0",
|
|
72
|
+
"mermaid": "^11.17.2",
|
|
73
|
+
"react-markdown": "^10.1.0",
|
|
74
|
+
"react-resizable-panels": "^4.12.3",
|
|
75
|
+
"rehype-highlight": "^7.0.2",
|
|
76
|
+
"rehype-katex": "^7.0.1",
|
|
77
|
+
"rehype-slug": "^6.0.0",
|
|
78
|
+
"remark-breaks": "^4.0.0",
|
|
79
|
+
"remark-gfm": "^4.0.1",
|
|
80
|
+
"remark-math": "^6.0.0",
|
|
81
|
+
"unist-util-visit": "^5.1.0"
|
|
82
|
+
},
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@eslint/js": "^10.0.1",
|
|
85
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
86
|
+
"@testing-library/react": "^16.3.2",
|
|
87
|
+
"@testing-library/user-event": "^14.6.6",
|
|
88
|
+
"@types/node": "^24.13.3",
|
|
89
|
+
"@types/react": "^19.2.18",
|
|
90
|
+
"@types/react-dom": "^19.2.4",
|
|
91
|
+
"@vitejs/plugin-react": "^6.1.0",
|
|
92
|
+
"eslint": "^10.9.1",
|
|
93
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
94
|
+
"eslint-plugin-react-refresh": "^0.5.4",
|
|
95
|
+
"globals": "^17.11.0",
|
|
96
|
+
"jsdom": "^30.0.1",
|
|
97
|
+
"oxlint": "^1.79.0",
|
|
98
|
+
"react": "^19.2.8",
|
|
99
|
+
"react-dom": "^19.2.8",
|
|
100
|
+
"typescript": "~6.0.2",
|
|
101
|
+
"typescript-eslint": "^8.68.0",
|
|
102
|
+
"vite": "^8.2.2",
|
|
103
|
+
"vite-plugin-dts": "^5.0.3",
|
|
104
|
+
"vitest": "^4.1.11"
|
|
105
|
+
}
|
|
106
|
+
}
|