Skip to content

fix(markdown): 修复 KaTeX 公式溢出、围栏相邻内容失效与表格代码断词 - #9

Merged
xiaolfeng merged 6 commits into
xiaolfeng:masterfrom
x-dc-coder:fix/markdown-rendering
Oct 28, 2026
Merged

xiaolfeng merged 6 commits into
xiaolfeng:masterfrom
x-dc-coder:fix/markdown-rendering

Conversation

@x-dc-coder

Copy link
Copy Markdown
Contributor

PR:fix(markdown): 修复 KaTeX 公式溢出、围栏相邻内容失效与表格代码断词

动机

Wiki Reader(web-wiki)与主站共用的 Markdown 渲染管线(components/src/markdown)存在三类前端渲染缺陷:

  1. KaTeX 长公式溢出公式块:katex.min.css 将 .katex-display > .katex 设为 nowrap,超宽公式直接画出公式块、被视口截断,页面版面被撑破。
  2. fenced 围栏与内容相邻时渲染为纯文本:markdown 会把相邻行合并为同一段落,:::callout{...} 后无空行直接跟内容时,围栏不再是独立段落,isOpeningFence 的单子节点判定失效,callout 整段按纯文本输出(如 research/twostage 页的全部 callout)。
  3. 表格行内代码逐字符断词:prose 的 [&_code]:break-all 使窄表格列中的文件名被任意拆断(02_gen_query_opt.py → 02_gen_query_opt.p / y)。

修改

  1. theme.css(9502f7b):.katex-display 改为 flex + overflow-x: auto 安全居中——放得下保持居中,放不下块内横向滚动;scrollbar-width: thin 与 padding-bottom 防止滚动条遮挡下标。
  2. remark-fenced-blocks.ts(812f001):新增 splitFenceParagraph 归一化,把段首开栏行、段尾关栏行从合并段落中拆回独立围栏段落,交由原有识别逻辑处理(isOpeningFence / parseOpeningFence / collectFencedBlock 零改动)。
  3. prose.ts + markdown-table.tsx(0d5afab):行内代码断行策略 break-all → break-words,长代码仅在放不下时才断;Markdown 表格外层包 overflow-x-auto 滚动容器(与代码块、shadcn Table 包装策略一致),窄屏下表格整体横向滑动而非压缩内容。

测试

  • components:vitest 298 项全部通过(新增 remark-fenced-blocks 单测 6 项、react-markdown 集成测试 2 项);tsc --noEmit 通过;eslint 无新增问题
  • web-wiki:vitest 80 项全部通过
  • 浏览器实测:长公式在块内滚动且放得下时居中回归正常;callout 卡片正确渲染、无 ::: 残留;表格代码完整不断词;移动端 390px 表格横向滚动且无页面级溢出

说明:三类缺陷均已在本地部署实例(Docker 镜像构建自本分支)复现修复前后对比并验证通过。

katex.min.css 将 .katex-display > .katex 设为 nowrap,超宽公式会直接
溢出公式块撑破版面。改为 flex + margin auto 实现安全居中:放得下时
保持居中,放不下时贴左并在块内出现横向滚动条。
markdown 会把相邻行合并为同一段落,开栏行后无空行直接跟内容(或关栏行
紧跟内容行)时,围栏不再是独立段落,isOpeningFence / isClosingFence 无法
识别,整段按纯文本渲染。拆分归一化将首行开栏、末行关栏从段落中还原为
独立围栏段落,交由原有识别逻辑处理。
prose 的行内代码断行策略由 break-all 调整为 break-words,长代码仅在
放不下时才断,表格窄列不再把 02_gen_query_opt.py 之类的文件名拆断;
Markdown 表格外层包一层横向滚动容器(与代码块 overflow-x-auto、
shadcn Table 包装策略一致),窄容器下表格整体可横向滑动。

@xiaolfeng xiaolfeng left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

三处 Markdown 渲染缺陷的根因定位很准:KaTeX 用 flex + margin-inline: auto 避开了 text-align: center 的左侧不可达溢出,围栏选择在 mdast 层做前置段落归一化,表格外包横向滚动容器并将行内代码切回 break-words,整体改动边界清晰。

代码走查结合 unified + remark-parse + remark-rehype + react-dom 实测发现 1 处阻断级白屏回归(Blocker) 与 3 处契约/排版问题(Major),需修正并补齐回归用例后再合入。

审查问题总览

级别 文件位置 问题摘要
Blocker remark-fenced-blocks.ts 单次首尾切分叠加开栏正则不一致,在无空行嵌套 :::steps 或非标准开栏头下生成空标签 hName: "",触发 React Invalid tag: "" 白屏崩溃
Major remark-fenced-blocks.test.ts 新增单测均基于手写 createAst,未覆盖 remark-parse 真实段落合并与列表惰性延续行为
Major theme.css .katex-display 改为 flex 后 .katex 宽度收缩,导致带 \tag{...} 编号的块级公式与右侧编号重叠
Major markdown-table.tsx 未透传其余 <table> 属性;外层 BFC 使 .prose 默认表格外边距将滚动条推离表格底边
Nit prose.ts 多列表格窄屏下中文表头可能被长代码列挤压成单字竖排;[overflow-wrap:anywhere] 被同级 break-words 覆盖

