feat: enhance styling and icon consistency across components

This commit is contained in:
kangfenmao 2025-04-14 00:18:11 +08:00
parent 24e46efa0c
commit 0e8c053cee
25 changed files with 93 additions and 107 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -199,3 +199,11 @@
overflow-y: auto;
overflow-x: hidden;
}
.ant-collapse {
border: 1px solid var(--color-border);
}
.ant-collapse-content {
border-top: 1px solid var(--color-border) !important;
}

View File

@ -40,7 +40,7 @@
--color-border-soft: #ffffff10;
--color-border-mute: #ffffff05;
--color-error: #f44336;
--color-link: #1677ff;
--color-link: #338cff;
--color-code-background: #323232;
--color-hover: rgba(40, 40, 40, 1);
--color-active: rgba(55, 55, 55, 1);

View File

@ -44,7 +44,7 @@ const CustomCollapse: FC<CustomCollapseProps> = ({
borderTopRightRadius: '8px'
},
body: {
borderTop: '0.5px solid var(--color-border)'
borderTop: 'none'
}
}

View File

@ -0,0 +1,32 @@
import { getLeadingEmoji } from '@renderer/utils'
import styled from 'styled-components'
const EmojiIcon = styled.div<{ $emoji: string }>`
width: 26px;
height: 26px;
border-radius: 13px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 15px;
position: relative;
overflow: hidden;
margin-right: 3px;
&:before {
width: 100%;
height: 100%;
content: ${({ $emoji }) => `'${getLeadingEmoji($emoji || ' ')}'`};
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 200%;
transform: scale(1.5);
filter: blur(5px);
opacity: 0.4;
}
`
export default EmojiIcon

View File

