Ver código fonte

Merge branch 'code-mode-ui/shiki' into code-mode-ui/trajectory-spans

Tianyi Cui 2 meses atrás
pai
commit
00388f2d82

+ 5 - 5
packages/client/ui-primitives/src/markdown/MarkdownText.tsx

@@ -53,14 +53,14 @@ function buildComponents(streaming: boolean): Components {
     // plain arm — retokenizing a growing fence on every chunk is quadratic
     // main-thread work; the finalize swap highlights it once.
     pre: ({ children }) => {
+      /* v8 ignore next 2 -- the markdown pipeline always hands `pre` its single `code` element; the undefined arm guards a react-markdown representation change. */
       const child = isValidElement<{ className?: string; children?: unknown }>(children) ? children : undefined
       const raw = child?.props.children
-      const text = typeof raw === 'string' ? raw : Array.isArray(raw) && typeof raw[0] === 'string' ? raw[0] : undefined
-      // A fence whose content isn't one plain string (never produced by the
-      // markdown pipeline) keeps the stock <pre> rather than guessing.
-      if (text === undefined) return <pre>{children}</pre>
+      // A fence whose content isn't one plain string (e.g. an empty fence)
+      // keeps the stock <pre> rather than guessing.
+      if (typeof raw !== 'string') return <pre>{children}</pre>
       const lang = /language-([\w-]+)/.exec(child?.props.className ?? '')?.[1]
-      return <CodeBlock code={text} lang={streaming ? undefined : lang} />
+      return <CodeBlock code={raw} lang={streaming ? undefined : lang} />
     },
   }
 }

+ 9 - 0
packages/client/ui-primitives/tests/markdown.spec.tsx

@@ -64,6 +64,15 @@ describe('MarkdownText', () => {
     expect(screen.getByRole('link', { name: 'https://deepseek.com' })).toBeTruthy()
   })
 
+  it('an empty fence keeps the stock pre; a language-less fence renders the plain CodeBlock arm', () => {
+    const empty = render(<MarkdownText text={'```\n```'} />)
+    expect(empty.container.querySelector('pre')?.outerHTML).toBe('<pre><code></code></pre>')
+
+    const plain = render(<MarkdownText text={'```\nno language here\n```'} />)
+    expect(plain.container.querySelector('pre.shiki')).toBeNull()
+    expect(plain.container.querySelector('pre code')?.textContent).toContain('no language here')
+  })
+
   it('streaming renders fences plain; the finalize swap highlights them', () => {
     const fence = '```ts\nconst answer = 42\n```'
     const live = render(<MarkdownText text={fence} streaming />)