diff --git a/src/renderer/src/components/Scrollbar/index.tsx b/src/renderer/src/components/Scrollbar/index.tsx index c367b80d66..70ea3e4edb 100644 --- a/src/renderer/src/components/Scrollbar/index.tsx +++ b/src/renderer/src/components/Scrollbar/index.tsx @@ -1,49 +1,57 @@ -import { forwardRef } from 'react' +import { throttle } from 'lodash' +import { FC, forwardRef, useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' -interface Props { - children?: React.ReactNode - className?: string - $isScrolling?: boolean - $right?: boolean +interface Props extends React.HTMLAttributes { + right?: boolean + ref?: any } -const ScrollbarContainer = styled.div<{ $isScrolling?: boolean; $right?: boolean }>` - overflow-y: auto; - overflow-x: hidden; - height: 100%; +const Scrollbar: FC = forwardRef((props, ref) => { + const [isScrolling, setIsScrolling] = useState(false) + const timeoutRef = useRef(null) - &::-webkit-scrollbar { - width: 6px; - height: 6px; - } + const handleScroll = useCallback( + throttle(() => { + setIsScrolling(true) - &::-webkit-scrollbar-track { - border-radius: 3px; - background: transparent; - ${({ $right }) => $right && `margin-right: 4px;`} - } + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } - &::-webkit-scrollbar-thumb { - border-radius: 3px; - background: ${({ $isScrolling }) => - $isScrolling ? 'var(--color-scrollbar-thumb)' : 'var(--color-scrollbar-track)'}; - transition: all 0.2s ease-in-out; - } + timeoutRef.current = setTimeout(() => setIsScrolling(false), 1500) // 增加到 2 秒 + }, 200), + [] + ) - &:hover::-webkit-scrollbar-thumb { - background: var(--color-scrollbar-thumb); - } -` + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + } + }, []) -const Scrollbar = forwardRef(({ children, className, $isScrolling, $right }, ref) => { return ( - - {children} - + + {props.children} + ) }) +const Container = styled.div<{ isScrolling: boolean; right?: boolean }>` + overflow-y: auto; + &::-webkit-scrollbar-thumb { + transition: background 2s ease; + background: ${(props) => + props.isScrolling ? `var(--color-scrollbar-thumb${props.right ? '-right' : ''})` : 'transparent'}; + &:hover { + background: ${(props) => + props.isScrolling ? `var(--color-scrollbar-thumb${props.right ? '-right' : ''}-hover)` : 'transparent'}; + } + } +` + Scrollbar.displayName = 'Scrollbar' export default Scrollbar diff --git a/src/renderer/src/pages/agents/Agents.tsx b/src/renderer/src/pages/agents/Agents.tsx index 331e82d1f6..6a7d07ca1b 100644 --- a/src/renderer/src/pages/agents/Agents.tsx +++ b/src/renderer/src/pages/agents/Agents.tsx @@ -75,7 +75,7 @@ const Agents: React.FC = ({ onClick, cardStyle = 'old' }) => { ] return ( - + onClick?.(agent)} diff --git a/src/renderer/src/pages/agents/AgentsPage.tsx b/src/renderer/src/pages/agents/AgentsPage.tsx index 96a756b5d0..cbc7fa6c06 100644 --- a/src/renderer/src/pages/agents/AgentsPage.tsx +++ b/src/renderer/src/pages/agents/AgentsPage.tsx @@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next' import ReactMarkdown from 'react-markdown' import styled from 'styled-components' +import { groupTranslations } from './agentGroupTranslations' import Agents from './Agents' import AddAgentPopup from './components/AddAgentPopup' import AgentCard from './components/AgentCard' @@ -41,7 +42,7 @@ const AgentsPage: FC = () => { return _agentGroups }, []) - const { t } = useTranslation() + const { t, i18n } = useTranslation() const filteredAgentGroups = useMemo(() => { const groups = search.trim() ? {} : { 我的: [] } @@ -102,6 +103,14 @@ const AgentsPage: FC = () => { } } + const getLocalizedGroupName = useCallback( + (group: string) => { + const currentLang = i18n.language + return groupTranslations[group]?.[currentLang] || group + }, + [i18n.language] + ) + const tabItems = useMemo(() => { let groups = Object.keys(filteredAgentGroups) groups = groups.filter((g) => g !== '我的' && g !== '办公') @@ -109,25 +118,27 @@ const AgentsPage: FC = () => { return groups.map((group, i) => { const id = String(i + 1) + const localizedGroupName = getLocalizedGroupName(group) + return { - label: group, + label: localizedGroupName, key: id, children: ( - {group} + {localizedGroupName} - + {group === '我的' ? ( <> - + AddAgentPopup.show()} /> ) : ( filteredAgentGroups[group]?.map((agent, index) => ( - + onAddAgentConfirm(getAgentFromSystemAgent(agent))} agent={agent as any} /> )) @@ -137,7 +148,7 @@ const AgentsPage: FC = () => { ) } }) - }, [filteredAgentGroups, onAddAgentConfirm]) + }, [filteredAgentGroups, getLocalizedGroupName, onAddAgentConfirm]) return ( @@ -187,6 +198,7 @@ const ContentContainer = styled.div` flex-direction: row; justify-content: center; height: 100%; + padding: 0 10px; ` const AssistantsContainer = styled.div` @@ -200,6 +212,7 @@ const TabContent = styled(Scrollbar)` height: calc(100vh - var(--navbar-height)); padding: 10px 10px 10px 15px; margin-right: 4px; + overflow-x: hidden; ` const AgentPrompt = styled.div` @@ -215,7 +228,6 @@ const EmptyView = styled.div` align-items: center; font-size: 16px; color: var(--color-text-secondary); - border-left: 0.5px solid var(--color-border); ` const Tabs = styled(TabsAntd)` @@ -247,7 +259,7 @@ const Tabs = styled(TabsAntd)` border-right: none; } .ant-tabs-content-holder { - border-left: 0.5px solid var(--color-border); + border-left: none; border-right: 0.5px solid var(--color-border); } .ant-tabs-ink-bar { diff --git a/src/renderer/src/pages/agents/agentGroupTranslations.ts b/src/renderer/src/pages/agents/agentGroupTranslations.ts new file mode 100644 index 0000000000..d71cba4183 --- /dev/null +++ b/src/renderer/src/pages/agents/agentGroupTranslations.ts @@ -0,0 +1,180 @@ +export type GroupTranslations = { + [key: string]: { + 'en-US': string + 'zh-CN': string + 'zh-TW': string + } +} + +export const groupTranslations: GroupTranslations = { + 我的: { + 'en-US': 'My Agents', + 'zh-CN': '我的', + 'zh-TW': '我的' + }, + 职业: { + 'en-US': 'Career', + 'zh-CN': '职业', + 'zh-TW': '職業' + }, + 商业: { + 'en-US': 'Business', + 'zh-CN': '商业', + 'zh-TW': '商業' + }, + 工具: { + 'en-US': 'Tools', + 'zh-CN': '工具', + 'zh-TW': '工具' + }, + 语言: { + 'en-US': 'Language', + 'zh-CN': '语言', + 'zh-TW': '語言' + }, + 办公: { + 'en-US': 'Office', + 'zh-CN': '办公', + 'zh-TW': '辦公' + }, + 通用: { + 'en-US': 'General', + 'zh-CN': '通用', + 'zh-TW': '通用' + }, + 写作: { + 'en-US': 'Writing', + 'zh-CN': '写作', + 'zh-TW': '寫作' + }, + Artifacts: { + 'en-US': 'Artifacts', + 'zh-CN': 'Artifacts', + 'zh-TW': 'Artifacts' + }, + 编程: { + 'en-US': 'Programming', + 'zh-CN': '编程', + 'zh-TW': '編程' + }, + 情感: { + 'en-US': 'Emotion', + 'zh-CN': '情感', + 'zh-TW': '情感' + }, + 教育: { + 'en-US': 'Education', + 'zh-CN': '教育', + 'zh-TW': '教育' + }, + 创意: { + 'en-US': 'Creative', + 'zh-CN': '创意', + 'zh-TW': '創意' + }, + 学术: { + 'en-US': 'Academic', + 'zh-CN': '学术', + 'zh-TW': '學術' + }, + 设计: { + 'en-US': 'Design', + 'zh-CN': '设计', + 'zh-TW': '設計' + }, + 艺术: { + 'en-US': 'Art', + 'zh-CN': '艺术', + 'zh-TW': '藝術' + }, + 娱乐: { + 'en-US': 'Entertainment', + 'zh-CN': '娱乐', + 'zh-TW': '娛樂' + }, + 生活: { + 'en-US': 'Life', + 'zh-CN': '生活', + 'zh-TW': '生活' + }, + 医疗: { + 'en-US': 'Medical', + 'zh-CN': '医疗', + 'zh-TW': '醫療' + }, + 游戏: { + 'en-US': 'Games', + 'zh-CN': '游戏', + 'zh-TW': '遊戲' + }, + 翻译: { + 'en-US': 'Translation', + 'zh-CN': '翻译', + 'zh-TW': '翻譯' + }, + 音乐: { + 'en-US': 'Music', + 'zh-CN': '音乐', + 'zh-TW': '音樂' + }, + 点评: { + 'en-US': 'Review', + 'zh-CN': '点评', + 'zh-TW': '點評' + }, + 文案: { + 'en-US': 'Copywriting', + 'zh-CN': '文案', + 'zh-TW': '文案' + }, + 百科: { + 'en-US': 'Encyclopedia', + 'zh-CN': '百科', + 'zh-TW': '百科' + }, + 健康: { + 'en-US': 'Health', + 'zh-CN': '健康', + 'zh-TW': '健康' + }, + 营销: { + 'en-US': 'Marketing', + 'zh-CN': '营销', + 'zh-TW': '營銷' + }, + 科学: { + 'en-US': 'Science', + 'zh-CN': '科学', + 'zh-TW': '科學' + }, + 分析: { + 'en-US': 'Analysis', + 'zh-CN': '分析', + 'zh-TW': '分析' + }, + 法律: { + 'en-US': 'Legal', + 'zh-CN': '法律', + 'zh-TW': '法律' + }, + 咨询: { + 'en-US': 'Consulting', + 'zh-CN': '咨询', + 'zh-TW': '諮詢' + }, + 金融: { + 'en-US': 'Finance', + 'zh-CN': '金融', + 'zh-TW': '金融' + }, + 旅游: { + 'en-US': 'Travel', + 'zh-CN': '旅游', + 'zh-TW': '旅遊' + }, + 管理: { + 'en-US': 'Management', + 'zh-CN': '管理', + 'zh-TW': '管理' + } +} diff --git a/src/renderer/src/pages/agents/components/AgentCard.tsx b/src/renderer/src/pages/agents/components/AgentCard.tsx index 433ecaf82f..b9fd6465a1 100644 --- a/src/renderer/src/pages/agents/components/AgentCard.tsx +++ b/src/renderer/src/pages/agents/components/AgentCard.tsx @@ -1,5 +1,6 @@ import { EllipsisOutlined } from '@ant-design/icons' import { Agent } from '@renderer/types' +import { getLeadingEmoji } from '@renderer/utils' import { Dropdown } from 'antd' import styled from 'styled-components' @@ -171,10 +172,11 @@ const MenuContainer = styled.div` ` const AgentCard: React.FC = ({ agent, onClick, contextMenu, menuItems }) => { + const emoji = agent.emoji || getLeadingEmoji(agent.name) const content = ( {agent.emoji && {agent.emoji}} - {agent.emoji} + {emoji} {menuItems && ( e.stopPropagation()}> = ({ agent, onClick, contextMenu, menuItems }) )} {agent.name} - {(agent.description || agent.prompt).substring(0, 50)}... + {(agent.description || agent.prompt).substring(0, 100)}... ) diff --git a/src/renderer/src/pages/files/FilesPage.tsx b/src/renderer/src/pages/files/FilesPage.tsx index 2ed87cb4c1..5538fdab3d 100644 --- a/src/renderer/src/pages/files/FilesPage.tsx +++ b/src/renderer/src/pages/files/FilesPage.tsx @@ -153,6 +153,7 @@ const ImageWrapper = styled.div` display: flex; align-items: center; justify-content: center; + border: 0.5px solid var(--color-border); .ant-image { height: 100%;