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-family: "iconfont"; /* Project id 4753420 */
src: url('iconfont.woff2?t=1736309723926') format('woff2'),
url('iconfont.woff?t=1736309723926') format('woff'),
url('iconfont.ttf?t=1736309723926') format('truetype');
font-family: 'iconfont'; /* Project id 4753420 */
src: url('iconfont.woff2?t=1738750230250') format('woff2');
}
.iconfont {
font-family: "iconfont" !important;
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-thinking:before {
content: '\e65b';
}
.icon-at:before {
content: "\e623";
content: '\e623';
}
.icon-icon-adaptive-width:before {
content: "\e87a";
content: '\e87a';
}
.icon-at1:before {
content: '\e630';
}
.icon-a-darkmode:before {
content: "\e6cd";
content: '\e6cd';
}
.icon-ai-model:before {
content: "\e827";
content: '\e827';
}
.icon-ai-model1:before {
content: "\ec09";
content: '\ec09';
}
.icon-gridlines:before {
content: "\e942";
content: '\e942';
}
.icon-inbox:before {
content: "\e869";
content: '\e869';
}
.icon-business-smart-assistant:before {
content: "\e601";
content: '\e601';
}
.icon-copy:before {
content: "\e6ae";
content: '\e6ae';
}
.icon-ic_send:before {
content: "\e795";
content: '\e795';
}
.icon-dark1:before {
content: "\e72f";
content: '\e72f';
}
.icon-theme-light:before {
content: "\e6b7";
content: '\e6b7';
}
.icon-translate_line:before {
content: "\e7de";
content: '\e7de';
}
.icon-history:before {
content: "\e758";
content: '\e758';
}
.icon-hide-sidebar:before {
content: "\e8eb";
content: '\e8eb';
}
.icon-show-sidebar:before {
content: "\e944";
content: '\e944';
}
.icon-appstore:before {
content: "\e792";
content: '\e792';
}
.icon-chat:before {
content: "\e615";
content: '\e615';
}
.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 styled from 'styled-components'
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);
font-size: 14px;
margin-left: 4px;
font-size: 15px;
margin-right: 6px;
`
export default VisionIcon

View File

@ -3,13 +3,23 @@ import React, { FC } from 'react'
import styled from 'styled-components'
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)`
color: var(--color-link);
font-size: 12px;
margin-left: 4px;
font-size: 15px;
margin-right: 6px;
`
export default WebSearchIcon

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -48,9 +48,9 @@ const MentionModelsButton: FC<Props> = ({ onMentionModel: onSelect, ToolbarButto
key: getModelUniqId(m),
label: (
<ModelItem>
<span>
{m?.name} <ModelTags model={m} />
</span>
<ModelNameRow>
<span>{m?.name}</span> <ModelTags model={m} />
</ModelNameRow>
{/* <Checkbox checked={selectedModels.some((sm) => sm.id === m.id)} /> */}
<PinIcon
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 }>`
margin-left: auto;
padding: 0 8px;

View File

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

View File

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

View File

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

View File

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

View File

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