feat(Messages): enhance citations display with improved styling and translation support

- Added a title for the citations list with translation using `useTranslation`.
- Introduced an `Info` icon next to the citations title.
- Updated the `CitationsContainer` styling for better visual appeal.
- Refactored citation rendering logic in `MessageContent` to streamline citation handling.
This commit is contained in:
kangfenmao 2025-04-19 17:44:44 +08:00
parent 1bb27ee3f9
commit c7ed15684a
2 changed files with 88 additions and 109 deletions

View File

@ -1,7 +1,8 @@
import Favicon from '@renderer/components/Icons/FallbackFavicon' import Favicon from '@renderer/components/Icons/FallbackFavicon'
import { HStack } from '@renderer/components/Layout' import { HStack } from '@renderer/components/Layout'
import { FileSearch } from 'lucide-react' import { FileSearch, Info } from 'lucide-react'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
interface Citation { interface Citation {
@ -19,11 +20,16 @@ interface CitationsListProps {
} }
const CitationsList: React.FC<CitationsListProps> = ({ citations }) => { const CitationsList: React.FC<CitationsListProps> = ({ citations }) => {
console.log('CitationsList', citations) const { t } = useTranslation()
if (!citations || citations.length === 0) return null if (!citations || citations.length === 0) return null
return ( return (
<CitationsContainer className="footnotes"> <CitationsContainer className="footnotes">
<CitationsTitle>
<span>{t('message.citations')}</span>
<Info size={14} style={{ opacity: 0.6 }} />
</CitationsTitle>
{citations.map((citation) => ( {citations.map((citation) => (
<HStack key={citation.url || citation.number} style={{ alignItems: 'center', gap: 8 }}> <HStack key={citation.url || citation.number} style={{ alignItems: 'center', gap: 8 }}>
<span style={{ fontSize: 13, color: 'var(--color-text-2)' }}>{citation.number}.</span> <span style={{ fontSize: 13, color: 'var(--color-text-2)' }}>{citation.number}.</span>
@ -83,7 +89,7 @@ const KnowledgeCitation: React.FC<{ citation: Citation }> = ({ citation }) => {
const CitationsContainer = styled.div` const CitationsContainer = styled.div`
background-color: rgb(242, 247, 253); background-color: rgb(242, 247, 253);
border-radius: 4px; border-radius: 10px;
padding: 8px 12px; padding: 8px 12px;
margin: 12px 0; margin: 12px 0;
display: flex; display: flex;
@ -95,6 +101,15 @@ const CitationsContainer = styled.div`
} }
` `
const CitationsTitle = styled.div`
font-weight: 500;
margin-bottom: 4px;
color: var(--color-text-1);
display: flex;
align-items: center;
gap: 6px;
`
const CitationLink = styled.a` const CitationLink = styled.a`
font-size: 14px; font-size: 14px;
line-height: 1.6; line-height: 1.6;

View File

@ -1,4 +1,4 @@
import { DownOutlined, InfoCircleOutlined, SyncOutlined, TranslationOutlined, UpOutlined } from '@ant-design/icons' import { SyncOutlined, TranslationOutlined } from '@ant-design/icons'
import { isOpenAIWebSearch } from '@renderer/config/models' import { isOpenAIWebSearch } from '@renderer/config/models'
import { getModelUniqId } from '@renderer/services/ModelService' import { getModelUniqId } from '@renderer/services/ModelService'
import { Message, Model } from '@renderer/types' import { Message, Model } from '@renderer/types'
@ -7,7 +7,7 @@ import { withMessageThought } from '@renderer/utils/formats'
import { Divider, Flex } from 'antd' import { Divider, Flex } from 'antd'
import { clone } from 'lodash' import { clone } from 'lodash'
import { Search } from 'lucide-react' import { Search } from 'lucide-react'
import React, { Fragment, useMemo, useState } from 'react' import React, { Fragment, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import BarLoader from 'react-spinners/BarLoader' import BarLoader from 'react-spinners/BarLoader'
import BeatLoader from 'react-spinners/BeatLoader' import BeatLoader from 'react-spinners/BeatLoader'
@ -30,7 +30,6 @@ const MessageContent: React.FC<Props> = ({ message: _message, model }) => {
const { t } = useTranslation() const { t } = useTranslation()
const message = withMessageThought(clone(_message)) const message = withMessageThought(clone(_message))
const isWebCitation = model && (isOpenAIWebSearch(model) || model.provider === 'openrouter') const isWebCitation = model && (isOpenAIWebSearch(model) || model.provider === 'openrouter')
const [citationsCollapsed, setCitationsCollapsed] = useState(true)
// HTML实体编码辅助函数 // HTML实体编码辅助函数
const encodeHTML = (str: string) => { const encodeHTML = (str: string) => {
@ -140,10 +139,11 @@ const MessageContent: React.FC<Props> = ({ message: _message, model }) => {
return data return data
}, [ }, [
formattedCitations, formattedCitations,
message?.metadata?.annotations, message.metadata?.annotations,
message?.metadata?.groundingMetadata?.groundingChunks, message.metadata?.groundingMetadata?.groundingChunks,
message?.metadata?.webSearch?.results, message.metadata?.knowledge,
message?.metadata?.webSearchInfo message.metadata?.webSearch?.results,
message.metadata?.webSearchInfo
]) ])
// Process content to make citation numbers clickable // Process content to make citation numbers clickable
@ -251,17 +251,7 @@ const MessageContent: React.FC<Props> = ({ message: _message, model }) => {
</Fragment> </Fragment>
)} )}
{hasCitations && ( {hasCitations && (
<CitationsContainer> <>
<CitationsHeader onClick={() => setCitationsCollapsed(!citationsCollapsed)}>
<div>
{t('message.citations')}
<InfoCircleOutlined style={{ fontSize: '14px', marginLeft: '4px', opacity: 0.6 }} />
</div>
{citationsCollapsed ? <DownOutlined /> : <UpOutlined />}
</CitationsHeader>
{!citationsCollapsed && (
<CitationsContent>
{message?.metadata?.groundingMetadata && message.status === 'success' && ( {message?.metadata?.groundingMetadata && message.status === 'success' && (
<> <>
<CitationsList <CitationsList
@ -325,9 +315,7 @@ const MessageContent: React.FC<Props> = ({ message: _message, model }) => {
}))} }))}
/> />
)} )}
</CitationsContent> </>
)}
</CitationsContainer>
)} )}
<MessageAttachments message={message} /> <MessageAttachments message={message} />
@ -335,30 +323,6 @@ const MessageContent: React.FC<Props> = ({ message: _message, model }) => {
) )
} }
const CitationsContainer = styled.div`
margin-top: 12px;
border: 1px solid var(--color-border);
border-radius: 8px;
overflow: hidden;
`
const CitationsHeader = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: var(--color-background-mute);
cursor: pointer;
&:hover {
background-color: var(--color-border);
}
`
const CitationsContent = styled.div`
padding: 10px;
background-color: var(--color-background-mute);
`
const MessageContentLoading = styled.div` const MessageContentLoading = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;