feat: new settings ui

This commit is contained in:
kangfenmao 2024-11-19 19:54:18 +08:00
parent 335ce4963b
commit 0ec61e1c47
13 changed files with 305 additions and 258 deletions

View File

@ -51,42 +51,42 @@ const Sidebar: FC = () => {
<AvatarImg src={avatar || UserAvatar} draggable={false} className="nodrag" onClick={onEditUser} />
<MainMenus>
<Menus onClick={MinApp.onClose}>
<Tooltip title={t('assistants.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('assistants.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/')}>
<Icon className={isRoute('/')}>
<i className="iconfont icon-chat" />
</Icon>
</StyledLink>
</Tooltip>
<Tooltip title={t('agents.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('agents.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/agents')}>
<Icon className={isRoutes('/agents')}>
<i className="iconfont icon-business-smart-assistant" />
</Icon>
</StyledLink>
</Tooltip>
<Tooltip title={t('paintings.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('paintings.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/paintings')}>
<Icon className={isRoute('/paintings')}>
<PictureOutlined style={{ fontSize: 16 }} />
</Icon>
</StyledLink>
</Tooltip>
<Tooltip title={t('translate.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('translate.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/translate')}>
<Icon className={isRoute('/translate')}>
<TranslationOutlined />
</Icon>
</StyledLink>
</Tooltip>
<Tooltip title={t('minapp.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('minapp.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/apps')}>
<Icon className={isRoute('/apps')}>
<i className="iconfont icon-appstore" />
</Icon>
</StyledLink>
</Tooltip>
<Tooltip title={t('files.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('files.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to('/files')}>
<Icon className={isRoute('/files')}>
<FolderOutlined />
@ -96,7 +96,7 @@ const Sidebar: FC = () => {
</Menus>
</MainMenus>
<Menus onClick={MinApp.onClose}>
<Tooltip title={t('settings.theme.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('settings.theme.title')} mouseEnterDelay={0.8} placement="right">
<Icon onClick={() => toggleTheme()}>
{theme === 'dark' ? (
<i className="iconfont icon-theme icon-dark1" />
@ -105,7 +105,7 @@ const Sidebar: FC = () => {
)}
</Icon>
</Tooltip>
<Tooltip title={t('settings.title')} mouseEnterDelay={0.5} placement="right">
<Tooltip title={t('settings.title')} mouseEnterDelay={0.8} placement="right">
<StyledLink onClick={() => to(isLocalAi ? '/settings/assistant' : '/settings/provider')}>
<Icon className={pathname.startsWith('/settings') ? 'active' : ''}>
<i className="iconfont icon-setting" />
@ -183,7 +183,8 @@ const Icon = styled.div`
background-color: var(--color-active);
.iconfont,
.anticon {
color: var(--color-icon-white);
color: var(--color-primary);
font-weight: bold;
}
}
`

View File

@ -302,8 +302,11 @@
"provider.api.url.preview": "Preview: {{url}}",
"provider.api.url.tip": "Ending with / ignores v1, ending with # forces use of input address",
"models.default_assistant_model": "Default Assistant Model",
"models.default_assistant_model_description": "Model used when creating a new assistant, if the assistant is not set, this model will be used",
"models.topic_naming_model": "Topic Naming Model",
"models.topic_naming_model_description": "Model used when automatically naming a new topic",
"models.translate_model": "Translate Model",
"models.translate_model_description": "Model used for translation service",
"models.add.add_model": "Add Model",
"models.add.model_id.placeholder": "Required e.g. gpt-3.5-turbo",
"models.add.model_id": "Model ID",

View File

@ -302,8 +302,11 @@
"provider.api.url.preview": "Предпросмотр: {{url}}",
"provider.api.url.tip": "Заканчивая на / игнорирует v1, заканчивая на # принудительно использует введенный адрес",
"models.default_assistant_model": "Модель ассистента по умолчанию",
"models.default_assistant_model_description": "Модель, используемая при создании нового ассистента, если ассистент не имеет настроенной модели, будет использоваться эта модель",
"models.topic_naming_model": "Модель именования топика",
"models.topic_naming_model_description": "Модель, используемая при автоматическом именовании нового топика",
"models.translate_model": "Модель перевода",
"models.translate_model_description": "Модель, используемая для сервиса перевода",
"models.add.add_model": "Добавить модель",
"models.add.model_id.placeholder": "Обязательно, например, gpt-3.5-turbo",
"models.add.model_id": "ID модели",

View File

@ -290,8 +290,11 @@
"advanced.title": "高级设置",
"advanced.auto_switch_to_topics": "自动切换到话题",
"models.default_assistant_model": "默认助手模型",
"models.default_assistant_model_description": "创建新助手时使用的模型,如果助手未设置模型,则使用此模型",
"models.topic_naming_model": "话题命名模型",
"models.topic_naming_model_description": "自动命名新话题时使用的模型",
"models.translate_model": "翻译模型",
"models.translate_model_description": "翻译服务使用的模型",
"models.add.add_model": "添加模型",
"models.add.model_id.placeholder": "必填 例如 gpt-3.5-turbo",
"models.add.model_id": "模型 ID",

View File

@ -290,8 +290,11 @@
"advanced.title": "進階設定",
"advanced.auto_switch_to_topics": "自動切換到話題",
"models.default_assistant_model": "預設助手模型",
"models.default_assistant_model_description": "創建新助手時使用的模型,如果助手未設置模型,則使用此模型",
"models.topic_naming_model": "話題命名模型",
"models.topic_naming_model_description": "自動命名新話題時使用的模型",
"models.translate_model": "翻譯模型",
"models.translate_model_description": "翻譯服務使用的模型",
"models.add.add_model": "添加模型",
"models.add.model_id.placeholder": "必填,例如 gpt-3.5-turbo",
"models.add.model_id": "模型 ID",

View File

@ -3,6 +3,7 @@ import { FileProtectOutlined, GlobalOutlined, MailOutlined, SoundOutlined } from
import { HStack } from '@renderer/components/Layout'
import MinApp from '@renderer/components/MinApp'
import { APP_NAME, AppLogo } from '@renderer/config/env'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useSettings } from '@renderer/hooks/useSettings'
import { useAppDispatch } from '@renderer/store'
import { setManualUpdateCheck } from '@renderer/store/settings'
@ -15,7 +16,7 @@ import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingRow, SettingTitle } from '.'
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingTitle } from '.'
const AboutSettings: FC = () => {
const [version, setVersion] = useState('')
@ -24,6 +25,7 @@ const AboutSettings: FC = () => {
const [checkUpdateLoading, setCheckUpdateLoading] = useState(false)
const [downloading, setDownloading] = useState(false)
const { manualUpdateCheck } = useSettings()
const { theme } = useTheme()
const dispatch = useAppDispatch()
const onCheckUpdate = debounce(
@ -104,7 +106,8 @@ const AboutSettings: FC = () => {
}, [t])
return (
<SettingContainer>
<SettingContainer theme={theme}>
<SettingGroup theme={theme}>
<SettingTitle>
{t('settings.about.title')}
<HStack alignItems="center">
@ -149,7 +152,8 @@ const AboutSettings: FC = () => {
<SettingRowTitle>{t('settings.general.manually_check_update.title')}</SettingRowTitle>
<Switch value={manualUpdateCheck} onChange={(v) => dispatch(setManualUpdateCheck(v))} />
</SettingRow>
<SettingDivider />
</SettingGroup>
<SettingGroup theme={theme}>
<SettingRow>
<SettingRowTitle>
<SoundOutlined />
@ -199,7 +203,7 @@ const AboutSettings: FC = () => {
</SettingRowTitle>
<Button onClick={mailto}>{t('settings.about.contact.button')}</Button>
</SettingRow>
<SettingDivider />
</SettingGroup>
</SettingContainer>
)
}

View File

@ -2,6 +2,7 @@ import { QuestionCircleOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import { TopView } from '@renderer/components/TopView'
import { DEFAULT_CONTEXTCOUNT, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { AssistantSettings as AssistantSettingsType } from '@renderer/types'
import { Button, Col, Input, InputNumber, Modal, Row, Slider, Switch, Tooltip } from 'antd'
@ -18,6 +19,7 @@ const AssistantSettings: FC = () => {
const [contextCount, setContextCount] = useState(defaultAssistant.settings?.contextCount ?? DEFAULT_CONTEXTCOUNT)
const [enableMaxTokens, setEnableMaxTokens] = useState(defaultAssistant?.settings?.enableMaxTokens ?? false)
const [maxTokens, setMaxTokens] = useState(defaultAssistant?.settings?.maxTokens ?? 0)
const { theme } = useTheme()
const { t } = useTranslation()
@ -67,7 +69,7 @@ const AssistantSettings: FC = () => {
}
return (
<SettingContainer style={{ height: 'auto' }}>
<SettingContainer style={{ height: 'auto', background: 'transparent', padding: 0 }} theme={theme}>
<SettingSubtitle style={{ marginTop: 0 }}>{t('common.name')}</SettingSubtitle>
<Input
placeholder={t('common.assistant') + t('common.name')}

View File

@ -1,5 +1,6 @@
import { FileSearchOutlined, FolderOpenOutlined, SaveOutlined } from '@ant-design/icons'
import { HStack, VStack } from '@renderer/components/Layout'
import { useTheme } from '@renderer/context/ThemeProvider'
import { backup, reset, restore } from '@renderer/services/BackupService'
import { AppInfo } from '@renderer/types'
import { Button, Typography } from 'antd'
@ -14,12 +15,14 @@ import WebDavSettings from './WebDavSettings'
const DataSettings: FC = () => {
const { t } = useTranslation()
const [appInfo, setAppInfo] = useState<AppInfo>()
const { theme } = useTheme()
useEffect(() => {
window.api.getAppInfo().then(setAppInfo)
}, [])
const handleOpenPath = (path: string) => {
const handleOpenPath = (path?: string) => {
if (!path) return
if (path?.endsWith('log')) {
const dirPath = path.split(/[/\\]/).slice(0, -1).join('/')
window.api.openPath(dirPath)
@ -29,8 +32,8 @@ const DataSettings: FC = () => {
}
return (
<SettingContainer>
<SettingGroup>
<SettingContainer theme={theme}>
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.data')}</SettingTitle>
<SettingDivider />
<SettingRow>
@ -63,10 +66,10 @@ const DataSettings: FC = () => {
</HStack>
</SettingRow>
</SettingGroup>
<SettingGroup>
<SettingGroup theme={theme}>
<WebDavSettings />
</SettingGroup>
<SettingGroup>
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.data.title')}</SettingTitle>
<SettingDivider />
<SettingRow>

View File

@ -1,4 +1,5 @@
import { isMac } from '@renderer/config/constant'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useSettings } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n'
import { useAppDispatch } from '@renderer/store'
@ -10,7 +11,7 @@ import { Input, Select, Space, Switch } from 'antd'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SettingContainer, SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from '.'
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '.'
const GeneralSettings: FC = () => {
const {
@ -25,6 +26,7 @@ const GeneralSettings: FC = () => {
proxyMode: storeProxyMode
} = useSettings()
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
const { theme: themeMode } = useTheme()
const updateTray = (value: boolean) => {
setTray(value)
@ -76,7 +78,8 @@ const GeneralSettings: FC = () => {
]
return (
<SettingContainer>
<SettingContainer theme={themeMode}>
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.general.title')}</SettingTitle>
<SettingDivider />
<SettingRow>
@ -128,7 +131,12 @@ const GeneralSettings: FC = () => {
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.proxy.mode.title')}</SettingRowTitle>
<Select value={storeProxyMode} style={{ width: 180 }} onChange={onProxyModeChange} options={proxyModeOptions} />
<Select
value={storeProxyMode}
style={{ width: 180 }}
onChange={onProxyModeChange}
options={proxyModeOptions}
/>
</SettingRow>
{storeProxyMode === 'custom' && (
<>
@ -151,7 +159,7 @@ const GeneralSettings: FC = () => {
<SettingRowTitle>{t('settings.tray.title')}</SettingRowTitle>
<Switch checked={tray} onChange={(checked) => updateTray(checked)} />
</SettingRow>
<SettingDivider />
</SettingGroup>
</SettingContainer>
)
}

View File

@ -1,5 +1,6 @@
import { EditOutlined, MessageOutlined, SettingOutlined, TranslationOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultModel } from '@renderer/hooks/useAssistant'
import { useProviders } from '@renderer/hooks/useProvider'
import { getModelUniqId, hasModel } from '@renderer/services/ModelService'
@ -9,7 +10,7 @@ import { find, sortBy } from 'lodash'
import { FC, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { SettingContainer, SettingDivider, SettingTitle } from '.'
import { SettingContainer, SettingDescription, SettingDivider, SettingGroup, SettingTitle } from '.'
import AssistantSettingsPopup from './AssistantSettings'
const ModelSettings: FC = () => {
@ -17,6 +18,7 @@ const ModelSettings: FC = () => {
useDefaultModel()
const { providers } = useProviders()
const allModels = providers.map((p) => p.models).flat()
const { theme } = useTheme()
const { t } = useTranslation()
const selectOptions = providers
@ -46,7 +48,8 @@ const ModelSettings: FC = () => {
)
return (
<SettingContainer>
<SettingContainer theme={theme}>
<SettingGroup theme={theme}>
<SettingTitle>
<div>
<MessageOutlined style={iconStyle} />
@ -65,7 +68,9 @@ const ModelSettings: FC = () => {
/>
<Button icon={<SettingOutlined />} style={{ marginLeft: 8 }} onClick={() => AssistantSettingsPopup.show()} />
</HStack>
<div style={{ height: 30 }} />
<SettingDescription>{t('settings.models.default_assistant_model_description')}</SettingDescription>
</SettingGroup>
<SettingGroup theme={theme}>
<SettingTitle>
<div>
<EditOutlined style={iconStyle} />
@ -81,7 +86,9 @@ const ModelSettings: FC = () => {
options={selectOptions}
placeholder={t('settings.models.empty')}
/>
<div style={{ height: 30 }} />
<SettingDescription>{t('settings.models.topic_naming_model_description')}</SettingDescription>
</SettingGroup>
<SettingGroup theme={theme}>
<SettingTitle>
<div>
<TranslationOutlined style={iconStyle} />
@ -97,6 +104,8 @@ const ModelSettings: FC = () => {
options={selectOptions}
placeholder={t('settings.models.empty')}
/>
<SettingDescription>{t('settings.models.translate_model_description')}</SettingDescription>
</SettingGroup>
</SettingContainer>
)
}

View File

@ -126,12 +126,7 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
}
return (
<SettingContainer
style={
theme === 'dark'
? { backgroundColor: 'var(--color-background)' }
: { backgroundColor: 'var(--color-background-mute)' }
}>
<SettingContainer theme={theme}>
<SettingTitle>
<Flex align="center">
<span>{provider.isSystem ? t(`provider.${provider.id}`) : provider.name}</span>

View File

@ -1,11 +1,12 @@
import { isMac } from '@renderer/config/constant'
import { useTheme } from '@renderer/context/ThemeProvider'
import { Switch, Table as AntTable, Tag } from 'antd'
import type { ColumnsType } from 'antd/es/table'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingTitle } from '.'
import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '.'
interface ShortcutItem {
key: string
@ -16,6 +17,7 @@ interface ShortcutItem {
const ShortcutSettings: FC = () => {
const { t } = useTranslation()
const { theme } = useTheme()
const commandKey = isMac ? '⌘' : 'Ctrl'
@ -81,7 +83,8 @@ const ShortcutSettings: FC = () => {
]
return (
<SettingContainer>
<SettingContainer theme={theme}>
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.shortcuts.title')}</SettingTitle>
<SettingDivider style={{ marginBottom: 0 }} />
<Table
@ -91,6 +94,7 @@ const ShortcutSettings: FC = () => {
size="middle"
showHeader={false}
/>
</SettingGroup>
</SettingContainer>
)
}

View File

@ -1,15 +1,17 @@
import { ThemeMode } from '@renderer/types'
import { Divider } from 'antd'
import Link from 'antd/es/typography/Link'
import styled from 'styled-components'
export const SettingContainer = styled.div`
export const SettingContainer = styled.div<{ theme?: ThemeMode }>`
display: flex;
flex-direction: column;
flex: 1;
height: calc(100vh - var(--navbar-height));
padding: 15px;
padding: 20px;
overflow-y: scroll;
font-family: Ubuntu;
background: ${(props) => (props.theme === 'dark' ? 'transparent' : 'var(--color-background-soft)')};
&::-webkit-scrollbar {
display: none;
@ -22,7 +24,7 @@ export const SettingTitle = styled.div`
justify-content: space-between;
align-items: center;
user-select: none;
font-size: 16px;
font-size: 14px;
font-weight: bold;
`
@ -34,6 +36,12 @@ export const SettingSubtitle = styled.div`
font-weight: bold;
`
export const SettingDescription = styled.div`
font-size: 12px;
color: var(--color-text-3);
margin-top: 10px;
`
export const SettingDivider = styled(Divider)`
margin: 10px 0;
`
@ -70,9 +78,10 @@ export const SettingHelpLink = styled(Link)`
padding: 0 5px;
`
export const SettingGroup = styled.div`
margin-bottom: 16px;
export const SettingGroup = styled.div<{ theme?: ThemeMode }>`
margin-bottom: 20px;
border-radius: 8px;
border: 0.5px solid var(--color-border);
padding: 16px;
background: ${(props) => (props.theme === 'dark' ? 'var(--color-background-soft)' : 'var(--color-background)')};
`