fix: address markdown renderer review findings

Restore the replaced pipeline's synthetic fence newline so a real trailing
blank line inside a fence survives CodeBlock's display trim, and pin it
plus header-only tables in the DOM-parity corpus. Route
extractMarkdownPlainText through parseGfm so the grammar has one source.
Document the fixture provenance check (all 46 fixtures reproduce
byte-identically from react-markdown at 9e8101b800), the deliberate
O(prefix) divergence memcmp, the per-arm scope of grammar consistency,
the React 18 MathML namespace limitation, and the prefix-equivalence
corpus constraint.
This commit is contained in:
07akioni
2026-08-06 14:58:33 +08:00
parent d1e4fb91d2
commit 31b99930d9
14 changed files with 152 additions and 21 deletions

View File

@@ -0,0 +1,37 @@
<div class="_markdown_404681">
<div class="_block_9aea57 md-code-block">
<div class="_bannerWrap_9aea57">
<div class="_banner_9aea57">
<div class="_infostring_9aea57">
<div class="_action_9aea57">
<button class="_copyButton_9aea57" type="button">
#text "复制"
<pre class="_plain_9aea57">
<code>
#text "kept blank line follows\n"
<div class="_block_9aea57 md-code-block">
<div class="_bannerWrap_9aea57">
<div class="_banner_9aea57">
<div class="_infostring_9aea57">
#text "ts"
<div class="_action_9aea57">
<button class="_copyButton_9aea57" type="button">
#text "复制"
<div>
<pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0">
<code>
<span class="line">
<span style="color:var(--shiki-token-keyword)">
#text "const"
<span style="color:var(--shiki-token-constant)">
#text " doubled"
<span style="color:var(--shiki-token-keyword)">
#text " ="
<span style="color:var(--shiki-token-constant)">
#text " true"
#text "\n"
<span class="line">
#text "\n"
<span class="line">
<p>
#text "after"

View File

@@ -0,0 +1,23 @@
<div class="_markdown_404681">
<div class="_block_9aea57 md-code-block">
<div class="_bannerWrap_9aea57">
<div class="_banner_9aea57">
<div class="_infostring_9aea57">
<div class="_action_9aea57">
<button class="_copyButton_9aea57" type="button">
#text "复制"
<pre class="_plain_9aea57">
<code>
#text "kept blank line follows\n"
<div class="_block_9aea57 md-code-block">
<div class="_bannerWrap_9aea57">
<div class="_banner_9aea57">
<div class="_infostring_9aea57">
<div class="_action_9aea57">
<button class="_copyButton_9aea57" type="button">
#text "复制"
<pre class="_plain_9aea57">
<code>
#text "const doubled = true\n\n"
<p>
#text "after"

View File

@@ -0,0 +1,11 @@
<div class="_markdown_404681">
<div class="_tableScroll_404681">
<table>
<thead>
<tr>
<th>
#text "a"
<th>
#text "b"
<p>
#text "after"

View File

@@ -0,0 +1,11 @@
<div class="_markdown_404681">
<div class="_tableScroll_404681">
<table>
<thead>
<tr>
<th>
#text "a"
<th>
#text "b"
<p>
#text "after"

View File

@@ -6,6 +6,15 @@
// normalization), so a fixture diff means a user-visible markdown style
// change and must be reviewed as such — never re-record to silence a
// refactor.
//
// Provenance is reproducible: the replaced pipeline last lived at commit
// 9e8101b800 (origin/master before the renderer swap merged). Checking out
// that ref in a worktree, copying this spec, and running it records all
// fixtures from react-markdown byte-identical to the ones committed here:
// git worktree add /tmp/parity origin/master --detach && cd /tmp/parity
// pnpm install && cp <this spec> packages/client/ui-primitives/tests/
// npx vitest run packages/client/ui-primitives/tests/markdown-dom-parity.spec.tsx
// diff -r <recorded fixtures> <this branch's fixtures> # byte-identical
import { cleanup, render } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'
import { MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives'
@@ -139,6 +148,21 @@ const CORPUS: Record<string, string> = {
' indented code block',
' second line',
].join('\n'),
'fence-trailing-blank-lines': [
'```',
'kept blank line follows',
'',
'```',
'',
'```ts',
'const doubled = true',
'',
'',
'```',
'',
'after',
].join('\n'),
'table-header-only': '| a | b |\n| --- | --- |\n\nafter',
'inline-code-with-newline': 'Spans `a\nb` across a line.',
'links-and-autolinks': [
'[https ok](https://example.com "with title") and [mailto ok](mailto:dev@example.com).',

View File

@@ -12,7 +12,15 @@ import { parseGfm } from '../src/markdown/parse.ts'
afterEach(cleanup)
/** A many-block document exercising every freeze-sensitive construct. */
/**
* A many-block document exercising every freeze-sensitive construct. The
* prefix-equivalence property below holds only while no reference or
* footnote definition lands on the far side of a freeze boundary from its
* use: a fresh mount parses everything in one tree while the live stream's
* frozen blocks are already baked (the fingerprint test demonstrates the
* documented deviation). Keep definitions adjacent to their references when
* extending this corpus.
*/
const STREAM_DOC = [
'# Title',
'',