feat: optimize UI interface display

This commit is contained in:
kangfenmao 2025-06-05 13:37:04 +08:00
parent 9538d07169
commit 245f37a673
11 changed files with 191 additions and 189 deletions

View File

@ -44,6 +44,9 @@
--color-reference-text: #ffffff;
--color-reference-background: #0b0e12;
--color-list-item: #222;
--color-list-item-hover: #1e1e1e;
--modal-background: #1f1f1f;
--color-highlight: rgba(0, 0, 0, 1);
@ -68,7 +71,7 @@
--chat-background-assistant: #2c2c2c;
--chat-text-user: var(--color-black);
--list-item-border-radius: 16px;
--list-item-border-radius: 20px;
}
[theme-mode='light'] {
@ -117,6 +120,9 @@
--color-reference-text: #000000;
--color-reference-background: #f1f7ff;
--color-list-item: #eee;
--color-list-item-hover: #f5f5f5;
--modal-background: var(--color-white);
--color-highlight: initial;

View File

@ -9,6 +9,7 @@ import { useMinapps } from '@renderer/hooks/useMinapps'
import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor'
import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n'
import { ThemeMode } from '@renderer/types'
import { isEmoji } from '@renderer/utils'
import type { MenuProps } from 'antd'
@ -19,7 +20,7 @@ import {
Folder,
Languages,
LayoutGrid,
MessageSquareQuote,
MessageSquare,
Moon,
Palette,
Settings,
@ -35,7 +36,6 @@ import styled from 'styled-components'
import DragableList from '../DragableList'
import MinAppIcon from '../Icons/MinAppIcon'
import UserPopup from '../Popups/UserPopup'
import i18n from '@renderer/i18n'
const Sidebar: FC = () => {
const { hideMinappPopup, openMinapp } = useMinappPopup()
@ -67,9 +67,7 @@ const Sidebar: FC = () => {
openMinapp({
id: docsId,
name: t('docs.title'),
url: isChinese
? 'https://docs.cherry-ai.com/'
: 'https://docs.cherry-ai.com/cherry-studio-wen-dang/en-us',
url: isChinese ? 'https://docs.cherry-ai.com/' : 'https://docs.cherry-ai.com/cherry-studio-wen-dang/en-us',
logo: AppLogo
})
}
@ -151,7 +149,7 @@ const MainMenus: FC = () => {
const isRoutes = (path: string): string => (pathname.startsWith(path) && !minappShow ? 'active' : '')
const iconMap = {
assistants: <MessageSquareQuote size={18} className="icon" />,
assistants: <MessageSquare size={18} className="icon" />,
agents: <Sparkle size={18} className="icon" />,
paintings: <Palette size={18} className="icon" />,
translate: <Languages size={18} className="icon" />,

View File

@ -48,6 +48,10 @@ const AntdProvider: FC<PropsWithChildren> = ({ children }) => {
},
ColorPicker: {
fontFamily: 'var(--code-font-family)'
},
Segmented: {
itemActiveBg: 'var(--color-background-mute)',
itemHoverBg: 'var(--color-background-mute)'
}
},
token: {

View File

@ -34,7 +34,7 @@ const Container = styled.div<{ $isDark: boolean }>`
margin: 5px 20px 0 20px;
border-radius: 10px;
cursor: pointer;
border: 1px solid var(--color-border);
border: 0.5px solid var(--color-border);
`
const Text = styled.div`

View File

@ -60,7 +60,7 @@ import {
} from '@renderer/types'
import { modalConfirm } from '@renderer/utils'
import { Button, Col, InputNumber, Row, Select, Slider, Switch, Tooltip } from 'antd'
import { CircleHelp, RotateCcw, Settings2 } from 'lucide-react'
import { CircleHelp, Settings2 } from 'lucide-react'
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -72,7 +72,7 @@ interface Props {
}
const SettingsTab: FC<Props> = (props) => {
const { assistant, updateAssistantSettings, updateAssistant } = useAssistant(props.assistant.id)
const { assistant, updateAssistantSettings } = useAssistant(props.assistant.id)
const { provider } = useProvider(assistant.model.provider)
const { messageStyle, fontSize, language } = useSettings()
@ -140,24 +140,6 @@ const SettingsTab: FC<Props> = (props) => {
}
}
const onReset = () => {
setTemperature(DEFAULT_TEMPERATURE)
setContextCount(DEFAULT_CONTEXTCOUNT)
updateAssistant({
...assistant,
settings: {
...assistant.settings,
temperature: DEFAULT_TEMPERATURE,
contextCount: DEFAULT_CONTEXTCOUNT,
enableMaxTokens: false,
maxTokens: DEFAULT_MAX_TOKENS,
streamOutput: true,
hideMessages: false,
customParameters: []
}
})
}
const codeStyle = useMemo(() => {
return codeEditor.enabled
? theme === ThemeMode.light
@ -211,14 +193,6 @@ const SettingsTab: FC<Props> = (props) => {
defaultExpanded={true}
extra={
<HStack alignItems="center" gap={2}>
<Tooltip title={t('chat.settings.reset')}>
<Button
type="text"
size="small"
onClick={onReset}
icon={<RotateCcw size={20} style={{ cursor: 'pointer', padding: '0 3px', opacity: 0.8 }} />}
/>
</Tooltip>
<Button
type="text"
size="small"
@ -714,6 +688,7 @@ const Container = styled(Scrollbar)`
padding-right: 0;
padding-top: 2px;
padding-bottom: 10px;
margin-top: 3px;
`
const SettingRowTitleSmall = styled(SettingRowTitle)`

View File

@ -4,6 +4,7 @@ import {
DeleteOutlined,
EditOutlined,
FolderOutlined,
MenuOutlined,
PushpinOutlined,
QuestionCircleOutlined,
UploadOutlined
@ -54,7 +55,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
const { assistants } = useAssistants()
const { assistant, removeTopic, moveTopic, updateTopic, updateTopics } = useAssistant(_assistant.id)
const { t } = useTranslation()
const { showTopicTime, pinTopicsToTop } = useSettings()
const { showTopicTime, pinTopicsToTop, setTopicPosition } = useSettings()
const borderRadius = showTopicTime ? 12 : 'var(--list-item-border-radius)'
@ -248,6 +249,23 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
})
}
},
{
label: t('settings.topic.position'),
key: 'topic-position',
icon: <MenuOutlined />,
children: [
{
label: t('settings.topic.position.left'),
key: 'left',
onClick: () => setTopicPosition('left')
},
{
label: t('settings.topic.position.right'),
key: 'right',
onClick: () => setTopicPosition('right')
}
]
},
{
label: t('chat.topics.copy.title'),
key: 'copy',
@ -363,26 +381,27 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
return menus
}, [
activeTopic.id,
assistant,
assistants,
exportMenuOptions.docx,
targetTopic,
t,
exportMenuOptions.image,
exportMenuOptions.joplin,
exportMenuOptions.markdown,
exportMenuOptions.markdown_reason,
exportMenuOptions.docx,
exportMenuOptions.notion,
exportMenuOptions.obsidian,
exportMenuOptions.siyuan,
exportMenuOptions.yuque,
onClearMessages,
onDeleteTopic,
onMoveTopic,
onPinTopic,
setActiveTopic,
t,
exportMenuOptions.obsidian,
exportMenuOptions.joplin,
exportMenuOptions.siyuan,
assistants,
assistant,
updateTopic,
targetTopic
activeTopic.id,
setActiveTopic,
onPinTopic,
onClearMessages,
setTopicPosition,
onMoveTopic,
onDeleteTopic
])
// Sort topics based on pinned status if pinTopicsToTop is enabled
@ -486,7 +505,6 @@ const TopicListItem = styled.div`
justify-content: space-between;
position: relative;
cursor: pointer;
border: 0.5px solid transparent;
position: relative;
width: calc(var(--assistants-width) - 20px);
.menu {
@ -494,15 +512,10 @@ const TopicListItem = styled.div`
color: var(--color-text-3);
}
&:hover {
background-color: var(--color-background-soft);
.name {
}
background-color: var(--color-list-item-hover);
}
&.active {
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
.name {
}
background-color: var(--color-list-item);
.menu {
opacity: 1;
&:hover {

View File

@ -383,23 +383,18 @@ const Container = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0 10px;
padding: 0 8px;
height: 37px;
position: relative;
border-radius: var(--list-item-border-radius);
border: 0.5px solid transparent;
width: calc(var(--assistants-width) - 20px);
cursor: pointer;
.iconfont {
opacity: 0;
color: var(--color-text-3);
}
&:hover {
background-color: var(--color-background-soft);
background-color: var(--color-list-item-hover);
}
&.active {
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
background-color: var(--color-list-item);
}
`

View File

@ -58,6 +58,7 @@ const HomeTabs: FC<Props> = ({
const assistantTab = {
label: t('assistants.abbr'),
value: 'assistants'
// icon: <BotIcon size={16} />
}
const onCreateAssistant = async () => {
@ -104,28 +105,35 @@ const HomeTabs: FC<Props> = ({
return (
<Container style={{ ...border, ...style }} className="home-tabs">
{(showTab || (forceToSeeAllTab == true && !showTopics)) && (
<Segmented
value={tab}
style={{ borderRadius: 16, paddingTop: 10, margin: '0 10px', gap: 2 }}
options={
[
(position === 'left' && topicPosition === 'left') || (forceToSeeAllTab == true && position === 'left')
? assistantTab
: undefined,
{
label: t('common.topics'),
value: 'topic'
},
{
label: t('settings.title'),
value: 'settings'
}
].filter(Boolean) as SegmentedProps['options']
}
onChange={(value) => setTab(value as 'topic' | 'settings')}
block
/>
<>
<Segmented
value={tab}
style={{ borderRadius: 50 }}
shape="round"
options={
[
(position === 'left' && topicPosition === 'left') || (forceToSeeAllTab == true && position === 'left')
? assistantTab
: undefined,
{
label: t('common.topics'),
value: 'topic'
// icon: <MessageSquareQuote size={16} />
},
{
label: t('settings.title'),
value: 'settings'
// icon: <SettingsIcon size={16} />
}
].filter(Boolean) as SegmentedProps['options']
}
onChange={(value) => setTab(value as 'topic' | 'settings')}
block
/>
<Divider />
</>
)}
<TabContent className="home-tabs-content">
{tab === 'assistants' && (
<Assistants
@ -149,7 +157,7 @@ const Container = styled.div`
flex-direction: column;
max-width: var(--assistants-width);
min-width: var(--assistants-width);
background-color: var(--color-background);
background-color: transparent;
overflow: hidden;
.collapsed {
width: 0;
@ -165,14 +173,21 @@ const TabContent = styled.div`
overflow-x: hidden;
`
const Divider = styled.div`
border-top: 0.5px solid var(--color-border);
margin-top: 10px;
margin-left: 10px;
margin-right: 10px;
`
const Segmented = styled(AntSegmented)`
font-family: var(--font-family);
&.ant-segmented {
background-color: transparent;
border-radius: 0 !important;
border-bottom: 0.5px solid var(--color-border);
padding-bottom: 10px;
margin: 0 10px;
margin-top: 10px;
padding: 0;
}
.ant-segmented-item {
overflow: hidden;
@ -184,10 +199,10 @@ const Segmented = styled(AntSegmented)`
border-radius: var(--list-item-border-radius);
box-shadow: none;
}
.ant-segmented-item-selected {
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
.ant-segmented-item-selected,
.ant-segmented-item-selected:active {
transition: none !important;
background-color: var(--color-list-item);
}
.ant-segmented-item-label {
align-items: center;
@ -200,25 +215,17 @@ const Segmented = styled(AntSegmented)`
.ant-segmented-item-label[aria-selected='true'] {
color: var(--color-text);
}
.iconfont {
font-size: 13px;
margin-left: -2px;
}
.anticon-setting {
font-size: 12px;
}
.icon-business-smart-assistant {
margin-right: -2px;
}
.ant-segmented-item-icon + * {
margin-left: 4px;
}
.ant-segmented-thumb {
transition: none !important;
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
background-color: var(--color-list-item);
border-radius: var(--list-item-border-radius);
box-shadow: none;
&:hover {
background-color: transparent;
}
}
.ant-segmented-item-label,
.ant-segmented-item-icon {

View File

@ -13,6 +13,8 @@ import { useTranslation } from 'react-i18next'
import ReactMarkdown from 'react-markdown'
import styled from 'styled-components'
import { SettingDivider } from '..'
interface Props {
assistant: Assistant
updateAssistant: (assistant: Assistant) => void
@ -90,7 +92,8 @@ const AssistantPromptSettings: React.FC<Props> = ({ assistant, updateAssistant }
style={{ flex: 1 }}
/>
</HStack>
<HStack mt={8} mb={8} alignItems="center" gap={4}>
<SettingDivider />
<HStack mb={8} alignItems="center" gap={4}>
<Box style={{ fontWeight: 'bold' }}>{t('common.prompt')}</Box>
<Tooltip title={t('agents.add.prompt.variables.tip')}>
<QuestionCircleOutlined size={14} color="var(--color-text-2)" />
@ -139,7 +142,6 @@ const Container = styled.div`
flex: 1;
flex-direction: column;
overflow: hidden;
padding: 5px;
`
const EmojiButtonWrapper = styled.div`

View File

@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { v4 as uuidv4 } from 'uuid'
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingTitle } from '..'
import { SettingDivider, SettingRow, SettingTitle } from '..'
const { TextArea } = Input
@ -79,52 +79,50 @@ const AssistantRegularPromptsSettings: FC<AssistantRegularPromptsSettingsProps>
const reversedPrompts = [...promptsList].reverse()
return (
<SettingContainer style={{ padding: 0, background: '#0000' }}>
<SettingGroup style={{ marginBottom: 0, padding: 0, border: 'none' }}>
<SettingTitle>
{t('assistants.settings.regular_phrases.title', 'Regular Prompts')}
<Button type="text" icon={<PlusOutlined />} onClick={handleAdd} />
</SettingTitle>
<SettingDivider />
<SettingRow>
<StyledPromptList>
<DragableList
list={reversedPrompts}
onUpdate={(newPrompts) => handleUpdateOrder([...newPrompts].reverse())}
style={{ paddingBottom: dragging ? '34px' : 0 }}
onDragStart={() => setDragging(true)}
onDragEnd={() => setDragging(false)}>
{(prompt) => (
<FileItem
key={prompt.id}
fileInfo={{
name: prompt.title,
ext: '.txt',
extra: prompt.content,
actions: (
<Flex gap={4} style={{ opacity: 0.6 }}>
<Button key="edit" type="text" icon={<EditOutlined />} onClick={() => handleEdit(prompt)} />
<Popconfirm
title={t('assistants.settings.regular_phrases.delete', 'Delete Prompt')}
description={t(
'assistants.settings.regular_phrases.deleteConfirm',
'Are you sure to delete this prompt?'
)}
okText={t('common.confirm')}
cancelText={t('common.cancel')}
onConfirm={() => handleDelete(prompt.id)}
icon={<ExclamationCircleOutlined style={{ color: 'red' }} />}>
<Button key="delete" type="text" danger icon={<DeleteOutlined />} />
</Popconfirm>
</Flex>
)
}}
/>
)}
</DragableList>
</StyledPromptList>
</SettingRow>
</SettingGroup>
<Container>
<SettingTitle>
{t('assistants.settings.regular_phrases.title', 'Regular Prompts')}
<Button type="text" icon={<PlusOutlined />} onClick={handleAdd} />
</SettingTitle>
<SettingDivider />
<SettingRow>
<StyledPromptList>
<DragableList
list={reversedPrompts}
onUpdate={(newPrompts) => handleUpdateOrder([...newPrompts].reverse())}
style={{ paddingBottom: dragging ? '34px' : 0 }}
onDragStart={() => setDragging(true)}
onDragEnd={() => setDragging(false)}>
{(prompt) => (
<FileItem
key={prompt.id}
fileInfo={{
name: prompt.title,
ext: '.txt',
extra: prompt.content,
actions: (
<Flex gap={4} style={{ opacity: 0.6 }}>
<Button key="edit" type="text" icon={<EditOutlined />} onClick={() => handleEdit(prompt)} />
<Popconfirm
title={t('assistants.settings.regular_phrases.delete', 'Delete Prompt')}
description={t(
'assistants.settings.regular_phrases.deleteConfirm',
'Are you sure to delete this prompt?'
)}
okText={t('common.confirm')}
cancelText={t('common.cancel')}
onConfirm={() => handleDelete(prompt.id)}
icon={<ExclamationCircleOutlined style={{ color: 'red' }} />}>
<Button key="delete" type="text" danger icon={<DeleteOutlined />} />
</Popconfirm>
</Flex>
)
}}
/>
)}
</DragableList>
</StyledPromptList>
</SettingRow>
<Modal
title={
@ -159,10 +157,16 @@ const AssistantRegularPromptsSettings: FC<AssistantRegularPromptsSettingsProps>
</div>
</Space>
</Modal>
</SettingContainer>
</Container>
)
}
const Container = styled.div`
display: flex;
flex: 1;
flex-direction: column;
`
const Label = styled.div`
font-size: 14px;
color: var(--color-text);
@ -171,8 +175,6 @@ const Label = styled.div`
const StyledPromptList = styled.div`
width: 100%;
height: calc(100vh - 162px); // Adjusted height to match other settings pages
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 8px;

View File

@ -66,16 +66,6 @@ export const INITIAL_PROVIDERS: Provider[] = [
isSystem: true,
enabled: false
},
{
id: 'openrouter',
name: 'OpenRouter',
type: 'openai',
apiKey: '',
apiHost: 'https://openrouter.ai/api/v1/',
models: SYSTEM_MODELS.openrouter,
isSystem: true,
enabled: false
},
{
id: 'ppio',
name: 'PPIO',
@ -96,16 +86,6 @@ export const INITIAL_PROVIDERS: Provider[] = [
isSystem: true,
enabled: false
},
{
id: 'infini',
name: 'Infini',
type: 'openai',
apiKey: '',
apiHost: 'https://cloud.infini-ai.com/maas',
models: SYSTEM_MODELS.infini,
isSystem: true,
enabled: false
},
{
id: 'qiniu',
name: 'Qiniu',
@ -136,6 +116,16 @@ export const INITIAL_PROVIDERS: Provider[] = [
isSystem: true,
enabled: false
},
{
id: 'tokenflux',
name: 'TokenFlux',
type: 'openai',
apiKey: '',
apiHost: 'https://tokenflux.ai',
models: SYSTEM_MODELS.tokenflux,
isSystem: true,
enabled: false
},
{
id: 'o3',
name: 'O3',
@ -146,6 +136,16 @@ export const INITIAL_PROVIDERS: Provider[] = [
isSystem: true,
enabled: false
},
{
id: 'openrouter',
name: 'OpenRouter',
type: 'openai',
apiKey: '',
apiHost: 'https://openrouter.ai/api/v1/',
models: SYSTEM_MODELS.openrouter,
isSystem: true,
enabled: false
},
{
id: 'ollama',
name: 'Ollama',
@ -298,6 +298,16 @@ export const INITIAL_PROVIDERS: Provider[] = [
isSystem: true,
enabled: false
},
{
id: 'infini',
name: 'Infini',
type: 'openai',
apiKey: '',
apiHost: 'https://cloud.infini-ai.com/maas',
models: SYSTEM_MODELS.infini,
isSystem: true,
enabled: false
},
{
id: 'minimax',
name: 'MiniMax',
@ -477,16 +487,6 @@ export const INITIAL_PROVIDERS: Provider[] = [
models: SYSTEM_MODELS.voyageai,
isSystem: true,
enabled: false
},
{
id: 'tokenflux',
name: 'TokenFlux',
type: 'openai',
apiKey: '',
apiHost: 'https://tokenflux.ai',
models: SYSTEM_MODELS.tokenflux,
isSystem: true,
enabled: false
}
]