mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-21 13:24:44 +08:00
feat(citations): add shared citation components and optimize code
## New Features - Add `CitationLink` shared component for rendering citation hover cards - Add `CitationsLoadingIndicator` component for showing loading state - Add `removeAllCitations` utility to strip all citations from content - Add backend support for removing citations when downloading markdown files - Add i18n support for citation loading messages (en-US, zh-CN) ## Code Optimizations - Remove duplicate `ExternalLinkBadge` component, reuse `CitationLink` instead - Consolidate `remarkPlugins` config in `streamdownPlugins` to avoid duplication - Remove unused imports: `Citation`, `buildCitationMap`, `extractDomainFromUrl`, etc. - Remove unused `messages` parameter from `ToolCall` component - Remove unused `isWriteFile` parameter from `ArtifactFilePreview` component - Remove unused `useI18n` hook from `MessageContent` component ## Bug Fixes - Fix `remarkGfm` plugin configuration that prevented table rendering - Fix React Hooks rule violation: move `useMemo` to component top level - Replace `||` with `??` for nullish coalescing in clipboard data ## Code Cleanup - Remove debug console.log/info statements from: - `threads/hooks.ts` - `notification/hooks.ts` - `memory-settings-page.tsx` - Fix import order in `message-group.tsx` Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -76,6 +76,29 @@ export function parseCitations(content: string): ParseCitationsResult {
|
||||
cleanContent = cleanContent.replace(/<citations>[\s\S]*$/g, "").trim();
|
||||
}
|
||||
|
||||
// Convert [cite-N] references to markdown links
|
||||
// Example: [cite-1] -> [Title](url)
|
||||
if (citations.length > 0) {
|
||||
// Build a map from citation id to citation object
|
||||
const idMap = new Map<string, Citation>();
|
||||
for (const citation of citations) {
|
||||
idMap.set(citation.id, citation);
|
||||
}
|
||||
|
||||
// Replace all [cite-N] patterns with markdown links
|
||||
cleanContent = cleanContent.replace(/\[cite-(\d+)\]/g, (match, num) => {
|
||||
const citeId = `cite-${num}`;
|
||||
const citation = idMap.get(citeId);
|
||||
if (citation) {
|
||||
// Use title if available, otherwise use domain
|
||||
const linkText = citation.title || extractDomainFromUrl(citation.url);
|
||||
return `[${linkText}](${citation.url})`;
|
||||
}
|
||||
// If citation not found, keep the original text
|
||||
return match;
|
||||
});
|
||||
}
|
||||
|
||||
return { citations, cleanContent };
|
||||
}
|
||||
|
||||
@@ -129,3 +152,51 @@ export function isCitationsBlockIncomplete(content: string): boolean {
|
||||
|
||||
return hasOpenTag && !hasCloseTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove ALL citations from content, including:
|
||||
* - <citations> blocks
|
||||
* - [cite-N] references
|
||||
* - Citation markdown links that were converted from [cite-N]
|
||||
*
|
||||
* This is used for copy/download operations where we want clean content without any references.
|
||||
*
|
||||
* @param content - The raw content that may contain citations
|
||||
* @returns Content with all citations completely removed
|
||||
*/
|
||||
export function removeAllCitations(content: string): string {
|
||||
if (!content) {
|
||||
return content;
|
||||
}
|
||||
|
||||
let result = content;
|
||||
|
||||
// Step 1: Remove all <citations> blocks (complete and incomplete)
|
||||
result = result.replace(/<citations>[\s\S]*?<\/citations>/g, "");
|
||||
result = result.replace(/<citations>[\s\S]*$/g, "");
|
||||
|
||||
// Step 2: Remove all [cite-N] references
|
||||
result = result.replace(/\[cite-\d+\]/g, "");
|
||||
|
||||
// Step 3: Parse to find citation URLs and remove those specific links
|
||||
const parsed = parseCitations(content);
|
||||
const citationUrls = new Set(parsed.citations.map(c => c.url));
|
||||
|
||||
// Remove markdown links that point to citation URLs
|
||||
// Pattern: [text](url)
|
||||
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
|
||||
// If this URL is a citation, remove the entire link
|
||||
if (citationUrls.has(url)) {
|
||||
return "";
|
||||
}
|
||||
// Keep non-citation links
|
||||
return match;
|
||||
});
|
||||
|
||||
// Step 4: Clean up extra whitespace and newlines
|
||||
result = result
|
||||
.replace(/\n{3,}/g, "\n\n") // Replace 3+ newlines with 2
|
||||
.trim();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user