fix(markdown): render CJK bold that ends in punctuation in desktop and phone chats (#32020)

This commit is contained in:
Kelvin Amoaba
2026-10-23 15:25:16 +00:00
committed by GitHub
parent 6d1aaa4e26
commit 2570d8fa5c
6 changed files with 73 additions and 3 deletions
+1
View File
@@ -54,6 +54,7 @@
"lowlight": "^3.3.0",
"lucide-react-native": "^1.14.0",
"markdown-it": "^14.3.2",
"markdown-it-cjk-friendly": "^2.0.3",
"markdown-it-footnote": "^4.0.0",
"mermaid": "11.17.2",
"react": "19.2.8",
+26
View File
@@ -279,6 +279,9 @@ importers:
markdown-it:
specifier: 14.3.3
version: 14.3.3
markdown-it-cjk-friendly:
specifier: ^2.0.3
version: 2.0.3(@types/markdown-it@14.1.2)(markdown-it@14.3.3)
markdown-it-footnote:
specifier: ^4.0.0
version: 4.0.0
@@ -4650,6 +4653,10 @@ packages:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
get-east-asian-width@1.7.0:
resolution: {integrity: sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==}
engines: {node: '>=18'}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
@@ -5558,6 +5565,16 @@ packages:
makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
markdown-it-cjk-friendly@2.0.3:
resolution: {integrity: sha512-dBAhqp7GZ6he15ug2t5JSfwTw2KvZX9tt0y7Up0reR1cZOBUogSq5V859hcIGwge7hjW1SkUaWkNr++0HszyZg==}
engines: {node: '>=18'}
peerDependencies:
'@types/markdown-it': '*'
markdown-it: 14.3.3
peerDependenciesMeta:
'@types/markdown-it':
optional: true
markdown-it-footnote@4.0.0:
resolution: {integrity: sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==}
@@ -12448,6 +12465,8 @@ snapshots:
get-caller-file@2.0.5: {}
get-east-asian-width@1.7.0: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -13550,6 +13569,13 @@ snapshots:
dependencies:
tmpl: 1.0.5
markdown-it-cjk-friendly@2.0.3(@types/markdown-it@14.1.2)(markdown-it@14.3.3):
dependencies:
get-east-asian-width: 1.7.0
markdown-it: 14.3.3
optionalDependencies:
'@types/markdown-it': 14.1.2
markdown-it-footnote@4.0.0: {}
markdown-it@14.3.3:
@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest'
import { parseMobileMarkdownTree } from './mobile-markdown-tree'
function inlineNodes(source: string) {
const [paragraph] = parseMobileMarkdownTree(source, false).children
return paragraph?.type === 'paragraph' ? paragraph.children : []
}
describe('CJK emphasis in mobile markdown', () => {
it('closes bold that ends in punctuation right before CJK text', () => {
expect(
inlineNodes('**单算 Runtime 执行引擎,平均每题 29.78 秒。**边界是开始执行到产出结果。')
).toContainEqual({
type: 'strong',
children: [{ type: 'text', value: '单算 Runtime 执行引擎,平均每题 29.78 秒。' }]
})
})
it('keeps CommonMark flanking rules between non-CJK characters', () => {
expect(inlineNodes('**Note.**Next')).toEqual([{ type: 'text', value: '**Note.**Next' }])
})
})
@@ -1,4 +1,5 @@
import MarkdownIt from 'markdown-it'
import cjkFriendly from 'markdown-it-cjk-friendly'
import footnote from 'markdown-it-footnote'
import type {
AlignType,
@@ -14,9 +15,10 @@ import remend from 'remend'
import { normalizeMobileMarkdownPreviewHtml } from './mobile-markdown-preview-html'
import { endLinkifyUrlsAtProsePunctuation } from './markdown-linkify-prose-boundary'
// CommonMark with GitHub's tables, strikethrough, autolinks and footnotes: the rules desktop's
// renderer follows, read by a parser fast enough for the phone's JavaScript engine.
const markdown = new MarkdownIt({ html: true, linkify: true }).use(footnote)
// CommonMark with GitHub's tables, strikethrough, autolinks, footnotes and CJK-friendly
// emphasis: the rules desktop's renderer follows, read by a parser fast enough for the phone's
// JavaScript engine.
const markdown = new MarkdownIt({ html: true, linkify: true }).use(footnote).use(cjkFriendly)
// Every href parses as a link, file: included; routeMarkdownHref decides what a tap opens.
markdown.validateLink = () => true
endLinkifyUrlsAtProsePunctuation(markdown.linkify)
@@ -333,6 +333,23 @@ function renderComment(content: string, variant: 'compact' | 'document' = 'docum
return renderToStaticMarkup(<CommentMarkdown variant={variant} content={content} />)
}
describe('CJK emphasis', () => {
it('closes bold that ends in punctuation right before CJK text', () => {
expect(
renderComment('**单算 Runtime 执行引擎,平均每题 29.78 秒。**边界是开始执行到产出结果。')
).toContain(
'<strong>单算 Runtime 执行引擎,平均每题 29.78 秒。</strong>边界是开始执行到产出结果。'
)
})
it('keeps CommonMark flanking rules between non-CJK characters', () => {
const markup = renderComment('**Note.**Next')
expect(markup).not.toContain('<strong>')
expect(markup).toContain('**Note.**Next')
})
})
describe('GitHub callouts', () => {
it.each(['compact', 'document'] as const)(
'renders a %s quote that opens with a marker as a titled callout',
@@ -6,6 +6,7 @@ import {
import Markdown, { defaultUrlTransform, type Components } from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkBreaks from 'remark-breaks'
import remarkCjkFriendly from 'remark-cjk-friendly/parseOnly'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize, { defaultSchema, type Options as SanitizeSchema } from 'rehype-sanitize'
import remend from 'remend'
@@ -83,6 +84,7 @@ const commentMarkdownFileUriUrlTransform: UrlTransform = (value, key, node) => {
// with existing plain-text comments that rely on newline formatting.
const remarkPlugins = [
remarkGfm,
remarkCjkFriendly,
remarkAutolinkLiteralProseBoundary,
remarkGitHubCallouts,
remarkBreaks