Files
deepseek-harness/packages/client/ui-primitives/src/markdown/parse.ts
T
Turtle 2099andTurtle c262a09fd0 fix(ui-primitives): keep backslashes in Windows-path Markdown links- #7098
* fix(ui-primitives): keep backslashes in Windows-path Markdown destinations

CommonMark reads \. in a link destination as an escaped dot, so
[x](D:\repo\.github\a.md) opened D:\repo.github\a.md. Destinations
that look like Windows paths now keep their authored text.

Fixes #7095

* fix(ui-primitives): cover relative paths and character references

Treat a backslash destination without a URL scheme as a Windows path
when it has no slash or a backslash before a letter or digit, keep
every backslash literal there, and still decode character references.
Pin the behavior in the Markdown file-links Web snapshot.

* chore: list micromark-util-decode-string in third-party notices

---------

Co-authored-by: Turtle <turtle1999@deepseek.com>
2026-10-23 23:40:50 +08:00

53 lines
2.3 KiB
TypeScript

/**
* The markdown renderer's two mdast grammars, one per rendering arm. Each
* arm is internally consistent — the incremental tail parses, the one-shot
* parses, and the plain-text projection of a given grammar always agree on
* where blocks start and end — and the settled grammar is the streaming one
* plus the math extensions, so the arms differ only where TeX delimiters
* begin a math construct (a `$$` block is a paragraph while streaming and a
* math block once settled, by design).
*/
import type { Root } from 'mdast'
import { recoverLocalImages } from './local-image-syntax.ts'
import { fromMarkdown } from 'mdast-util-from-markdown'
import { gfmFromMarkdown } from 'mdast-util-gfm'
import { mathFromMarkdown } from 'mdast-util-math'
import { gfm } from 'micromark-extension-gfm'
import { math } from 'micromark-extension-math'
import { cjkFriendlyStrong } from './cjkFriendlyStrong.ts'
import { mathCompatibility } from './mathCompatibility.ts'
import { windowsPathDestinations } from './windows-path-destinations.ts'
/** GFM strikethrough requires `~~`; a single `~` stays literal, as in ranges such as `10~20`. */
const gfmOptions = { singleTilde: false }
/**
* Parse GFM markdown (the streaming arm's grammar: no math, so incomplete
* TeX never flashes KaTeX errors mid-stream).
* @param text - Markdown source.
* @returns The mdast root.
*/
export function parseGfm(text: string): Root {
return recoverLocalImages(fromMarkdown(text, {
extensions: [gfm(gfmOptions), cjkFriendlyStrong()],
mdastExtensions: [gfmFromMarkdown(), windowsPathDestinations()],
}), text)
}
/**
* Parse GFM markdown plus TeX math with the compatibility delimiters
* (the settled arm's grammar).
* @param text - Markdown source.
* @returns The mdast root.
*/
export function parseGfmWithMath(text: string): Root {
return recoverLocalImages(fromMarkdown(text, {
// `mathCompatibility()` MUST stay last: micromark dispatches the extension
// registered last first, so its dollar flow takes precedence over `math()`
// and keeps the opening line's text as math instead of upstream's fence meta.
extensions: [gfm(gfmOptions), cjkFriendlyStrong(), math(), mathCompatibility()],
mdastExtensions: [gfmFromMarkdown(), mathFromMarkdown(), windowsPathDestinations()],
}), text)
}