fix: citation list loading (#5742)

* fix: citation loading

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* fix: remove unused import & cleanup some code

---------

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
This commit is contained in:
自由的世界人
2025-05-08 03:04:31 +08:00
committed by GitHub
parent a38f4a5ad3
commit 80177458b1
6 changed files with 175 additions and 68 deletions
@@ -167,7 +167,7 @@ interface ChatFlowHistoryProps {
}
// 定义节点和边的类型
type FlowNode = Node<any, string>
type FlowNode = Node<any>
type FlowEdge = Edge<any>
// 统一的边样式
@@ -5,6 +5,7 @@ import { Button, Drawer } from 'antd'
import { FileSearch } from 'lucide-react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { QueryClient, QueryClientProvider } from 'react-query'
import styled from 'styled-components'
export interface Citation {
@@ -21,6 +22,17 @@ interface CitationsListProps {
citations: Citation[]
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
cacheTime: Infinity,
refetchOnWindowFocus: false,
retry: false
}
}
})
/**
* 限制文本长度
* @param text
@@ -50,61 +62,49 @@ const CitationsList: React.FC<CitationsListProps> = ({ citations }) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const hasCitations = citations.length > 0
const count = citations.length
const previewItems = citations.slice(0, 3)
if (!hasCitations) return null
const handleOpen = () => {
setOpen(true)
}
const handleClose = () => {
setOpen(false)
}
const count = citations.length
if (!count) return null
return (
<>
<OpenButton type="text" onClick={handleOpen}>
<PreviewIcons>
{previewItems.map((c, i) => (
<PreviewIcon key={i} style={{ zIndex: previewItems.length - i }}>
{c.type === 'websearch' && c.url ? (
<Favicon hostname={new URL(c.url).hostname} alt={''} />
) : (
<FileSearch width={16} />
)}
</PreviewIcon>
))}
</PreviewIcons>
{t('message.citation', { count: count })}
</OpenButton>
<QueryClientProvider client={queryClient}>
<>
<OpenButton type="text" onClick={() => setOpen(true)}>
<PreviewIcons>
{previewItems.map((c, i) => (
<PreviewIcon key={i} style={{ zIndex: previewItems.length - i }}>
{c.type === 'websearch' && c.url ? (
<Favicon hostname={new URL(c.url).hostname} alt={c.title || ''} />
) : (
<FileSearch width={16} />
)}
</PreviewIcon>
))}
</PreviewIcons>
{t('message.citation', { count })}
</OpenButton>
<Drawer
title={t('message.citations')}
placement="right"
onClose={handleClose}
open={open}
width={680}
destroyOnClose
styles={{
body: {
padding: 16,
height: 'calc(100% - 55px)'
}
}}>
{citations.map((citation) => (
<HStack key={citation.url || citation.number} style={{ alignItems: 'center', gap: 8, marginBottom: 12 }}>
{citation.type === 'websearch' ? (
<WebSearchCitation citation={citation} />
) : (
<KnowledgeCitation citation={citation} />
)}
</HStack>
))}
</Drawer>
</>
<Drawer
title={t('message.citations')}
placement="right"
onClose={() => setOpen(false)}
open={open}
width={680}
destroyOnClose={true} // unmount on close
>
{open &&
citations.map((citation) => (
<HStack key={citation.url || citation.number} style={{ alignItems: 'center', gap: 8, marginBottom: 12 }}>
{citation.type === 'websearch' ? (
<WebSearchCitation citation={citation} />
) : (
<KnowledgeCitation citation={citation} />
)}
</HStack>
))}
</Drawer>
</>
</QueryClientProvider>
)
}
@@ -212,14 +212,14 @@ const WebSearchCard = styled.div`
padding: 12px;
margin-bottom: 8px;
border-radius: 8px;
border: 1px solid var(--color-border);
background-color: var(--color-bg-2);
border: 1px solid #e5e6eb;
background-color: #f8f9fa;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
background-color: var(--color-bg-3);
border-color: var(--color-primary-light);
background-color: #f1f3f5;
border-color: rgba(24, 144, 255, 0.1);
transform: translateY(-2px);
}
`
@@ -264,22 +264,22 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
key: 'export',
icon: <UploadOutlined />,
children: [
exportMenuOptions.image !== false && {
exportMenuOptions.image && {
label: t('chat.topics.export.image'),
key: 'image',
onClick: () => EventEmitter.emit(EVENT_NAMES.EXPORT_TOPIC_IMAGE, topic)
},
exportMenuOptions.markdown !== false && {
exportMenuOptions.markdown && {
label: t('chat.topics.export.md'),
key: 'markdown',
onClick: () => exportTopicAsMarkdown(topic)
},
exportMenuOptions.markdown_reason !== false && {
exportMenuOptions.markdown_reason && {
label: t('chat.topics.export.md.reason'),
key: 'markdown_reason',
onClick: () => exportTopicAsMarkdown(topic, true)
},
exportMenuOptions.docx !== false && {
exportMenuOptions.docx && {
label: t('chat.topics.export.word'),
key: 'word',
onClick: async () => {
@@ -287,14 +287,14 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
window.api.export.toWord(markdown, removeSpecialCharactersForFileName(topic.name))
}
},
exportMenuOptions.notion !== false && {
exportMenuOptions.notion && {
label: t('chat.topics.export.notion'),
key: 'notion',
onClick: async () => {
exportTopicToNotion(topic)
}
},
exportMenuOptions.yuque !== false && {
exportMenuOptions.yuque && {
label: t('chat.topics.export.yuque'),
key: 'yuque',
onClick: async () => {
@@ -302,7 +302,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
exportMarkdownToYuque(topic.name, markdown)
}
},
exportMenuOptions.obsidian !== false && {
exportMenuOptions.obsidian && {
label: t('chat.topics.export.obsidian'),
key: 'obsidian',
onClick: async () => {
@@ -310,7 +310,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
await ObsidianExportPopup.show({ title: topic.name, markdown, processingMethod: '3' })
}
},
exportMenuOptions.joplin !== false && {
exportMenuOptions.joplin && {
label: t('chat.topics.export.joplin'),
key: 'joplin',
onClick: async () => {
@@ -318,7 +318,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
exportMarkdownToJoplin(topic.name, markdown)
}
},
exportMenuOptions.siyuan !== false && {
exportMenuOptions.siyuan && {
label: t('chat.topics.export.siyuan'),
key: 'siyuan',
onClick: async () => {
+1 -1
View File
@@ -193,7 +193,7 @@ const formatCitationsFromBlock = (block: CitationMessageBlock | undefined): Cita
let url = result.sourceUrl
let title = result.sourceUrl
let showFavicon = true
const showFavicon = true
// 如果匹配文件链接格式 [filename](http://file/xxx)
if (fileMatch) {