@ -1,5 +1,5 @@
import { EyeOutlined } from '@ant-design/icons'
import { Tooltip } from 'antd'
import { ImageIcon } from 'lucide-react'
import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -10,7 +10,7 @@ const VisionIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>,
return (
<Container>
<Tooltip title={t('models.type.vision')} placement="top">
<Icon {...(props as any)} />
<Icon size={15} {...(props as any)} />
</Tooltip>
</Container>
)
@ -22,9 +22,8 @@ const Container = styled.div`
align-items: center;
`
const Icon = styled(EyeOutlined)`
const Icon = styled(ImageIcon)`
color: var(--color-primary);
font-size: 15px;
margin-right: 6px;
`

View File

@ -13,6 +13,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import EmojiIcon from '../EmojiIcon'
import { HStack } from '../Layout'
import Scrollbar from '../Scrollbar'
@ -186,12 +187,9 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
onClick={() => onCreateAssistant(agent)}
className={`agent-item ${agent.id === 'default' ? 'default' : ''} ${index === selectedIndex ? 'keyboard-selected' : ''}`}
onMouseEnter={() => setSelectedIndex(index)}>
<HStack
alignItems="center"
gap={5}
style={{ overflow: 'hidden', maxWidth: '100%' }}
className="text-nowrap">
{agent.emoji} {agent.name}
<HStack alignItems="center" gap={5} style={{ overflow: 'hidden', maxWidth: '100%' }}>
<EmojiIcon $emoji={agent.emoji || ''}>{agent.emoji}</EmojiIcon>
<span className="text-nowrap">{agent.name}</span>
</HStack>
{agent.id === 'default' && <Tag color="green">{t('agents.tag.system')}</Tag>}
{agent.type === 'agent' && <Tag color="orange">{t('agents.tag.agent')}</Tag>}
@ -220,13 +218,11 @@ const AgentItem = styled.div`
margin-bottom: 8px;
cursor: pointer;
overflow: hidden;
border: 1px solid transparent;
&.default {
background-color: var(--color-background-mute);
}
&.keyboard-selected {
background-color: var(--color-background-mute);
border: 1px solid var(--color-primary);
}
.anticon {
font-size: 16px;

View File

@ -12,15 +12,15 @@ import type { MenuProps } from 'antd'
import { Avatar, Dropdown, Tooltip } from 'antd'
import {
CircleHelp,
FileSearch,
Folder,
Languages,
LayoutGrid,
LibraryBig,
MessageSquareQuote,
Moon,
Palette,
Settings,
Sparkles,
Sparkle,
Sun
} from 'lucide-react'
import { FC, useEffect } from 'react'
@ -131,11 +131,11 @@ const MainMenus: FC = () => {
const iconMap = {
assistants: <MessageSquareQuote size={18} className="icon" />,
agents: <Sparkles size={18} className="icon" />,
agents: <Sparkle size={18} className="icon" />,
paintings: <Palette size={18} className="icon" />,
translate: <Languages size={18} className="icon" />,
minapp: <LayoutGrid size={18} className="icon" />,
knowledge: <LibraryBig size={18} className="icon" />,
knowledge: <FileSearch size={18} className="icon" />,
files: <Folder size={17} className="icon" />
}

View File

@ -1,7 +1,7 @@
import ZhinaoProviderLogo from '@renderer/assets/images/models/360.png'
import HunyuanProviderLogo from '@renderer/assets/images/models/hunyuan.png'
import AzureProviderLogo from '@renderer/assets/images/models/microsoft.png'
import AiHubMixProviderLogo from '@renderer/assets/images/providers/aihubmix.jpg'
import AiHubMixProviderLogo from '@renderer/assets/images/providers/aihubmix.webp'
import AlayaNewProviderLogo from '@renderer/assets/images/providers/alayanew.webp'
import AnthropicProviderLogo from '@renderer/assets/images/providers/anthropic.png'
import BaichuanProviderLogo from '@renderer/assets/images/providers/baichuan.png'

View File

@ -33,11 +33,10 @@ const AntdProvider: FC<PropsWithChildren> = ({ children }) => {
boxShadowSecondary: 'none',
defaultShadow: 'none',
dangerShadow: 'none',
primaryShadow: 'none',
borderRadius: 20
primaryShadow: 'none'
},
Select: {
borderRadius: 20
Collapse: {
headerBg: 'transparent'
}
},
token: {

View File

@ -33,7 +33,7 @@
},
"assistants": {
"title": "Assistants",
"abbr": "Assistant",
"abbr": "Assistants",
"settings.title": "Assistant Settings",
"clear.content": "Clearing the topic will delete all topics and files in the assistant. Are you sure you want to continue?",
"clear.title": "Clear topics",

View File

@ -932,7 +932,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
<ToolbarMenu>
<Tooltip placement="top" title={t('chat.input.new_topic', { Command: newTopicShortcut })} arrow>
<ToolbarButton type="text" onClick={addNewTopic}>
<MessageSquareDiff size={18} />
<MessageSquareDiff size={19} />
</ToolbarButton>
</Tooltip>
<AttachmentButton

View File

@ -4,7 +4,7 @@ import { QuickPanelListItem, useQuickPanel } from '@renderer/components/QuickPan
import { useAppSelector } from '@renderer/store'
import { KnowledgeBase } from '@renderer/types'
import { Tooltip } from 'antd'
import { LibraryBig } from 'lucide-react'
import { FileSearch } from 'lucide-react'
import { FC, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router'
@ -89,7 +89,7 @@ const KnowledgeBaseButton: FC<Props> = ({ ref, selectedBases, onSelect, disabled
return (
<Tooltip placement="top" title={t('chat.input.knowledge_base')} arrow>
<ToolbarButton type="text" onClick={handleOpenQuickPanel} disabled={disabled}>
<LibraryBig size={18} />
<FileSearch size={18} />
</ToolbarButton>
</Tooltip>
)

View File

@ -34,7 +34,7 @@ const CitationsList: React.FC<CitationsListProps> = ({ citations }) => {
{citation.showFavicon && citation.url && (
<Favicon hostname={new URL(citation.url).hostname} alt={citation.title || citation.hostname || ''} />
)}
<CitationLink href={citation.url} target="_blank" rel="noopener noreferrer">
<CitationLink href={citation.url} className="text-nowrap" target="_blank" rel="noopener noreferrer">
{citation.title ? citation.title : <span className="hostname">{citation.hostname}</span>}
</CitationLink>
</HStack>

View File

@ -263,7 +263,7 @@ const ToolResponseContainer = styled.div`
padding: 12px 16px;
overflow: auto;
max-height: 300px;
border-top: 1px solid var(--color-border);
border-top: none;
position: relative;
`

View File

@ -32,10 +32,9 @@ const Prompt: FC<Props> = ({ assistant, topic }) => {
const Container = styled.div<{ $isDark: boolean }>`
padding: 10px 20px;
margin: 5px 20px 0 20px;
border-radius: 6px;
border-radius: 10px;
cursor: pointer;
border: 0.5px solid var(--color-border);
background-color: ${({ $isDark }) => ($isDark ? 'var(--color-background-opacity)' : 'transparent')};
border: 1px solid var(--color-border);
`
const Text = styled.div`

View File

@ -8,6 +8,7 @@ import {
SortDescendingOutlined
} from '@ant-design/icons'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import EmojiIcon from '@renderer/components/EmojiIcon'
import CopyIcon from '@renderer/components/Icons/CopyIcon'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useAssistants } from '@renderer/hooks/useAssistant'
@ -215,11 +216,11 @@ const AssistantItem: FC<AssistantItemProps> = ({ assistant, isActive, onSwitch,
/>
) : (
assistantIconType === 'emoji' && (
<AssistantEmoji
<EmojiIcon
$emoji={assistant.emoji || assistantName.slice(0, 1)}
className={isPending && !isActive ? 'animation-pulse' : ''}>
{assistant.emoji || assistantName.slice(0, 1)}
</AssistantEmoji>
</EmojiIcon>
)
)}
<AssistantName className="text-nowrap">{assistantName}</AssistantName>
@ -270,34 +271,6 @@ const AssistantNameRow = styled.div`
gap: 8px;
`
const AssistantEmoji = styled.div<{ $emoji: string }>`
width: 26px;
height: 26px;
border-radius: 13px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 15px;
position: relative;
overflow: hidden;
margin-right: 3px;
&:before {
width: 100%;
height: 100%;
content: ${({ $emoji }) => `'${$emoji || ' '}'`};
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 200%;
transform: scale(1.5);
filter: blur(5px);
opacity: 0.4;
}
`
const AssistantName = styled.div`
font-size: 13px;
`

View File

@ -184,6 +184,9 @@ const Segmented = styled(AntSegmented)`
font-size: 13px;
height: 100%;
}
.ant-segmented-item-label[aria-selected='true'] {
color: var(--color-text);
}
.iconfont {
font-size: 13px;
margin-left: -2px;
@ -204,6 +207,11 @@ const Segmented = styled(AntSegmented)`
border-radius: var(--list-item-border-radius);
box-shadow: none;
}
.ant-segmented-item-label,
.ant-segmented-item-icon {
display: flex;
align-items: center;
}
/* These styles ensure the same appearance as before */
border-radius: 0;
box-shadow: none;

View File

@ -1,5 +1,4 @@
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import ModelTags from '@renderer/components/ModelTags'
import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup'
import { isLocalAi } from '@renderer/config/env'
import { useAssistant } from '@renderer/hooks/useAssistant'
@ -33,13 +32,12 @@ const SelectModelButton: FC<Props> = ({ assistant }) => {
const providerName = getProviderName(model?.provider)
return (
<DropdownButton size="small" type="default" onClick={onSelectModel}>
<DropdownButton size="small" type="text" onClick={onSelectModel}>
<ButtonContent>
<ModelAvatar model={model} size={20} />
<ModelName>
{model ? model.name : t('button.select_model')} {providerName ? '| ' + providerName : ''}
</ModelName>
<ModelTags model={model} showFree={false} showReasoning={false} showToolsCalling={false} />
</ButtonContent>
</DropdownButton>
)

View File

@ -13,7 +13,7 @@ import { formatFileSize } from '@renderer/utils'
import { bookExts, documentExts, textExts, thirdPartyApplicationExts } from '@shared/config/constant'
import { Alert, Button, Dropdown, Empty, message, Tag, Tooltip, Upload } from 'antd'
import dayjs from 'dayjs'
import { ChevronsDown, ChevronsUp, Plus, Search, Settings2 } from 'lucide-react'
import { ChevronsDown, ChevronsUp, Plus, Settings2 } from 'lucide-react'
import VirtualList from 'rc-virtual-list'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -21,7 +21,6 @@ import styled from 'styled-components'
import CustomCollapse from '../../components/CustomCollapse'
import FileItem from '../files/FileItem'
import KnowledgeSearchPopup from './components/KnowledgeSearchPopup'
import KnowledgeSettingsPopup from './components/KnowledgeSettingsPopup'
import StatusIcon from './components/StatusIcon'
@ -58,7 +57,6 @@ const KnowledgeContent: FC<KnowledgeContentProps> = ({ selectedBase }) => {
} = useKnowledge(selectedBase.id || '')
const providerName = getProviderName(base?.model.provider || '')
const rerankModelProviderName = getProviderName(base?.rerankModel?.provider || '')
const disabled = !base?.version || !providerName
if (!base) {
@ -239,7 +237,7 @@ const KnowledgeContent: FC<KnowledgeContentProps> = ({ selectedBase }) => {
</div>
<Tooltip title={providerName} placement="bottom">
<div className="tag-column">
<Tag color="geekblue" style={{ borderRadius: 20, margin: 0 }}>
<Tag color="green" style={{ borderRadius: 20, margin: 0 }}>
{base.model.name}
</Tag>
</div>
@ -248,30 +246,8 @@ const KnowledgeContent: FC<KnowledgeContentProps> = ({ selectedBase }) => {
{t('models.dimensions', { dimensions: base.dimensions || 0 })}
</Tag>
</div>
{base.rerankModel && (
<div className="model-row">
<div className="label-column">
<label>{t('models.rerank_model')}</label>
</div>
<Tooltip title={rerankModelProviderName} placement="bottom">
<div className="tag-column">
<Tag color="green" style={{ borderRadius: 20, margin: 0 }}>
{base.rerankModel?.name}
</Tag>
</div>
</Tooltip>
</div>
)}
</ModelInfo>
<HStack gap={8} alignItems="center">
<Button
size="small"
shape="round"
onClick={() => KnowledgeSearchPopup.show({ base })}
icon={<Search size={14} />}
disabled={disabled}>
{t('knowledge.search')}
</Button>
<Tooltip title={expandAll ? t('common.collapse') : t('common.expand')}>
<Button
size="small"

View File

@ -1,10 +1,7 @@
import {
CloudSyncOutlined,
DatabaseOutlined,
FileMarkdownOutlined,
FileSearchOutlined,
FolderOpenOutlined,
MenuOutlined,
SaveOutlined,
YuqueOutlined
} from '@ant-design/icons'
@ -20,6 +17,7 @@ import { reset } from '@renderer/services/BackupService'
import { AppInfo } from '@renderer/types'
import { formatFileSize } from '@renderer/utils'
import { Button, Typography } from 'antd'
import { FileText, FolderCog, FolderInput } from 'lucide-react'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -44,7 +42,7 @@ const DataSettings: FC = () => {
//joplin icon needs to be updated into iconfont
const JoplinIcon = () => (
<svg viewBox="0 0 24 24" width="16" height="16" fill="grey" xmlns="http://www.w3.org/2000/svg">
<svg viewBox="0 0 24 24" width="16" height="16" fill="var(--color-icon)" xmlns="http://www.w3.org/2000/svg">
<path d="M20.97 0h-8.9a.15.15 0 00-.16.15v2.83c0 .1.08.17.18.17h1.22c.49 0 .89.38.93.86V17.4l-.01.36-.05.29-.04.13a2.06 2.06 0 01-.38.7l-.02.03a2.08 2.08 0 01-.37.34c-.5.35-1.17.5-1.92.43a4.66 4.66 0 01-2.67-1.22 3.96 3.96 0 01-1.34-2.42c-.1-.78.14-1.47.65-1.93l.07-.05c.37-.31.84-.5 1.39-.55a.09.09 0 00.01 0l.3-.01.35.01h.02a4.39 4.39 0 011.5.44c.15.08.17 0 .18-.06V9.63a.26.26 0 00-.2-.26 7.5 7.5 0 00-6.76 1.61 6.37 6.37 0 00-2.03 5.5 8.18 8.18 0 002.71 5.08A9.35 9.35 0 0011.81 24c1.88 0 3.62-.64 4.9-1.81a6.32 6.32 0 002.06-4.3l.01-10.86V4.08a.95.95 0 01.95-.93h1.22a.17.17 0 00.17-.17V.15a.15.15 0 00-.15-.15z" />
</svg>
)
@ -67,7 +65,7 @@ const DataSettings: FC = () => {
const menuItems = [
{ key: 'divider_0', isDivider: true, text: t('settings.data.divider.basic') },
{ key: 'data', title: 'settings.data.data.title', icon: <DatabaseOutlined style={{ fontSize: 16 }} /> },
{ key: 'data', title: 'settings.data.data.title', icon: <FolderCog size={16} /> },
{ key: 'divider_1', isDivider: true, text: t('settings.data.divider.cloud_storage') },
{ key: 'webdav', title: 'settings.data.webdav.title', icon: <CloudSyncOutlined style={{ fontSize: 16 }} /> },
{ key: 'nutstore', title: 'settings.data.nutstore.title', icon: <NutstoreIcon /> },
@ -75,12 +73,12 @@ const DataSettings: FC = () => {
{
key: 'export_menu',
title: 'settings.data.export_menu.title',
icon: <MenuOutlined style={{ fontSize: 16 }} />
icon: <FolderInput size={16} />
},
{
key: 'markdown_export',
title: 'settings.data.markdown_export.title',
icon: <FileMarkdownOutlined style={{ fontSize: 16 }} />
icon: <FileText size={16} />
},
{ key: 'divider_3', isDivider: true, text: t('settings.data.divider.third_party') },
{ key: 'notion', title: 'settings.data.notion.title', icon: <i className="iconfont icon-notion" /> },

View File

@ -5,8 +5,7 @@ import {
LoadingOutlined,
MinusCircleOutlined,
MinusOutlined,
PlusOutlined,
SettingOutlined
PlusOutlined
} from '@ant-design/icons'
import CustomCollapse from '@renderer/components/CustomCollapse'
import { HStack } from '@renderer/components/Layout'
@ -22,7 +21,7 @@ import { Model } from '@renderer/types'
import { maskApiKey } from '@renderer/utils/api'
import { Avatar, Button, Flex, Tooltip, Typography } from 'antd'
import { groupBy, sortBy, toPairs } from 'lodash'
import { ListCheck } from 'lucide-react'
import { Bolt, ListCheck } from 'lucide-react'
import React, { memo, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -302,7 +301,7 @@ const ModelList: React.FC<ModelListProps> = ({ providerId, modelStatuses = [], s
type="text"
onClick={() => !isChecking && onEditModel(model)}
disabled={isChecking}
icon={<SettingOutlined />}
icon={<Bolt size={16} />}
/>
<Button
type="text"

View File

@ -1,4 +1,4 @@
import { CheckOutlined, LoadingOutlined, SettingOutlined } from '@ant-design/icons'
import { CheckOutlined, LoadingOutlined } from '@ant-design/icons'
import { StreamlineGoodHealthAndWellBeing } from '@renderer/components/Icons/SVGIcon'
import { HStack } from '@renderer/components/Layout'
import OAuthButton from '@renderer/components/OAuth/OAuthButton'
@ -17,7 +17,7 @@ import { providerCharge } from '@renderer/utils/oauth'
import { Button, Divider, Flex, Input, Space, Switch, Tooltip } from 'antd'
import Link from 'antd/es/typography/Link'
import { debounce, isEmpty } from 'lodash'
import { SquareArrowOutUpRight } from 'lucide-react'
import { Settings, SquareArrowOutUpRight } from 'lucide-react'
import { FC, useCallback, useDeferredValue, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -285,9 +285,10 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
</Link>
)}
{!provider.isSystem && (
<SettingOutlined
<Settings
type="text"
style={{ width: 30 }}
size={16}
style={{ cursor: 'pointer' }}
onClick={() => ProviderSettingsPopup.show({ provider })}
/>
)}

View File

@ -12,7 +12,7 @@ export function getDefaultAssistant(): Assistant {
return {
id: 'default',
name: i18n.t('chat.default.name'),
emoji: '⭐️',
emoji: '😀',
prompt: '',
topics: [getDefaultTopic('default')],
messages: [],