/** * 渲染契约兼容层 * * 把 markdown-it 的默认输出调整到与 v1(以及 tiptap/CSS 消费方)一致的标签契约: * *斜体* → (markdown-it 默认 ,v1/CSS 用 ) * ~~删除~~ → (markdown-it 默认 ,v1/CSS 用 ) * `行内代码` → * [链接](url) → (v1 一致;markdown-it 同时修复 href 引号与 title 支持) * ![alt(WxH)](url) → 注入 width/height style(v1 的图片尺寸扩展语法) */ export function rendererCompatPlugin(md: any) { // 注意 token 类型是 em_open / s_open(不是 emphasis_open / strikethrough_open) md.renderer.rules.em_open = () => "" md.renderer.rules.em_close = () => "" md.renderer.rules.s_open = () => "" md.renderer.rules.s_close = () => "" md.renderer.rules.code_inline = (tokens: any[], idx: number) => `${md.utils.escapeHtml(tokens[idx].content)}` const defaultLinkOpen = md.renderer.rules.link_open || ((tokens: any[], idx: number, options: any, env: any, self: any) => self.renderToken(tokens, idx, options)) md.renderer.rules.link_open = (tokens: any[], idx: number, options: any, env: any, self: any) => { tokens[idx].attrSet("target", "_blank") return defaultLinkOpen(tokens, idx, options, env, self) } const defaultImage = md.renderer.rules.image || ((tokens: any[], idx: number, options: any, env: any, self: any) => self.renderToken(tokens, idx, options)) md.renderer.rules.image = (tokens: any[], idx: number, options: any, env: any, self: any) => { const token = tokens[idx] // v1 扩展:alt 中的 (宽x高) 转为固定尺寸,alt 文本原样保留 const alt = self.renderInlineAsText(token.children || [], options, env) const m = /\((\d+x\d+)\)/.exec(alt) if (m) { const [w, h] = m[1].split("x").map(Number) if (w > 0 && h > 0) token.attrSet("style", `width: ${w}px; height: ${h}px;`) } return defaultImage(tokens, idx, options, env, self) } // 代码块语言/行号 class(v1 的 options.highlight / options.lineNumber 行为) const defaultFence = md.renderer.rules.fence || ((tokens: any[], idx: number, options: any, env: any, self: any) => self.renderToken(tokens, idx, options)) md.renderer.rules.fence = (tokens: any[], idx: number, options: any, env: any, self: any) => { const token = tokens[idx] const info = (token.info || "").trim() const classes = [ options.highlight ? `language-${md.utils.escapeHtml(info.split(/\s+/g)[0])}` : "", options.lineNumber ? "line-numbers" : "", ] .filter(Boolean) .join(" ") if (classes) token.attrJoin("class", classes) return defaultFence(tokens, idx, options, env, self) } }