fix(citations): render external links as badges during streaming

During streaming when citations are still loading (isLoadingCitations=true),
all external links should be rendered as badges since we don't know yet
which links are citations. After streaming completes, only links in
citationMap are rendered as badges.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ruitanglin
2026-02-06 16:09:03 +08:00
parent e9648b11cd
commit e444817c5d

View File

@@ -77,25 +77,43 @@ export function MessageListItem({
/**
* Custom link component that handles citations and external links
* Only links that are in citationMap are rendered as CitationLink badges
* Other links (project URLs, regular links) are rendered as plain links
* - During streaming (isLoadingCitations=true): all external links render as badges
* - After streaming: only links in citationMap render as badges
* - Human messages and non-citation links render as plain links
*/
function MessageLink({
href,
children,
citationMap,
isHuman,
isLoadingCitations,
}: React.AnchorHTMLAttributes<HTMLAnchorElement> & {
citationMap: Map<string, Citation>;
isHuman: boolean;
isLoadingCitations: boolean;
}) {
if (!href) return <span>{children}</span>;
// Human messages always render as plain links
if (isHuman) {
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="text-primary underline underline-offset-2 hover:no-underline"
>
{children}
</a>
);
}
const citation = citationMap.get(href);
const isExternalLink = href.startsWith("http://") || href.startsWith("https://");
// Only render as CitationLink badge if it's a citation (in citationMap)
// This ensures project URLs and regular links are not rendered as badges
if (citation && !isHuman) {
// During streaming: render all external links as badges (citations not yet fully loaded)
// After streaming: only render links in citationMap as badges
if (citation || (isLoadingCitations && isExternalLink)) {
return (
<CitationLink citation={citation} href={href}>
{children}
@@ -103,7 +121,7 @@ function MessageLink({
);
}
// All other links (including project URLs) render as plain links
// Non-citation links render as plain links
return (
<a
href={href}
@@ -202,12 +220,12 @@ function MessageContent_({
// Shared markdown components
const markdownComponents = useMemo(() => ({
a: (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
<MessageLink {...props} citationMap={citationMap} isHuman={isHuman} />
<MessageLink {...props} citationMap={citationMap} isHuman={isHuman} isLoadingCitations={isLoadingCitations} />
),
img: (props: React.ImgHTMLAttributes<HTMLImageElement>) => (
<MessageImage {...props} threadId={thread_id} maxWidth={isHuman ? "full" : "90%"} />
),
}), [citationMap, thread_id, isHuman]);
}), [citationMap, thread_id, isHuman, isLoadingCitations]);
// Render message response
// Human messages use humanMessagePlugins (no autolink) to prevent URL bleeding into adjacent text