feat: Enhance model tags and icons with new reasoning and visual styles

This commit is contained in:
kangfenmao 2025-02-05 20:00:18 +08:00
parent a27150c154
commit 9aa829e6fc
17 changed files with 163 additions and 88 deletions

View File

@ -1,91 +1,96 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4753420 */ font-family: 'iconfont'; /* Project id 4753420 */
src: url('iconfont.woff2?t=1736309723926') format('woff2'), src: url('iconfont.woff2?t=1738750230250') format('woff2');
url('iconfont.woff?t=1736309723926') format('woff'),
url('iconfont.ttf?t=1736309723926') format('truetype');
} }
.iconfont { .iconfont {
font-family: "iconfont" !important; font-family: 'iconfont' !important;
font-size: 16px; font-size: 16px;
font-style: normal; font-style: normal;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-thinking:before {
content: '\e65b';
}
.icon-at:before { .icon-at:before {
content: "\e623"; content: '\e623';
} }
.icon-icon-adaptive-width:before { .icon-icon-adaptive-width:before {
content: "\e87a"; content: '\e87a';
}
.icon-at1:before {
content: '\e630';
} }
.icon-a-darkmode:before { .icon-a-darkmode:before {
content: "\e6cd"; content: '\e6cd';
} }
.icon-ai-model:before { .icon-ai-model:before {
content: "\e827"; content: '\e827';
} }
.icon-ai-model1:before { .icon-ai-model1:before {
content: "\ec09"; content: '\ec09';
} }
.icon-gridlines:before { .icon-gridlines:before {
content: "\e942"; content: '\e942';
} }
.icon-inbox:before { .icon-inbox:before {
content: "\e869"; content: '\e869';
} }
.icon-business-smart-assistant:before { .icon-business-smart-assistant:before {
content: "\e601"; content: '\e601';
} }
.icon-copy:before { .icon-copy:before {
content: "\e6ae"; content: '\e6ae';
} }
.icon-ic_send:before { .icon-ic_send:before {
content: "\e795"; content: '\e795';
} }
.icon-dark1:before { .icon-dark1:before {
content: "\e72f"; content: '\e72f';
} }
.icon-theme-light:before { .icon-theme-light:before {
content: "\e6b7"; content: '\e6b7';
} }
.icon-translate_line:before { .icon-translate_line:before {
content: "\e7de"; content: '\e7de';
} }
.icon-history:before { .icon-history:before {
content: "\e758"; content: '\e758';
} }
.icon-hide-sidebar:before { .icon-hide-sidebar:before {
content: "\e8eb"; content: '\e8eb';
} }
.icon-show-sidebar:before { .icon-show-sidebar:before {
content: "\e944"; content: '\e944';
} }
.icon-appstore:before { .icon-appstore:before {
content: "\e792"; content: '\e792';
} }
.icon-chat:before { .icon-chat:before {
content: "\e615"; content: '\e615';
} }
.icon-setting:before { .icon-setting:before {
content: "\e78e"; content: '\e78e';
} }

View File

@ -0,0 +1,24 @@
import React, { FC } from 'react'
import styled from 'styled-components'
const ReasoningIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>> = (props) => {
return (
<Container>
<Icon className="iconfont icon-thinking" {...(props as any)} />
</Container>
)
}
const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
`
const Icon = styled.i`
color: var(--color-link);
font-size: 16px;
margin-right: 6px;
`
export default ReasoningIcon

View File

@ -1,15 +1,25 @@
import { EyeOutlined } from '@ant-design/icons' import { PictureOutlined } from '@ant-design/icons'
import React, { FC } from 'react' import React, { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
const VisionIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>> = (props) => { const VisionIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>> = (props) => {
return <Icon {...(props as any)} /> return (
<Container>
<Icon {...(props as any)} />
</Container>
)
} }
const Icon = styled(EyeOutlined)` const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
`
const Icon = styled(PictureOutlined)`
color: var(--color-primary); color: var(--color-primary);
font-size: 14px; font-size: 15px;
margin-left: 4px; margin-right: 6px;
` `
export default VisionIcon export default VisionIcon

View File

@ -3,13 +3,23 @@ import React, { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
const WebSearchIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>> = (props) => { const WebSearchIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>> = (props) => {
return <Icon {...(props as any)} /> return (
<Container>
<Icon {...(props as any)} />
</Container>
)
} }
const Container = styled.div`
display: flex;
justify-content: center;
align-items: center;
`
const Icon = styled(GlobalOutlined)` const Icon = styled(GlobalOutlined)`
color: var(--color-link); color: var(--color-link);
font-size: 12px; font-size: 15px;
margin-left: 4px; margin-right: 6px;
` `
export default WebSearchIcon export default WebSearchIcon

View File

@ -4,38 +4,37 @@ import { isFreeModel } from '@renderer/utils'
import { Tag } from 'antd' import { Tag } from 'antd'
import { FC } from 'react' import { FC } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import ReasoningIcon from './Icons/ReasoningIcon'
import VisionIcon from './Icons/VisionIcon' import VisionIcon from './Icons/VisionIcon'
import WebSearchIcon from './Icons/WebSearchIcon' import WebSearchIcon from './Icons/WebSearchIcon'
interface ModelTagsProps { interface ModelTagsProps {
model: Model model: Model
showFree?: boolean showFree?: boolean
showReasoning?: boolean
} }
const ModelTags: FC<ModelTagsProps> = ({ model, showFree = true }) => { const ModelTags: FC<ModelTagsProps> = ({ model, showFree = true, showReasoning = true }) => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<> <Container>
{isVisionModel(model) && <VisionIcon />} {isVisionModel(model) && <VisionIcon />}
{isWebSearchModel(model) && <WebSearchIcon />} {isWebSearchModel(model) && <WebSearchIcon />}
{showFree && isFreeModel(model) && ( {showReasoning && isReasoningModel(model) && <ReasoningIcon />}
<Tag style={{ marginLeft: 10 }} color="green"> {isEmbeddingModel(model) && <Tag color="orange">{t('models.embedding')}</Tag>}
{t('models.free')} {showFree && isFreeModel(model) && <Tag color="green">{t('models.free')}</Tag>}
</Tag> </Container>
)}
{isEmbeddingModel(model) && (
<Tag style={{ marginLeft: 10 }} color="orange">
{t('models.embedding')}
</Tag>
)}
{isReasoningModel(model) && (
<Tag color="blue" style={{ marginLeft: 10 }}>
{t('models.reasoning')}
</Tag>
)}
</>
) )
} }
const Container = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 2px;
`
export default ModelTags export default ModelTags

View File

@ -74,9 +74,9 @@ const PopupContainer: React.FC<PopupContainerProps> = ({ model, resolve }) => {
key: getModelUniqId(m), key: getModelUniqId(m),
label: ( label: (
<ModelItem> <ModelItem>
<span> <ModelNameRow>
{m?.name} <ModelTags model={m} /> <span>{m?.name}</span> <ModelTags model={m} />
</span> </ModelNameRow>
<PinIcon <PinIcon
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
@ -118,7 +118,9 @@ const PopupContainer: React.FC<PopupContainerProps> = ({ model, resolve }) => {
key: getModelUniqId(m) + '_pinned', key: getModelUniqId(m) + '_pinned',
label: ( label: (
<ModelItem> <ModelItem>
{m?.name} <ModelTags model={m} /> <ModelNameRow>
<span>{m?.name}</span> <ModelTags model={m} />
</ModelNameRow>
<PinIcon <PinIcon
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
@ -277,6 +279,13 @@ const ModelItem = styled.div`
width: 100%; width: 100%;
` `
const ModelNameRow = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
`
const EmptyState = styled.div` const EmptyState = styled.div`
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -629,12 +629,12 @@
"reasoning": "推論" "reasoning": "推論"
}, },
"all": "すべて", "all": "すべて",
"vision": "画像モデル", "vision": "画像",
"websearch": "ウェブ検索モデル", "websearch": "ウェブ検索",
"free": "無料モデル", "free": "無料",
"reasoning": "推論モデル", "reasoning": "推論",
"embedding": "埋め込みモデル", "embedding": "埋め込み",
"embedding_model": "埋め込みモデル", "embedding_model": "埋め込み模型",
"embedding_model_tooltip": "設定->モデルサービス->管理で追加", "embedding_model_tooltip": "設定->モデルサービス->管理で追加",
"dimensions": "{{dimensions}} 次元", "dimensions": "{{dimensions}} 次元",
"custom_parameters": "カスタムパラメータ", "custom_parameters": "カスタムパラメータ",

View File

@ -641,11 +641,11 @@
"reasoning": "Рассуждение" "reasoning": "Рассуждение"
}, },
"all": "Все", "all": "Все",
"vision": "Визуальные модели", "vision": "Визуальные",
"websearch": "Веб-поисковые модели", "websearch": "Веб-поисковые",
"free": "Бесплатные модели", "free": "Бесплатные",
"reasoning": "Модели рассуждения", "reasoning": "Рассуждение",
"embedding": "Встраиваемые модели", "embedding": "Встраиваемые",
"embedding_model": "Встраиваемые модели", "embedding_model": "Встраиваемые модели",
"embedding_model_tooltip": "Добавьте в настройки->модель сервиса->управление", "embedding_model_tooltip": "Добавьте в настройки->модель сервиса->управление",
"dimensions": "{{dimensions}} мер", "dimensions": "{{dimensions}} мер",

View File

@ -494,7 +494,7 @@
"delete.title": "删除提供商", "delete.title": "删除提供商",
"docs_check": "查看", "docs_check": "查看",
"docs_more_details": "获取更多详情", "docs_more_details": "获取更多详情",
"get_api_key": "获取密钥", "get_api_key": "点击这里获取密钥",
"no_models": "请先添加模型再检查 API 连接", "no_models": "请先添加模型再检查 API 连接",
"not_checked": "未检查", "not_checked": "未检查",
"remove_duplicate_keys": "移除重复密钥", "remove_duplicate_keys": "移除重复密钥",
@ -636,11 +636,11 @@
"reasoning": "推理" "reasoning": "推理"
}, },
"all": "全部", "all": "全部",
"vision": "视觉模型", "vision": "视觉",
"websearch": "联网模型", "websearch": "联网",
"free": "免费模型", "free": "免费",
"reasoning": "推理模型", "reasoning": "推理",
"embedding": "嵌入模型", "embedding": "嵌入",
"embedding_model": "嵌入模型", "embedding_model": "嵌入模型",
"embedding_model_tooltip": "在设置->模型服务中点击管理按钮添加", "embedding_model_tooltip": "在设置->模型服务中点击管理按钮添加",
"dimensions": "{{dimensions}} 维", "dimensions": "{{dimensions}} 维",

View File

@ -493,7 +493,7 @@
"delete.title": "刪除提供者", "delete.title": "刪除提供者",
"docs_check": "檢查", "docs_check": "檢查",
"docs_more_details": "查看更多細節", "docs_more_details": "查看更多細節",
"get_api_key": "獲取密鑰", "get_api_key": "點擊這裡獲取密鑰",
"no_models": "請先添加模型再檢查 API 連接", "no_models": "請先添加模型再檢查 API 連接",
"not_checked": "未檢查", "not_checked": "未檢查",
"remove_duplicate_keys": "移除重複密鑰", "remove_duplicate_keys": "移除重複密鑰",
@ -635,11 +635,11 @@
"reasoning": "推理" "reasoning": "推理"
}, },
"all": "全部", "all": "全部",
"vision": "視覺模型", "vision": "視覺",
"websearch": "網路搜索模型", "websearch": "網路搜索",
"free": "免費模型", "free": "免費",
"reasoning": "推理模型", "reasoning": "推理",
"embedding": "嵌入模型", "embedding": "嵌入",
"embedding_model": "嵌入模型", "embedding_model": "嵌入模型",
"embedding_model_tooltip": "在设置->模型服务中点击管理按钮添加", "embedding_model_tooltip": "在设置->模型服务中点击管理按钮添加",
"dimensions": "{{dimensions}} 維", "dimensions": "{{dimensions}} 維",

View File

@ -48,9 +48,9 @@ const MentionModelsButton: FC<Props> = ({ onMentionModel: onSelect, ToolbarButto
key: getModelUniqId(m), key: getModelUniqId(m),
label: ( label: (
<ModelItem> <ModelItem>
<span> <ModelNameRow>
{m?.name} <ModelTags model={m} /> <span>{m?.name}</span> <ModelTags model={m} />
</span> </ModelNameRow>
{/* <Checkbox checked={selectedModels.some((sm) => sm.id === m.id)} /> */} {/* <Checkbox checked={selectedModels.some((sm) => sm.id === m.id)} /> */}
<PinIcon <PinIcon
onClick={(e) => { onClick={(e) => {
@ -136,6 +136,13 @@ const ModelItem = styled.div`
} }
` `
const ModelNameRow = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
`
const PinIcon = styled.span.attrs({ className: 'pin-icon' })<{ $isPinned: boolean }>` const PinIcon = styled.span.attrs({ className: 'pin-icon' })<{ $isPinned: boolean }>`
margin-left: auto; margin-left: auto;
padding: 0 8px; padding: 0 8px;

View File

@ -39,7 +39,7 @@ const SelectModelButton: FC<Props> = ({ assistant }) => {
<ModelName> <ModelName>
{model ? model.name : t('button.select_model')} {providerName ? '| ' + providerName : ''} {model ? model.name : t('button.select_model')} {providerName ? '| ' + providerName : ''}
</ModelName> </ModelName>
<ModelTags model={model} showFree={false} /> <ModelTags model={model} showFree={false} showReasoning={false} />
</ButtonContent> </ButtonContent>
</DropdownButton> </DropdownButton>
) )

View File

@ -237,6 +237,10 @@ const ListItemHeader = styled.div`
` `
const ListItemName = styled.div` const ListItemName = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
color: var(--color-text); color: var(--color-text);
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
@ -252,7 +256,6 @@ const ModelHeaderTitle = styled.div`
const Question = styled(QuestionCircleOutlined)` const Question = styled(QuestionCircleOutlined)`
cursor: pointer; cursor: pointer;
margin: 0 10px;
color: #888; color: #888;
` `