合入前核对清单

  • 重构 remark-fenced-blocks.ts 为按行切分,统一开栏正则与组件白名单(callout | card | steps | step),处理列表/引用尾行 ::: 惰性延续,消除 AST 子节点原地变异
  • 补齐经过真实 Markdown 解析管线的嵌套围栏、连续相邻围栏、非标准开栏回退与列表内关栏回归单测
  • 修复 KaTeX 带 \tag{...} 块级公式的宽度收缩重叠问题
  • 补齐 MarkdownTable 的 ...rest 属性转发与纵向外边距归属调整

Comment thread components/src/styles/theme.css Resolved
Comment thread components/src/markdown/prose.ts Resolved
- 重构 remark-fenced-blocks 为按行切分与统一白名单校验,杜绝空标签 hName 生成与 AST 原地变异
- 提升列表与引用块尾行由于 CommonMark 惰性延续误吞的 ::: 关栏标记
- 修复 KaTeX 带 \tag{...} 块级公式在 flex 下宽度收缩导致的编号重叠缺陷
- 补齐 MarkdownTable 的 ...rest 属性透传与纵向外边距归属调整
- prose 增加 [&_th]:whitespace-nowrap 防止多列表格表头被长代码挤压为单字竖排
- 补齐嵌套围栏、连续相邻围栏、非标准开栏回退与列表内关栏端到端回归测试
@xiaolfeng

Copy link
Copy Markdown
Owner

看到新提交推上来了。我平时只从待审队列里集中看 PR,不会主动轮询分支上的新 commit。

Tip

每次改完推送并确认 CI 跑绿后,记得在页面右上角 Reviewers 一栏点一下我名字旁边的 Re-request review(环形箭头图标)。状态切回待复审后我这边才会收到提醒并进入下一轮检查,避免你等空。

- 恢复 proseArticle 核心表格与列表样式变量,消除暗色模式文字隐形回归
- 重构 KaTeX 编号公式为 flex 弹性流,移除固定 4rem padding 并自适应撑开
- 修复 remark-fenced-blocks 中 autolink 拆分开栏、列表尾行吞行漏提及属性正则
- MarkdownTable 过滤 node hast 属性杜绝 DOM 污染,收敛外边距为自适应
- 补齐真实 Markdown 渲染管线下 autolink 开栏、连续 steps 及编号公式测试
- 为 .katex-display 补充基于 local/scroll 背景的双向滚动边缘渐隐阴影,指示横向溢出
- 为 .tag 补充 align-self: center,确保在 aligned 多行推导或矩阵环境下与几何中心严格对齐
- 补齐多行带编号 aligned 环境与 pmatrix 复杂矩阵的端到端单测覆盖
@xiaolfeng
xiaolfeng self-requested a review October 28, 2026 17:19
@xiaolfeng

Copy link
Copy Markdown
Owner

修复与优化进度说明 (commits: 1ff01b5, 105ab98)

已接手并彻底修复上一轮评审指出的全部遗留缺陷与视觉回归:

  1. proseArticle 暗色模式样式丢失修复:恢复末尾被误删的 6 行微明色盘变量覆盖(表格、列表、分割线、强调),消除暗色模式文字隐形问题,并在 prose.test.ts 补齐断言守卫。
  2. KaTeX 编号公式 flex 弹性流与自适应间距重构:
    • 为 .katex-display > .katex 补充 flex-shrink: 0 并移除显式 min-width: 100%,根治窄屏长公式被强制压缩重叠缺陷;
    • 将带编号 .katex-html 重构为 flex 流式排版,移除固定 padding-right: 4rem,让 .tag 脱离绝对定位自适应撑开且短公式完美居中不偏心;
    • 为 .katex-display 增加双向滚动渐隐阴影提示,指示横向溢出;.tag 增加 align-self: center 确保与多行推导(aligned)及复杂矩阵严格垂直居中对齐。
  3. remark-fenced-blocks 深度解析缺陷根治:
    • 支持还原被 remark-gfm autolink literal 误拆的开栏属性 URL 为纯文本,解决带链接卡片开栏 100% 失败问题;
    • 采用多节点提升机制根治列表/引用尾行紧贴关栏 ::: 且后方紧跟普通段落或连续 steps 时的吞行漏提问题;
    • 属性解析正则增强支持单引号、双引号、嵌套花括号与空值。
  4. MarkdownTable DOM 污染与外边距自适应:解构并过滤 react-markdown 注入的 node hast 对象,外边距收敛为自适应 my-4 first:mt-0 last:mb-0。
  5. 单测全量通过:
    • @lumina/components:56 个测试文件、313 项测试全部通过(新增开栏 URL 还原、多节点提升、多行 aligned 与矩阵测试);
    • web-wiki:10 个测试文件、80 项测试全部通过;
    • web:35 个测试文件、173 项测试全部通过;
    • 库构建 tsc --noEmit 零报错通过。

@xiaolfeng
xiaolfeng merged commit 3f5f2de into xiaolfeng:master Oct 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants