diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx index 53bfcb1..4c88aa9 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -34,7 +34,11 @@ import { CodeEditor } from "@/components/workspace/code-editor"; import { useArtifactContent } from "@/core/artifacts/hooks"; import { urlOfArtifact } from "@/core/artifacts/utils"; import type { Citation } from "@/core/citations"; -import { removeAllCitations, useParsedCitations } from "@/core/citations"; +import { + contentWithoutCitationsFromParsed, + removeAllCitations, + useParsedCitations, +} from "@/core/citations"; import { useI18n } from "@/core/i18n/hooks"; import { installSkill } from "@/core/skills/api"; import { streamdownPlugins } from "@/core/streamdown"; @@ -96,12 +100,10 @@ export function ArtifactFileDetail({ ); const cleanContent = language === "markdown" && content ? parsed.cleanContent : (content ?? ""); - const contentWithoutCitations = useMemo(() => { - if (language === "markdown" && content) { - return removeAllCitations(content); - } - return content; - }, [content, language]); + const contentWithoutCitations = + language === "markdown" && content + ? contentWithoutCitationsFromParsed(parsed) + : (content ?? ""); const [viewMode, setViewMode] = useState<"code" | "preview">("code"); const [isInstalling, setIsInstalling] = useState(false); diff --git a/frontend/src/core/citations/index.ts b/frontend/src/core/citations/index.ts index a40fec2..c56d293 100644 --- a/frontend/src/core/citations/index.ts +++ b/frontend/src/core/citations/index.ts @@ -1,4 +1,5 @@ export { + contentWithoutCitationsFromParsed, extractDomainFromUrl, getCleanContent, hasCitationsBlock, diff --git a/frontend/src/core/citations/utils.ts b/frontend/src/core/citations/utils.ts index 965c2a6..0567b02 100644 --- a/frontend/src/core/citations/utils.ts +++ b/frontend/src/core/citations/utils.ts @@ -185,27 +185,25 @@ export function isCitationsBlockIncomplete(content: string): boolean { } /** - * Remove ALL citations from content, including: - * - blocks - * - [cite-N] references (and their converted markdown links) - * - * Uses parseCitations once, then strips citation links from cleanContent. - * Used for copy/download to produce content without any citation references. - * - * @param content - The raw content that may contain citations - * @returns Content with all citations completely removed + * Strip citation markdown links from already-cleaned content (from parseCitations). + * Use when you already have ParseCitationsResult to avoid parsing twice. */ -export function removeAllCitations(content: string): string { - if (!content) return content; - - const parsed = parseCitations(content); +export function contentWithoutCitationsFromParsed( + parsed: ParseCitationsResult, +): string { const citationUrls = new Set(parsed.citations.map((c) => c.url)); - - // Remove markdown links that point to citation URLs; keep non-citation links const withoutLinks = parsed.cleanContent.replace( /\[([^\]]+)\]\(([^)]+)\)/g, (fullMatch, _text, url) => (citationUrls.has(url) ? "" : fullMatch), ); - return withoutLinks.replace(/\n{3,}/g, "\n\n").trim(); } + +/** + * Remove ALL citations from content (blocks, [cite-N], and citation links). + * Used for copy/download. For display you typically use parseCitations/useParsedCitations. + */ +export function removeAllCitations(content: string): string { + if (!content) return content; + return contentWithoutCitationsFromParsed(parseCitations(content)); +} diff --git a/frontend/src/core/i18n/locales/zh-CN.ts b/frontend/src/core/i18n/locales/zh-CN.ts index 6f10f68..faa0210 100644 --- a/frontend/src/core/i18n/locales/zh-CN.ts +++ b/frontend/src/core/i18n/locales/zh-CN.ts @@ -77,7 +77,7 @@ export const zhCN: Translations = { reasoningModeDescription: "思考后再行动,在时间与准确性之间取得平衡", proMode: "Pro", proModeDescription: "思考、计划再执行,获得更精准的结果,可能需要更多时间", - ultraMode: "Ultra", + ultraMode: "超级", ultraModeDescription: "思考、计划并执行,可调用子代理分工协作,适合复杂多步骤任务,能力最强", searchModels: "搜索模型...",