@@ -180,6 +180,7 @@ interface ChatMarkdownProps {
180180 onUseArtifactTemplate ?: ( ( template : CodexArtifactTemplate ) => void ) | undefined ;
181181 imageBaseDir ?: string | undefined ;
182182 onImageExpand ?: ( ( preview : ExpandedImagePreview ) => void ) | undefined ;
183+ extraRemarkPlugins ?: NonNullable < ReactMarkdownOptions [ "remarkPlugins" ] > ;
183184}
184185
185186export function canUseMarkdownFileShellActions (
@@ -229,6 +230,7 @@ export function shouldUseMarkdownFileBrowserPrimaryAction(input: {
229230}
230231
231232const EMPTY_MARKDOWN_SKILLS : ReadonlyArray < Pick < ServerProviderSkill , "name" | "displayName" > > = [ ] ;
233+ const EMPTY_REMARK_PLUGINS : NonNullable < ReactMarkdownOptions [ "remarkPlugins" ] > = [ ] ;
232234
233235const ARTIFACT_TEMPLATE_ICON_BY_KIND = {
234236 document : FileTextIcon ,
@@ -378,6 +380,7 @@ const CHAT_MARKDOWN_SANITIZE_SCHEMA = {
378380 code : [ ...( defaultSchema . attributes ?. code ?? [ ] ) , "dataCodeMeta" , "dataInlineCode" ] ,
379381 blockquote : [ ...( defaultSchema . attributes ?. blockquote ?? [ ] ) , "dataAlert" ] ,
380382 div : [ ...( defaultSchema . attributes ?. div ?? [ ] ) , ...CODEX_ARTIFACT_TEMPLATE_HAST_PROPERTIES ] ,
383+ a : [ ...( defaultSchema . attributes ?. a ?? [ ] ) , "dataPullRequestAutolink" ] ,
381384 img : [ ...( defaultSchema . attributes ?. img ?? [ ] ) , "dataLocalSrc" , "dataMarkdownTitle" ] ,
382385 } ,
383386 protocols : {
@@ -1830,6 +1833,7 @@ function ChatMarkdown({
18301833 onUseArtifactTemplate,
18311834 imageBaseDir,
18321835 onImageExpand,
1836+ extraRemarkPlugins = EMPTY_REMARK_PLUGINS ,
18331837} : ChatMarkdownProps ) {
18341838 const { resolvedTheme } = useTheme ( ) ;
18351839 const createAssetUrl = useAtomQueryRunner ( assetEnvironment . createUrl , {
@@ -2237,13 +2241,25 @@ function ChatMarkdown({
22372241 : null ;
22382242 if ( ! fileLinkMeta ) {
22392243 const faviconHost = resolveExternalWebLinkHost ( href ) ;
2244+ const pullRequestAutolink = String (
2245+ ( props as Record < string , unknown > ) [ "data-pull-request-autolink" ] ?? "" ,
2246+ ) ;
2247+ const pullRequestCopy =
2248+ pullRequestAutolink === "commit"
2249+ ? / \/ c o m m i t \/ ( [ 0 - 9 a - f ] { 40 } ) $ / iu. exec ( href ?? "" ) ?. [ 1 ]
2250+ : pullRequestAutolink === "reference"
2251+ ? plainHastText ( node )
2252+ : undefined ;
2253+ const isPullRequestAutolink = pullRequestCopy !== undefined ;
22402254 const isSameDocumentLink = href ?. startsWith ( "#" ) ?? false ;
22412255 const onClick = props . onClick ;
22422256 const canOpenInPreview = Boolean ( threadRef ) && isPreviewSupportedInRuntime ( ) ;
22432257 const linkChildren = < MarkdownLinkContext value > { children } </ MarkdownLinkContext > ;
22442258 const link = (
22452259 < a
22462260 { ...props }
2261+ className = { cn ( props . className , pullRequestAutolink === "commit" && "font-mono" ) }
2262+ data-markdown-copy = { pullRequestCopy }
22472263 href = { href }
22482264 target = { isSameDocumentLink ? undefined : "_blank" }
22492265 rel = { isSameDocumentLink ? undefined : "noopener noreferrer" }
@@ -2315,7 +2331,7 @@ function ChatMarkdown({
23152331 } ) ;
23162332 } }
23172333 >
2318- { faviconHost && hastHasText ( node ) ? (
2334+ { faviconHost && hastHasText ( node ) && ! isPullRequestAutolink ? (
23192335 // The provider wraps the result rather than the children:
23202336 // MarkdownExternalLinkContent inspects its first child for the
23212337 // leading text it splits with <wbr/>, and an element there
@@ -2479,6 +2495,14 @@ function ChatMarkdown({
24792495 ] ) ;
24802496 /* eslint-enable react/no-unstable-nested-components */
24812497
2498+ const remarkPlugins = useMemo (
2499+ ( ) => [
2500+ ...( lineBreaks ? CHAT_MARKDOWN_REMARK_PLUGINS_WITH_BREAKS : CHAT_MARKDOWN_REMARK_PLUGINS ) ,
2501+ ...extraRemarkPlugins ,
2502+ ] ,
2503+ [ extraRemarkPlugins , lineBreaks ] ,
2504+ ) ;
2505+
24822506 // react-markdown converts unparsed HTML nodes to text when skipHtml is false.
24832507 // Keep that behavior explicit because literal mode depends on escaping the
24842508 // complete source token instead of dropping it from the rendered message.
@@ -2491,9 +2515,7 @@ function ChatMarkdown({
24912515 onCopy = { handleCopy }
24922516 >
24932517 < ReactMarkdown
2494- remarkPlugins = {
2495- lineBreaks ? CHAT_MARKDOWN_REMARK_PLUGINS_WITH_BREAKS : CHAT_MARKDOWN_REMARK_PLUGINS
2496- }
2518+ remarkPlugins = { remarkPlugins }
24972519 rehypePlugins = { parseRawHtml ? CHAT_MARKDOWN_REHYPE_PLUGINS : undefined }
24982520 skipHtml = { false }
24992521 components = { markdownComponents }
0 commit comments