View File

@ -309,8 +309,10 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
<Avatar src={getModelLogo(model.id)} size={22} style={{ marginRight: '8px' }}> <Avatar src={getModelLogo(model.id)} size={22} style={{ marginRight: '8px' }}>
{model?.name?.[0]?.toUpperCase()} {model?.name?.[0]?.toUpperCase()}
</Avatar> </Avatar>
{model?.name} <ModelNameRow>
<span>{model?.name}</span>
<ModelTags model={model} /> <ModelTags model={model} />
</ModelNameRow>
<Popover content={modelTypeContent(model)} title={t('models.type.select')} trigger="click"> <Popover content={modelTypeContent(model)} title={t('models.type.select')} trigger="click">
<SettingIcon /> <SettingIcon />
</Popover> </Popover>
@ -360,6 +362,13 @@ const ModelListHeader = styled.div`
align-items: center; align-items: center;
` `
const ModelNameRow = styled.div`
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
`
const RemoveIcon = styled(MinusCircleOutlined)` const RemoveIcon = styled(MinusCircleOutlined)`
font-size: 18px; font-size: 18px;
margin-left: 10px; margin-left: 10px;
@ -369,7 +378,7 @@ const RemoveIcon = styled(MinusCircleOutlined)`
` `
const SettingIcon = styled(SettingOutlined)` const SettingIcon = styled(SettingOutlined)`
margin-left: 10px; margin-left: 2px;
color: var(--color-text); color: var(--color-text);
cursor: pointer; cursor: pointer;
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;

View File

@ -30,7 +30,7 @@ const persistedReducer = persistReducer(
{ {
key: 'cherry-studio', key: 'cherry-studio',
storage, storage,
version: 62, version: 63,
blacklist: ['runtime'], blacklist: ['runtime'],
migrate migrate
}, },

View File

@ -906,7 +906,6 @@ const migrateConfig = {
state.minapps.enabled.push(mintop) state.minapps.enabled.push(mintop)
} }
} }
removeMiniAppIconsFromState(state)
return state return state
} }
} }