feat: add setting panel
This commit is contained in:
parent
9d96b826e2
commit
d0280186bc
@ -27,6 +27,10 @@
|
||||
--color-background-soft: var(--color-black-soft);
|
||||
--color-background-mute: var(--color-black-mute);
|
||||
|
||||
--color-primary: #00b96b;
|
||||
--color-primary-soft: #00b96b99;
|
||||
--color-primary-mute: #00b96b33;
|
||||
|
||||
--color-text: var(--color-text-1);
|
||||
--color-icon: #ffffff99;
|
||||
--color-icon-white: #ffffff;
|
||||
@ -36,8 +40,8 @@
|
||||
--navbar-background: #1f1f1f;
|
||||
--navbar-height: 42px;
|
||||
--sidebar-width: 55px;
|
||||
--assistants-width: 250px;
|
||||
--topic-list-width: 250px;
|
||||
--assistants-width: 245px;
|
||||
--topic-list-width: 260px;
|
||||
--settings-width: var(--assistants-width);
|
||||
--status-bar-height: 40px;
|
||||
--input-bar-height: 125px;
|
||||
@ -103,29 +107,3 @@ body,
|
||||
#inputbar .ant-input {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
/* 全局初始化滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* Safari 和 Chrome */
|
||||
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||
body {
|
||||
scrollbar-width: thin; /* 告诉 FF 用细滚动条 */
|
||||
scrollbar-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.1); /* FF 前面色后面色 */
|
||||
}
|
||||
}
|
||||
|
||||
15
src/renderer/src/assets/styles/scrollbar.scss
Normal file
15
src/renderer/src/assets/styles/scrollbar.scss
Normal file
@ -0,0 +1,15 @@
|
||||
/* 全局初始化滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import { setAvatar } from '@renderer/store/runtime'
|
||||
import { runAsyncFunction } from '@renderer/utils'
|
||||
import { useEffect } from 'react'
|
||||
import { useSettings } from './useSettings'
|
||||
import { isWindows } from '@renderer/config/constant'
|
||||
|
||||
export function useAppInit() {
|
||||
const dispatch = useAppDispatch()
|
||||
@ -28,4 +29,8 @@ export function useAppInit() {
|
||||
useEffect(() => {
|
||||
proxyUrl && window.api.setProxy(proxyUrl)
|
||||
}, [proxyUrl])
|
||||
|
||||
useEffect(() => {
|
||||
isWindows && import('@renderer/assets/styles/scrollbar.scss')
|
||||
}, [])
|
||||
}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { toggleRightSidebar, toggleShowAssistants } from '@renderer/store/settings'
|
||||
import { setShowRightSidebar, toggleRightSidebar, toggleShowAssistants } from '@renderer/store/settings'
|
||||
|
||||
export function useShowRightSidebar() {
|
||||
const showRightSidebar = useAppSelector((state) => state.settings.showRightSidebar)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return {
|
||||
showRightSidebar,
|
||||
toggleRightSidebar: () => dispatch(toggleRightSidebar())
|
||||
rightSidebarShown: showRightSidebar,
|
||||
toggleRightSidebar: () => dispatch(toggleRightSidebar()),
|
||||
showRightSidebar: () => dispatch(setShowRightSidebar(true)),
|
||||
hideRightSidebar: () => dispatch(setShowRightSidebar(false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -51,8 +51,6 @@ const resources = {
|
||||
'default.description': "Hello, I'm Default Assistant. You can start chatting with me right away",
|
||||
'default.topic.name': 'Default Topic',
|
||||
'topics.title': 'Topics',
|
||||
'topics.hide_topics': 'Hide Topics',
|
||||
'topics.show_topics': 'Show Topics',
|
||||
'topics.auto_rename': 'Auto Rename',
|
||||
'topics.edit.title': 'Rename',
|
||||
'topics.edit.placeholder': 'Enter new name',
|
||||
@ -103,14 +101,15 @@ const resources = {
|
||||
model: 'Model Settings',
|
||||
assistant: 'Default Assistant',
|
||||
about: 'About & Feedback',
|
||||
'messages.model.title': 'Model Settings',
|
||||
'messages.title': 'Message Settings',
|
||||
'messages.divider': 'Show divider between messages',
|
||||
'messages.use_serif_font': 'Use serif font',
|
||||
'messages.input.title': 'Input Settings',
|
||||
'messages.input.show_estimated_tokens': 'Show estimated input tokens',
|
||||
'general.title': 'General Settings',
|
||||
'general.message.title': 'Message Settings',
|
||||
'general.message.divider': 'Show divider between messages',
|
||||
'general.message.use_serif_font': 'Use serif font',
|
||||
'general.user_name': 'User Name',
|
||||
'general.user_name.placeholder': 'Enter your name',
|
||||
'general.input.title': 'Input Settings',
|
||||
'general.input.show_estimated_tokens': 'Show estimated input tokens',
|
||||
'provider.api_key': 'API Key',
|
||||
'provider.check': 'Check',
|
||||
'provider.get_api_key': 'Get API Key',
|
||||
@ -203,8 +202,6 @@ const resources = {
|
||||
'default.description': '你好,我是默认助手。你可以立刻开始跟我聊天。',
|
||||
'default.topic.name': '默认话题',
|
||||
'topics.title': '话题',
|
||||
'topics.hide_topics': '隐藏话题',
|
||||
'topics.show_topics': '显示话题',
|
||||
'topics.auto_rename': 'AI 重命名',
|
||||
'topics.edit.title': '重命名',
|
||||
'topics.edit.placeholder': '输入新名称',
|
||||
@ -256,14 +253,15 @@ const resources = {
|
||||
model: '模型设置',
|
||||
assistant: '默认助手',
|
||||
about: '关于我们',
|
||||
'messages.model.title': '模型设置',
|
||||
'messages.title': '消息设置',
|
||||
'messages.divider': '消息分割线',
|
||||
'messages.use_serif_font': '使用衬线字体',
|
||||
'messages.input.title': '输入设置',
|
||||
'messages.input.show_estimated_tokens': '状态显示',
|
||||
'general.title': '常规设置',
|
||||
'general.user_name': '用户名',
|
||||
'general.user_name.placeholder': '请输入用户名',
|
||||
'general.message.title': '消息设置',
|
||||
'general.message.divider': '消息分割线',
|
||||
'general.message.use_serif_font': '使用衬线字体',
|
||||
'general.input.title': '输入设置',
|
||||
'general.input.show_estimated_tokens': '状态显示',
|
||||
'provider.api_key': 'API 密钥',
|
||||
'provider.check': '检查',
|
||||
'provider.get_api_key': '点击这里获取密钥',
|
||||
|
||||
@ -6,18 +6,15 @@ import Chat from './components/Chat'
|
||||
import Assistants from './components/Assistants'
|
||||
import { uuid } from '@renderer/utils'
|
||||
import { useShowAssistants, useShowRightSidebar } from '@renderer/hooks/useStore'
|
||||
import { Tooltip } from 'antd'
|
||||
import Navigation from './components/NavigationCenter'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { isMac, isWindows } from '@renderer/config/constant'
|
||||
|
||||
const HomePage: FC = () => {
|
||||
const { assistants, addAssistant } = useAssistants()
|
||||
const [activeAssistant, setActiveAssistant] = useState(assistants[0])
|
||||
const { showRightSidebar, toggleRightSidebar } = useShowRightSidebar()
|
||||
const { rightSidebarShown, toggleRightSidebar } = useShowRightSidebar()
|
||||
const { showAssistants, toggleShowAssistants } = useShowAssistants()
|
||||
const { defaultAssistant } = useDefaultAssistant()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const onCreateAssistant = () => {
|
||||
const assistant = { ...defaultAssistant, id: uuid() }
|
||||
@ -40,14 +37,9 @@ const HomePage: FC = () => {
|
||||
)}
|
||||
<Navigation activeAssistant={activeAssistant} />
|
||||
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 8 }}>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={showRightSidebar ? t('assistant.topics.hide_topics') : t('assistant.topics.show_topics')}
|
||||
arrow>
|
||||
<NewButton onClick={toggleRightSidebar}>
|
||||
<i className={`iconfont ${showRightSidebar ? 'icon-showsidebarhoriz' : 'icon-hidesidebarhoriz'}`} />
|
||||
</NewButton>
|
||||
</Tooltip>
|
||||
<NewButton onClick={toggleRightSidebar}>
|
||||
<i className={`iconfont ${rightSidebarShown ? 'icon-showsidebarhoriz' : 'icon-hidesidebarhoriz'}`} />
|
||||
</NewButton>
|
||||
</NavbarRight>
|
||||
</Navbar>
|
||||
<ContentContainer>
|
||||
|
||||
@ -4,7 +4,7 @@ import styled from 'styled-components'
|
||||
import Inputbar from './Inputbar'
|
||||
import Messages from './Messages'
|
||||
import { Flex } from 'antd'
|
||||
import Topics from './Topics'
|
||||
import RightSidebar from './RightSidebar'
|
||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||
import { useActiveTopic } from '@renderer/hooks/useTopic'
|
||||
|
||||
@ -22,7 +22,7 @@ const Chat: FC<Props> = (props) => {
|
||||
<Messages assistant={assistant} topic={activeTopic} />
|
||||
<Inputbar assistant={assistant} setActiveTopic={setActiveTopic} />
|
||||
</Flex>
|
||||
<Topics assistant={assistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
|
||||
<RightSidebar assistant={assistant} activeTopic={activeTopic} setActiveTopic={setActiveTopic} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
} from '@ant-design/icons'
|
||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
||||
import { getDefaultTopic } from '@renderer/services/assistant'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||
import store, { useAppSelector } from '@renderer/store'
|
||||
@ -24,7 +23,6 @@ import { debounce, isEmpty } from 'lodash'
|
||||
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
import AssistantSettings from './AssistantSettings'
|
||||
import SendMessageSetting from './SendMessageSetting'
|
||||
import { DEFAULT_CONEXTCOUNT } from '@renderer/config/constant'
|
||||
|
||||
@ -35,7 +33,6 @@ interface Props {
|
||||
|
||||
const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
||||
const [text, setText] = useState('')
|
||||
const { toggleRightSidebar } = useShowRightSidebar()
|
||||
const { addTopic } = useAssistant(assistant.id)
|
||||
const { sendMessageShortcut, showInputEstimatedTokens } = useSettings()
|
||||
const [expended, setExpend] = useState(false)
|
||||
@ -104,6 +101,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
||||
if (!generating) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'n') {
|
||||
addNewTopic()
|
||||
EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR)
|
||||
inputRef.current?.focus()
|
||||
}
|
||||
}
|
||||
@ -137,11 +135,6 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
||||
<PlusCircleOutlined />
|
||||
</ToolbarButton>
|
||||
</Tooltip>
|
||||
<Tooltip placement="top" title={t('assistant.input.topics')} arrow>
|
||||
<ToolbarButton type="text" onClick={toggleRightSidebar}>
|
||||
<HistoryOutlined />
|
||||
</ToolbarButton>
|
||||
</Tooltip>
|
||||
<Tooltip placement="top" title={t('assistant.input.clear')} arrow>
|
||||
<Popconfirm
|
||||
icon={false}
|
||||
@ -155,11 +148,16 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
||||
</ToolbarButton>
|
||||
</Popconfirm>
|
||||
</Tooltip>
|
||||
<AssistantSettings assistant={assistant}>
|
||||
<ToolbarButton type="text">
|
||||
<Tooltip placement="top" title={t('assistant.input.topics')} arrow>
|
||||
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR)}>
|
||||
<HistoryOutlined />
|
||||
</ToolbarButton>
|
||||
</Tooltip>
|
||||
<Tooltip placement="top" title={t('assistant.input.settings')} arrow>
|
||||
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS)}>
|
||||
<ControlOutlined />
|
||||
</ToolbarButton>
|
||||
</AssistantSettings>
|
||||
</Tooltip>
|
||||
<Tooltip placement="top" title={expended ? t('assistant.input.collapse') : t('assistant.input.expand')} arrow>
|
||||
<ToolbarButton type="text" onClick={() => setExpend(!expended)}>
|
||||
{expended ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
|
||||
|
||||
100
src/renderer/src/pages/home/components/RightSidebar.tsx
Normal file
100
src/renderer/src/pages/home/components/RightSidebar.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import { Assistant, Topic } from '@renderer/types'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import TopicsTab from './TopicsTab'
|
||||
import SettingsTab from './SettingsTab'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
||||
|
||||
interface Props {
|
||||
assistant: Assistant
|
||||
activeTopic: Topic
|
||||
setActiveTopic: (topic: Topic) => void
|
||||
}
|
||||
|
||||
const RightSidebar: FC<Props> = (props) => {
|
||||
const [tab, setTab] = useState<'topic' | 'settings'>('topic')
|
||||
const { rightSidebarShown, showRightSidebar, hideRightSidebar } = useShowRightSidebar()
|
||||
const { t } = useTranslation()
|
||||
const isTopicTab = tab === 'topic'
|
||||
const isSettingsTab = tab === 'settings'
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribes = [
|
||||
EventEmitter.on(EVENT_NAMES.SHOW_TOPIC_SIDEBAR, (): any => {
|
||||
if (rightSidebarShown && isTopicTab) {
|
||||
return hideRightSidebar()
|
||||
}
|
||||
if (rightSidebarShown) {
|
||||
return setTab('topic')
|
||||
}
|
||||
showRightSidebar()
|
||||
setTab('topic')
|
||||
}),
|
||||
EventEmitter.on(EVENT_NAMES.SHOW_CHAT_SETTINGS, (): any => {
|
||||
if (rightSidebarShown && isSettingsTab) {
|
||||
return hideRightSidebar()
|
||||
}
|
||||
if (rightSidebarShown) {
|
||||
return setTab('settings')
|
||||
}
|
||||
showRightSidebar()
|
||||
setTab('settings')
|
||||
})
|
||||
]
|
||||
return () => unsubscribes.forEach((unsub) => unsub())
|
||||
}, [hideRightSidebar, isSettingsTab, isTopicTab, rightSidebarShown, showRightSidebar])
|
||||
|
||||
return (
|
||||
<Container style={{ display: rightSidebarShown ? 'block' : 'none' }}>
|
||||
<Tabs>
|
||||
<Tab className={tab === 'topic' ? 'active' : ''} onClick={() => setTab('topic')}>
|
||||
{t('common.topics')}
|
||||
</Tab>
|
||||
<Tab className={tab === 'settings' ? 'active' : ''} onClick={() => setTab('settings')}>
|
||||
{t('settings.title')}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
{tab === 'topic' && <TopicsTab {...props} />}
|
||||
{tab === 'settings' && <SettingsTab assistant={props.assistant} />}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
width: var(--topic-list-width);
|
||||
height: 100%;
|
||||
border-left: 0.5px solid var(--color-border);
|
||||
overflow-y: auto;
|
||||
.collapsed {
|
||||
width: 0;
|
||||
border-left: none;
|
||||
}
|
||||
`
|
||||
|
||||
const Tabs = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
padding: 0 10px;
|
||||
`
|
||||
|
||||
const Tab = styled.div`
|
||||
padding: 8px 0;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
color: #8a8a8a;
|
||||
border-bottom: 1px solid transparent;
|
||||
&.active {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
`
|
||||
|
||||
export default RightSidebar
|
||||
@ -1,23 +1,31 @@
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons'
|
||||
import { Assistant } from '@renderer/types'
|
||||
import styled from 'styled-components'
|
||||
import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant'
|
||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||
import { Assistant } from '@renderer/types'
|
||||
import { Button, Col, InputNumber, Popover, Row, Slider, Tooltip } from 'antd'
|
||||
import { Button, Col, InputNumber, Row, Slider, Switch, Tooltip } from 'antd'
|
||||
import { debounce } from 'lodash'
|
||||
import { FC, PropsWithChildren, useCallback, useEffect, useState } from 'react'
|
||||
import { FC, useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
import { QuestionCircleOutlined } from '@ant-design/icons'
|
||||
import { SettingDivider, SettingRow, SettingRowTitle, SettingSubtitle } from '@renderer/pages/settings/components'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import { setMessageFont, setShowInputEstimatedTokens, setShowMessageDivider } from '@renderer/store/settings'
|
||||
|
||||
interface Props {
|
||||
assistant: Assistant
|
||||
}
|
||||
|
||||
const PopoverContent: FC<Props> = (props) => {
|
||||
const SettingsTab: FC<Props> = (props) => {
|
||||
const { assistant, updateAssistantSettings, updateAssistant } = useAssistant(props.assistant.id)
|
||||
const [temperature, setTemperature] = useState(assistant?.settings?.temperature ?? DEFAULT_TEMPERATURE)
|
||||
const [contextCount, setConextCount] = useState(assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const { showMessageDivider, messageFont, showInputEstimatedTokens } = useSettings()
|
||||
|
||||
const onUpdateAssistantSettings = useCallback(
|
||||
debounce(
|
||||
({ _temperature, _contextCount }: { _temperature?: number; _contextCount?: number }) => {
|
||||
@ -70,30 +78,29 @@ const PopoverContent: FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row align="middle" style={{ marginBottom: 10 }} gutter={20}>
|
||||
<Col span={6}>
|
||||
<Row align="middle" justify="end">
|
||||
<Label>{t('assistant.settings.temperature')}</Label>
|
||||
<Tooltip title={t('assistant.settings.temperature.tip')}>
|
||||
<QuestionIcon />
|
||||
</Tooltip>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={14}>
|
||||
<SettingSubtitle>{t('settings.messages.model.title')}</SettingSubtitle>
|
||||
<SettingDivider />
|
||||
<Row align="middle">
|
||||
<Label>{t('assistant.settings.conext_count')}</Label>
|
||||
<Tooltip title={t('assistant.settings.temperature.tip')}>
|
||||
<QuestionIcon />
|
||||
</Tooltip>
|
||||
</Row>
|
||||
<Row align="middle" gutter={10}>
|
||||
<Col span={18}>
|
||||
<Slider
|
||||
min={0}
|
||||
max={1.2}
|
||||
onChange={onTemperatureChange}
|
||||
value={typeof temperature === 'number' ? temperature : 0}
|
||||
marks={{ 0: '0', 0.7: '0.7', 1: '1', 1.2: '1.2' }}
|
||||
marks={{ 0: '0', 0.7: '0.7', 1.2: '1.2' }}
|
||||
step={0.1}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<InputNumber
|
||||
<Col span={6}>
|
||||
<InputNumberic
|
||||
min={0}
|
||||
max={1.2}
|
||||
style={{ width: 50, marginLeft: 5, textAlign: 'center' }}
|
||||
step={0.1}
|
||||
value={temperature}
|
||||
onChange={onTemperatureChange}
|
||||
@ -101,16 +108,14 @@ const PopoverContent: FC<Props> = (props) => {
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row align="middle" style={{ marginBottom: 10 }} gutter={20}>
|
||||
<Col span={6}>
|
||||
<Row align="middle" justify="end">
|
||||
<Label>{t('assistant.settings.conext_count')}</Label>
|
||||
<Tooltip title={t('assistant.settings.conext_count.tip')}>
|
||||
<QuestionIcon />
|
||||
</Tooltip>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={14}>
|
||||
<Row align="middle">
|
||||
<Label>{t('assistant.settings.conext_count')}</Label>
|
||||
<Tooltip title={t('assistant.settings.conext_count.tip')}>
|
||||
<QuestionIcon />
|
||||
</Tooltip>
|
||||
</Row>
|
||||
<Row align="middle" gutter={10}>
|
||||
<Col span={18}>
|
||||
<Slider
|
||||
min={0}
|
||||
max={20}
|
||||
@ -120,11 +125,10 @@ const PopoverContent: FC<Props> = (props) => {
|
||||
step={1}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={3}>
|
||||
<InputNumber
|
||||
<Col span={6}>
|
||||
<InputNumberic
|
||||
min={0}
|
||||
max={20}
|
||||
style={{ width: 50, marginLeft: 5, textAlign: 'center' }}
|
||||
step={1}
|
||||
value={contextCount}
|
||||
onChange={onConextCountChange}
|
||||
@ -132,49 +136,63 @@ const PopoverContent: FC<Props> = (props) => {
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row justify="center">
|
||||
<Button onClick={onReset}>{t('assistant.settings.reset')}</Button>
|
||||
</Row>
|
||||
<Button onClick={onReset} style={{ width: '100%' }}>
|
||||
{t('assistant.settings.reset')}
|
||||
</Button>
|
||||
<SettingSubtitle>{t('settings.messages.title')}</SettingSubtitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.messages.divider')}</SettingRowTitle>
|
||||
<Switch checked={showMessageDivider} onChange={(checked) => dispatch(setShowMessageDivider(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.messages.use_serif_font')}</SettingRowTitle>
|
||||
<Switch
|
||||
checked={messageFont === 'serif'}
|
||||
onChange={(checked) => dispatch(setMessageFont(checked ? 'serif' : 'system'))}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingSubtitle style={{ marginTop: 20 }}>{t('settings.messages.input.title')}</SettingSubtitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.messages.input.show_estimated_tokens')}</SettingRowTitle>
|
||||
<Switch
|
||||
checked={showInputEstimatedTokens}
|
||||
onChange={(checked) => dispatch(setShowInputEstimatedTokens(checked))}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const AssistantSettings: FC<Props & PropsWithChildren> = ({ children, assistant }) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Popover content={<PopoverContent assistant={assistant} />} trigger="click" onOpenChange={setOpen}>
|
||||
{open ? (
|
||||
children
|
||||
) : (
|
||||
<Tooltip placement="top" title={t('assistant.input.settings')} arrow>
|
||||
{children}
|
||||
</Tooltip>
|
||||
)}
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
width: 420px;
|
||||
padding: 5px;
|
||||
padding: 0 10px;
|
||||
`
|
||||
|
||||
const InputNumberic = styled(InputNumber)`
|
||||
width: 45px;
|
||||
padding: 0;
|
||||
margin-left: 5px;
|
||||
text-align: center;
|
||||
.ant-input-number-input {
|
||||
text-align: center;
|
||||
}
|
||||
`
|
||||
|
||||
const Label = styled.p`
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
margin-right: 8px;
|
||||
`
|
||||
|
||||
const QuestionIcon = styled(QuestionCircleOutlined)`
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-3);
|
||||
`
|
||||
|
||||
export default AssistantSettings
|
||||
export default SettingsTab
|
||||
@ -3,7 +3,7 @@ import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||
import { useShowRightSidebar } from '@renderer/hooks/useStore'
|
||||
import { fetchMessagesSummary } from '@renderer/services/api'
|
||||
import { Assistant, Topic } from '@renderer/types'
|
||||
import { Button, Dropdown, MenuProps, Popconfirm } from 'antd'
|
||||
import { Dropdown, MenuProps } from 'antd'
|
||||
import { FC } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { DeleteOutlined, EditOutlined, SignatureOutlined } from '@ant-design/icons'
|
||||
@ -19,9 +19,9 @@ interface Props {
|
||||
setActiveTopic: (topic: Topic) => void
|
||||
}
|
||||
|
||||
const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic }) => {
|
||||
const { showRightSidebar } = useShowRightSidebar()
|
||||
const { assistant, removeTopic, updateTopic, removeAllTopics, updateTopics } = useAssistant(_assistant.id)
|
||||
const TopicsTab: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic }) => {
|
||||
const { rightSidebarShown } = useShowRightSidebar()
|
||||
const { assistant, removeTopic, updateTopic, updateTopics } = useAssistant(_assistant.id)
|
||||
const { t } = useTranslation()
|
||||
const generating = useAppSelector((state) => state.runtime.generating)
|
||||
|
||||
@ -93,23 +93,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
||||
}
|
||||
|
||||
return (
|
||||
<Container style={{ display: showRightSidebar ? 'block' : 'none' }}>
|
||||
<TopicTitle>
|
||||
<span>
|
||||
{t('assistant.topics.title')} ({assistant.topics.length})
|
||||
</span>
|
||||
<Popconfirm
|
||||
icon={false}
|
||||
title={t('assistant.topics.delete.all.title')}
|
||||
description={t('assistant.topics.delete.all.content')}
|
||||
placement="leftBottom"
|
||||
onConfirm={removeAllTopics}
|
||||
okType="danger">
|
||||
<DeleteButton type="text">
|
||||
<DeleteIcon />
|
||||
</DeleteButton>
|
||||
</Popconfirm>
|
||||
</TopicTitle>
|
||||
<Container style={{ display: rightSidebarShown ? 'block' : 'none' }}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Droppable droppableId="droppable">
|
||||
{(provided) => (
|
||||
@ -138,15 +122,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
width: var(--topic-list-width);
|
||||
height: 100%;
|
||||
border-left: 0.5px solid var(--color-border);
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
&.collapsed {
|
||||
width: 0;
|
||||
border-left: none;
|
||||
}
|
||||
padding: 15px 10px;
|
||||
`
|
||||
|
||||
const TopicListItem = styled.div`
|
||||
@ -166,31 +142,4 @@ const TopicListItem = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const TopicTitle = styled.div`
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
color: var(--color-text-1);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const DeleteButton = styled(Button)`
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
padding: 0;
|
||||
&:hover {
|
||||
.anticon {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const DeleteIcon = styled(DeleteOutlined)`
|
||||
font-size: 16px;
|
||||
`
|
||||
|
||||
export default Topics
|
||||
export default TopicsTab
|
||||
@ -1,6 +1,6 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { SettingContainer, SettingDivider, SettingRow, SettingRowTitle, SettingTitle } from './components'
|
||||
import { Avatar, Input, Select, Switch, Upload } from 'antd'
|
||||
import { Avatar, Input, Select, Upload } from 'antd'
|
||||
import styled from 'styled-components'
|
||||
import LocalStorage from '@renderer/services/storage'
|
||||
import { compressImage, isValidProxyUrl } from '@renderer/utils'
|
||||
@ -8,27 +8,14 @@ import useAvatar from '@renderer/hooks/useAvatar'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import { setAvatar } from '@renderer/store/runtime'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import {
|
||||
setLanguage,
|
||||
setMessageFont,
|
||||
setShowInputEstimatedTokens,
|
||||
setShowMessageDivider,
|
||||
setUserName
|
||||
} from '@renderer/store/settings'
|
||||
import { setLanguage, setUserName } from '@renderer/store/settings'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { setProxyUrl as _setProxyUrl } from '@renderer/store/settings'
|
||||
import i18n from '@renderer/i18n'
|
||||
|
||||
const GeneralSettings: FC = () => {
|
||||
const avatar = useAvatar()
|
||||
const {
|
||||
language,
|
||||
proxyUrl: storeProxyUrl,
|
||||
userName,
|
||||
showMessageDivider,
|
||||
messageFont,
|
||||
showInputEstimatedTokens
|
||||
} = useSettings()
|
||||
const { language, proxyUrl: storeProxyUrl, userName } = useSettings()
|
||||
const [proxyUrl, setProxyUrl] = useState<string | undefined>(storeProxyUrl)
|
||||
const dispatch = useAppDispatch()
|
||||
const { t } = useTranslation()
|
||||
@ -110,31 +97,6 @@ const GeneralSettings: FC = () => {
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingTitle style={{ marginTop: 20 }}>{t('settings.general.message.title')}</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.general.message.divider')}</SettingRowTitle>
|
||||
<Switch checked={showMessageDivider} onChange={(checked) => dispatch(setShowMessageDivider(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.general.message.use_serif_font')}</SettingRowTitle>
|
||||
<Switch
|
||||
checked={messageFont === 'serif'}
|
||||
onChange={(checked) => dispatch(setMessageFont(checked ? 'serif' : 'system'))}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingTitle style={{ marginTop: 20 }}>{t('settings.general.input.title')}</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.general.input.show_estimated_tokens')}</SettingRowTitle>
|
||||
<Switch
|
||||
checked={showInputEstimatedTokens}
|
||||
onChange={(checked) => dispatch(setShowInputEstimatedTokens(checked))}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
</SettingContainer>
|
||||
)
|
||||
}
|
||||
|
||||
@ -39,10 +39,10 @@ export const SettingRow = styled.div`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 40px;
|
||||
`
|
||||
|
||||
export const SettingRowTitle = styled.div`
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
color: var(--color-text-1);
|
||||
`
|
||||
|
||||
@ -11,5 +11,7 @@ export const EVENT_NAMES = {
|
||||
EDIT_MESSAGE: 'EDIT_MESSAGE',
|
||||
REGENERATE_MESSAGE: 'REGENERATE_MESSAGE',
|
||||
CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED',
|
||||
ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT'
|
||||
ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT',
|
||||
SHOW_CHAT_SETTINGS: 'SHOW_CHAT_SETTINGS',
|
||||
SHOW_TOPIC_SIDEBAR: 'SHOW_TOPIC_SIDEBAR'
|
||||
}
|
||||
|
||||
@ -33,6 +33,9 @@ const settingsSlice = createSlice({
|
||||
toggleRightSidebar: (state) => {
|
||||
state.showRightSidebar = !state.showRightSidebar
|
||||
},
|
||||
setShowRightSidebar: (state, action: PayloadAction<boolean>) => {
|
||||
state.showRightSidebar = action.payload
|
||||
},
|
||||
toggleShowAssistants: (state) => {
|
||||
state.showAssistants = !state.showAssistants
|
||||
},
|
||||
@ -61,6 +64,7 @@ const settingsSlice = createSlice({
|
||||
})
|
||||
|
||||
export const {
|
||||
setShowRightSidebar,
|
||||
toggleRightSidebar,
|
||||
toggleShowAssistants,
|
||||
setSendMessageShortcut,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user