feat: Add assistant icon display toggle in settings

- Implement new setting to show/hide model icons in assistant list
- Add localization support for new assistant settings
- Update UI to conditionally render model avatar in AssistantItem
- Modify settings store to include showAssistantIcon state
- Enhance display settings with new toggle switch for assistant icon
This commit is contained in:
kangfenmao 2025-03-01 00:18:47 +08:00
parent ac92f1a783
commit 8e1207c2a2
13 changed files with 90 additions and 26 deletions

View File

@ -2,6 +2,7 @@ import store, { useAppDispatch, useAppSelector } from '@renderer/store'
import {
SendMessageShortcut,
setSendMessageShortcut as _setSendMessageShortcut,
setShowAssistantIcon,
setSidebarIcons,
setTargetLanguage,
setTheme,
@ -45,6 +46,9 @@ export function useSettings() {
},
updateSidebarDisabledIcons(icons: SidebarIcon[]) {
dispatch(setSidebarIcons({ disabled: icons }))
},
setShowAssistantIcon(showAssistantIcon: boolean) {
dispatch(setShowAssistantIcon(showAssistantIcon))
}
}
}

View File

@ -683,6 +683,7 @@
"display.sidebar.visible": "Show icons",
"display.title": "Display Settings",
"display.topic.title": "Topic Settings",
"display.assistant.title": "Assistant Settings",
"font_size.title": "Message font size",
"general": "General Settings",
"general.backup.button": "Backup",
@ -824,6 +825,7 @@
"topic.position.left": "Left",
"topic.position.right": "Right",
"topic.show.time": "Show topic time",
"assistant.show.icon": "Show model icon",
"tray.title": "Enable System Tray Icon",
"websearch": {
"get_api_key": "Get API Key",

View File

@ -683,6 +683,7 @@
"display.sidebar.visible": "アイコンを表示",
"display.title": "表示設定",
"display.topic.title": "トピック設定",
"display.assistant.title": "アシスタント設定",
"font_size.title": "メッセージのフォントサイズ",
"general": "一般設定",
"general.backup.button": "バックアップ",
@ -813,8 +814,8 @@
"zoom_reset": "ズームをリセット"
},
"theme.auto": "自動",
"theme.dark": "ダークテーマ",
"theme.light": "ライトテーマ",
"theme.dark": "ダーク",
"theme.light": "ライト",
"theme.title": "テーマ",
"theme.window.style.opaque": "不透明ウィンドウ",
"theme.window.style.title": "ウィンドウスタイル",
@ -824,6 +825,7 @@
"topic.position.left": "左",
"topic.position.right": "右",
"topic.show.time": "トピックの時間を表示",
"assistant.show.icon": "モデルアイコンを表示",
"tray.title": "システムトレイアイコンを有効にする",
"websearch": {
"get_api_key": "APIキーを取得",

View File

@ -683,6 +683,7 @@
"display.sidebar.visible": "Показывать иконки",
"display.title": "Настройки отображения",
"display.topic.title": "Настройки топиков",
"display.assistant.title": "Настройки ассистентов",
"font_size.title": "Размер шрифта сообщений",
"general": "Общие настройки",
"general.backup.button": "Резервное копирование",
@ -824,6 +825,7 @@
"topic.position.left": "Слева",
"topic.position.right": "Справа",
"topic.show.time": "Показывать время топика",
"assistant.show.icon": "Показывать модельный иконки",
"tray.title": "Включить значок системного трея",
"websearch": {
"get_api_key": "Получить ключ API",

View File

@ -683,6 +683,7 @@
"display.sidebar.visible": "显示的图标",
"display.title": "显示设置",
"display.topic.title": "话题设置",
"display.assistant.title": "助手设置",
"font_size.title": "消息字体大小",
"general": "常规设置",
"general.backup.button": "备份",
@ -812,9 +813,9 @@
"zoom_out": "缩小界面",
"zoom_reset": "重置缩放"
},
"theme.auto": "跟随系统",
"theme.dark": "深色主题",
"theme.light": "浅色主题",
"theme.auto": "自动",
"theme.dark": "深色",
"theme.light": "浅色",
"theme.title": "主题",
"theme.window.style.opaque": "不透明窗口",
"theme.window.style.title": "窗口样式",
@ -824,6 +825,7 @@
"topic.position.left": "左侧",
"topic.position.right": "右侧",
"topic.show.time": "显示话题时间",
"assistant.show.icon": "显示模型图标",
"tray.title": "启用系统托盘图标",
"websearch": {
"blacklist": "黑名单",

View File

@ -813,8 +813,8 @@
"zoom_reset": "重置縮放"
},
"theme.auto": "自動",
"theme.dark": "深色主題",
"theme.light": "淺色主題",
"theme.dark": "深色",
"theme.light": "淺色",
"theme.title": "主題",
"theme.window.style.opaque": "不透明視窗",
"theme.window.style.title": "視窗樣式",
@ -824,6 +824,7 @@
"topic.position.left": "左側",
"topic.position.right": "右側",
"topic.show.time": "顯示話題時間",
"assistant.show.icon": "顯示模型圖標",
"tray.title": "啟用系統托盤圖標",
"websearch": {
"get_api_key": "點擊這裡獲取密鑰",

View File

@ -35,7 +35,7 @@ const Container = styled.div<{ $isDark: boolean }>`
border-radius: 6px;
cursor: pointer;
border: 0.5px solid var(--color-border);
background-color: ${({ $isDark }) => ($isDark ? 'var(--color-background-soft)' : 'transparent')};
background-color: ${({ $isDark }) => ($isDark ? 'var(--color-background-opacity)' : 'transparent')};
`
const Text = styled.div`

View File

@ -1,10 +1,11 @@
import { DeleteOutlined, EditOutlined, MinusCircleOutlined, SaveOutlined } from '@ant-design/icons'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import CopyIcon from '@renderer/components/Icons/CopyIcon'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { modelGenerating } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
import { getDefaultTopic } from '@renderer/services/AssistantService'
import { getDefaultModel, getDefaultTopic } from '@renderer/services/AssistantService'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { Assistant } from '@renderer/types'
import { uuid } from '@renderer/utils'
@ -28,7 +29,8 @@ interface AssistantItemProps {
const AssistantItem: FC<AssistantItemProps> = ({ assistant, isActive, onSwitch, onDelete, addAgent, addAssistant }) => {
const { t } = useTranslation()
const { removeAllTopics } = useAssistant(assistant.id) // 使用当前助手的ID
const { clickAssistantToShowTopic, topicPosition } = useSettings()
const { clickAssistantToShowTopic, topicPosition, showAssistantIcon } = useSettings()
const defaultModel = getDefaultModel()
const getMenuItems = useCallback(
(assistant: Assistant): ItemType[] => [
@ -114,7 +116,8 @@ const AssistantItem: FC<AssistantItemProps> = ({ assistant, isActive, onSwitch,
<Dropdown menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}>
<Container onClick={handleSwitch} className={isActive ? 'active' : ''}>
<AssistantName className="name" title={fullAssistantName}>
{fullAssistantName}
{showAssistantIcon && <ModelAvatar model={assistant.model || defaultModel} size={22} />}
{showAssistantIcon ? assistantName : fullAssistantName}
</AssistantName>
{isActive && (
<MenuButton onClick={() => EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}>
@ -149,6 +152,7 @@ const Container = styled.div`
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
.name {
font-weight: 500;
}
}
`
@ -160,6 +164,10 @@ const AssistantName = styled.div`
-webkit-box-orient: vertical;
overflow: hidden;
font-size: 13px;
display: flex;
flex-direction: row;
align-items: center;
gap: 5px;
`
const MenuButton = styled.div`

View File

@ -388,6 +388,7 @@ const TopicListItem = styled.div`
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
.name {
font-weight: 500;
}
.menu {
opacity: 1;

View File

@ -1,3 +1,4 @@
import { SyncOutlined } from '@ant-design/icons'
import { isMac } from '@renderer/config/constant'
import { DEFAULT_MIN_APPS } from '@renderer/config/minapps'
import { useTheme } from '@renderer/context/ThemeProvider'
@ -12,8 +13,8 @@ import {
setSidebarIcons
} from '@renderer/store/settings'
import { ThemeMode } from '@renderer/types'
import { Button, Input, Segmented, Select, Switch } from 'antd'
import { FC, useCallback, useState } from 'react'
import { Button, Input, Segmented, Switch } from 'antd'
import { FC, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -32,7 +33,9 @@ const DisplaySettings: FC = () => {
clickAssistantToShowTopic,
showTopicTime,
customCss,
sidebarIcons
sidebarIcons,
showAssistantIcon,
setShowAssistantIcon
} = useSettings()
const { minapps, disabled, updateMinapps, updateDisabledMinapps } = useMinapps()
const { theme: themeMode } = useTheme()
@ -65,6 +68,39 @@ const DisplaySettings: FC = () => {
updateDisabledMinapps([])
}, [updateDisabledMinapps, updateMinapps])
const themeOptions = useMemo(
() => [
{
value: ThemeMode.light,
label: (
<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
<i className="iconfont icon-theme icon-theme-light" />
<span>{t('settings.theme.light')}</span>
</div>
)
},
{
value: ThemeMode.dark,
label: (
<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
<i className="iconfont icon-theme icon-dark1" />
<span>{t('settings.theme.dark')}</span>
</div>
)
},
{
value: ThemeMode.auto,
label: (
<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
<SyncOutlined />
<span>{t('settings.theme.auto')}</span>
</div>
)
}
],
[t]
)
return (
<SettingContainer theme={themeMode}>
<SettingGroup theme={theme}>
@ -72,16 +108,7 @@ const DisplaySettings: FC = () => {
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.theme.title')}</SettingRowTitle>
<Select
value={theme}
style={{ width: 120 }}
onChange={setTheme}
options={[
{ value: ThemeMode.light, label: t('settings.theme.light') },
{ value: ThemeMode.dark, label: t('settings.theme.dark') },
{ value: ThemeMode.auto, label: t('settings.theme.auto') }
]}
/>
<Segmented value={theme} onChange={setTheme} options={themeOptions} />
</SettingRow>
{isMac && (
<>
@ -93,6 +120,14 @@ const DisplaySettings: FC = () => {
</>
)}
</SettingGroup>
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.display.assistant.title')}</SettingTitle>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.assistant.show.icon')}</SettingRowTitle>
<Switch checked={showAssistantIcon} onChange={(checked) => setShowAssistantIcon(checked)} />
</SettingRow>
</SettingGroup>
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.display.topic.title')}</SettingTitle>
<SettingDivider />

View File

@ -166,7 +166,6 @@ const Container = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 2px 0;
`
const ProviderListContainer = styled.div`

View File

@ -117,7 +117,9 @@ const WebSearchSettings: FC = () => {
autoSize={{ minRows: 4, maxRows: 8 }}
rows={4}
/>
<Button onClick={() => updateManualBlacklist(blacklistInput)}>{t('common.save')}</Button>
<Button onClick={() => updateManualBlacklist(blacklistInput)} style={{ marginTop: 10 }}>
{t('common.save')}
</Button>
{errFormat && <Alert message={t('settings.websearch.blacklist_tooltip')} type="error" />}
</SettingGroup>
</SettingContainer>

View File

@ -34,6 +34,7 @@ export interface SettingsState {
fontSize: number
topicPosition: 'left' | 'right'
showTopicTime: boolean
showAssistantIcon: boolean
pasteLongTextAsFile: boolean
pasteLongTextThreshold: number
clickAssistantToShowTopic: boolean
@ -98,6 +99,7 @@ const initialState: SettingsState = {
fontSize: 14,
topicPosition: 'left',
showTopicTime: false,
showAssistantIcon: false,
pasteLongTextAsFile: false,
pasteLongTextThreshold: 1500,
clickAssistantToShowTopic: false,
@ -202,6 +204,9 @@ const settingsSlice = createSlice({
setShowTopicTime: (state, action: PayloadAction<boolean>) => {
state.showTopicTime = action.payload
},
setShowAssistantIcon: (state, action: PayloadAction<boolean>) => {
state.showAssistantIcon = action.payload
},
setPasteLongTextAsFile: (state, action: PayloadAction<boolean>) => {
state.pasteLongTextAsFile = action.payload
},
@ -341,6 +346,7 @@ export const {
setWindowStyle,
setTopicPosition,
setShowTopicTime,
setShowAssistantIcon,
setPasteLongTextAsFile,
setRenderInputMessageAsMarkdown,
setClickAssistantToShowTopic,