mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
fix(markdown): render CJK bold that ends in punctuation in desktop and phone chats (#32020)
This commit is contained in:
@@ -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",
|
||||
|
||||
Generated
+26
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user