feat: Improved UI components and added new features

- Replaced 'CopyOutlined' icon with custom 'CopyIcon'.
- Replaced Topics component with RightSidebar component to match topicPosition settings.
- Removed unused imports and updated UI components in the Inputbar.
- Implemented a new Token Count component for displaying context and estimated token information in the input bar.
- Adjusted the height of code block header.
- Added functionality to toggle theme opacity.
- Added functionality to dynamically change the sidebar border style based on stored settings.
- Updated CSS styles for dynamic topic list width and padding adjustments.
- Removed unused import and styles to improve code efficiency and reduce clutter.
This commit is contained in:
kangfenmao 2024-09-07 18:11:27 +08:00
parent 467e97ff4b
commit eb18be200e
9 changed files with 121 additions and 80 deletions

View File

@ -1,5 +1,6 @@
import { CopyOutlined, DeleteOutlined, EditOutlined, MinusCircleOutlined } from '@ant-design/icons'
import { DeleteOutlined, EditOutlined, MinusCircleOutlined } from '@ant-design/icons'
import DragableList from '@renderer/components/DragableList'
import CopyIcon from '@renderer/components/Icons/CopyIcon'
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
import { useShowTopics } from '@renderer/hooks/useStore'
@ -58,7 +59,7 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
{
label: t('common.duplicate'),
key: 'duplicate',
icon: <CopyOutlined />,
icon: <CopyIcon />,
onClick: async () => {
const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic()] }
addAssistant(_assistant)

View File

@ -1,6 +1,5 @@
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useSettings } from '@renderer/hooks/useSettings'
import { useShowTopics } from '@renderer/hooks/useStore'
import { Assistant, Topic } from '@renderer/types'
import { Flex } from 'antd'
import { FC } from 'react'
@ -8,7 +7,7 @@ import styled from 'styled-components'
import Inputbar from './Inputbar/Inputbar'
import Messages from './Messages/Messages'
import Topics from './Topics'
import RightSidebar from './RightSidebar'
interface Props {
assistant: Assistant
@ -18,20 +17,19 @@ interface Props {
const Chat: FC<Props> = (props) => {
const { assistant } = useAssistant(props.assistant.id)
const { showTopics } = useShowTopics()
const { topicPosition } = useSettings()
return (
<Container id="chat">
{showTopics && topicPosition === 'left' && (
<Topics assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
{topicPosition === 'left' && (
<RightSidebar assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
)}
<Main vertical flex={1} justify="space-between">
<Messages assistant={assistant} topic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
<Inputbar assistant={assistant} setActiveTopic={props.setActiveTopic} />
</Main>
{showTopics && topicPosition === 'right' && (
<Topics assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
{topicPosition === 'right' && (
<RightSidebar assistant={assistant} activeTopic={props.activeTopic} setActiveTopic={props.setActiveTopic} />
)}
</Container>
)

View File

@ -1,17 +1,15 @@
import {
ClearOutlined,
ControlOutlined,
FormOutlined,
FullscreenExitOutlined,
FullscreenOutlined,
HistoryOutlined,
PauseCircleOutlined,
PlusCircleOutlined,
QuestionCircleOutlined
} from '@ant-design/icons'
import { isWindows } from '@renderer/config/constant'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useSettings } from '@renderer/hooks/useSettings'
import { useShowTopics } from '@renderer/hooks/useStore'
import { getDefaultTopic } from '@renderer/services/assistant'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { estimateInputTokenCount } from '@renderer/services/messages'
@ -19,7 +17,7 @@ import store, { useAppSelector } from '@renderer/store'
import { setGenerating } from '@renderer/store/runtime'
import { Assistant, Message, Topic } from '@renderer/types'
import { delay, uuid } from '@renderer/utils'
import { Button, Divider, Popconfirm, Popover, Tag, Tooltip } from 'antd'
import { Button, Popconfirm, Tooltip } from 'antd'
import TextArea, { TextAreaRef } from 'antd/es/input/TextArea'
import dayjs from 'dayjs'
import { debounce, isEmpty } from 'lodash'
@ -27,8 +25,8 @@ import { CSSProperties, FC, useCallback, useEffect, useMemo, useRef, useState }
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import SettingsTab from '../Settings'
import SendMessageButton from './SendMessageButton'
import TokenCount from './TokenCount'
interface Props {
assistant: Assistant
@ -41,7 +39,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
const [text, setText] = useState(_text)
const [inputFocus, setInputFocus] = useState(false)
const { addTopic } = useAssistant(assistant.id)
const { sendMessageShortcut, showInputEstimatedTokens, fontSize } = useSettings()
const { sendMessageShortcut, fontSize } = useSettings()
const [expended, setExpend] = useState(false)
const [estimateTokenCount, setEstimateTokenCount] = useState(0)
const [contextCount, setContextCount] = useState(0)
@ -50,7 +48,6 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
const [files, setFiles] = useState<File[]>([])
const { t } = useTranslation()
const containerRef = useRef(null)
const { toggleShowTopics } = useShowTopics()
_text = text
@ -221,12 +218,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
<ToolbarMenu>
<Tooltip placement="top" title={t('chat.input.new_topic')} arrow>
<ToolbarButton type="text" onClick={addNewTopic}>
<FormOutlined />
</ToolbarButton>
</Tooltip>
<Tooltip placement="top" title={t('chat.input.new.context')} arrow>
<ToolbarButton type="text" onClick={onNewContext}>
<i className="iconfont icon-grid-row-2copy" />
<PlusCircleOutlined />
</ToolbarButton>
</Tooltip>
<Tooltip placement="top" title={t('chat.input.clear')} arrow>
@ -243,36 +235,28 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
</Popconfirm>
</Tooltip>
<Tooltip placement="top" title={t('chat.input.topics')} arrow>
<ToolbarButton type="text" onClick={toggleShowTopics}>
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_TOPIC_SIDEBAR)}>
<HistoryOutlined />
</ToolbarButton>
</Tooltip>
<Popover content={<SettingsTab assistant={assistant} />} trigger="click" placement="topRight">
<Tooltip placement="top" title={t('chat.input.settings')} arrow>
<ToolbarButton type="text">
<ToolbarButton type="text" onClick={() => EventEmitter.emit(EVENT_NAMES.SHOW_CHAT_SETTINGS)}>
<ControlOutlined />
</ToolbarButton>
</Tooltip>
</Popover>
{/* <AttachmentButton files={files} setFiles={setFiles} ToolbarButton={ToolbarButton} /> */}
<Tooltip placement="top" title={expended ? t('chat.input.collapse') : t('chat.input.expand')} arrow>
<ToolbarButton type="text" onClick={onToggleExpended}>
{expended ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
</ToolbarButton>
</Tooltip>
{showInputEstimatedTokens && (
<TextCount>
<Tooltip title={t('chat.input.context_count.tip') + ' | ' + t('chat.input.estimated_tokens.tip')}>
<StyledTag>
<span style={isWindows ? { fontFamily: 'serif', marginRight: 2 } : { marginRight: 3 }}></span>
{contextCount}
<Divider type="vertical" style={{ marginTop: 2, marginLeft: 5, marginRight: 5 }} />{inputTokenCount}
<span style={{ margin: '0 2px', fontSize: 10 }}>/</span>
{estimateTokenCount}
</StyledTag>
</Tooltip>
</TextCount>
)}
<TokenCount
estimateTokenCount={estimateTokenCount}
inputTokenCount={inputTokenCount}
contextCount={contextCount}
ToolbarButton={ToolbarButton}
onClick={onNewContext}
/>
</ToolbarMenu>
<ToolbarMenu>
{generating && (
@ -291,8 +275,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
const TextareaStyle: CSSProperties = {
paddingLeft: 0,
padding: '10px 15px 8px',
transition: 'all 0.3s ease'
padding: '10px 15px 8px'
}
const Container = styled.div`
@ -346,9 +329,6 @@ const ToolbarButton = styled(Button)`
justify-content: center;
align-items: center;
padding: 0;
.iconfont {
font-size: 17px;
}
&.anticon,
&.iconfont {
transition: all 0.3s ease;
@ -364,26 +344,4 @@ const ToolbarButton = styled(Button)`
}
`
const TextCount = styled.div`
font-size: 11px;
color: var(--color-text-3);
z-index: 10;
padding: 2px;
border-top-left-radius: 7px;
user-select: none;
`
const StyledTag = styled(Tag)`
cursor: pointer;
border-radius: 20px;
display: flex;
align-items: center;
padding: 2px 8px;
border-width: 0.5;
margin: 0;
height: 25px;
font-size: 12px;
line-height: 16px;
`
export default Inputbar

View File

@ -0,0 +1,83 @@
import { ArrowUpOutlined, MenuOutlined, PicCenterOutlined } from '@ant-design/icons'
import { HStack, VStack } from '@renderer/components/Layout'
import { useSettings } from '@renderer/hooks/useSettings'
import { Divider, Popover, Tooltip } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
type Props = {
estimateTokenCount: number
inputTokenCount: number
contextCount: number
ToolbarButton: any
} & React.HTMLAttributes<HTMLDivElement>
const TokenCount: FC<Props> = ({ estimateTokenCount, inputTokenCount, contextCount, ToolbarButton, ...props }) => {
const { t } = useTranslation()
const { showInputEstimatedTokens } = useSettings()
if (!showInputEstimatedTokens) {
return null
}
const PopoverContent = () => {
return (
<VStack w="150px" background="100%">
<HStack justifyContent="space-between" w="100%">
<Text>{t('chat.input.context_count.tip')}</Text>
<Text>{contextCount}</Text>
</HStack>
<Divider style={{ margin: '5px 0' }} />
<HStack justifyContent="space-between" w="100%">
<Text>{t('chat.input.estimated_tokens.tip')}</Text>
<Text>{estimateTokenCount}</Text>
</HStack>
</VStack>
)
}
return (
<>
<ToolbarButton type="text" onClick={props.onClick}>
<Tooltip placement="top" title={t('chat.input.new.context')}>
<PicCenterOutlined />
</Tooltip>
</ToolbarButton>
<Container {...props}>
<Popover content={PopoverContent} title="" mouseEnterDelay={0.6}>
<MenuOutlined /> {contextCount}
<Divider type="vertical" style={{ marginTop: 0, marginLeft: 5, marginRight: 5 }} />
<ArrowUpOutlined />
{inputTokenCount} / {estimateTokenCount}
</Popover>
</Container>
</>
)
}
const Container = styled.div`
font-size: 11px;
line-height: 16px;
color: var(--color-text-2);
z-index: 10;
padding: 3px 10px;
user-select: none;
font-family: Ubuntu;
border: 0.5px solid var(--color-text-3);
border-radius: 20px;
display: flex;
align-items: center;
cursor: pointer;
.anticon {
font-size: 10px;
margin-right: 3px;
}
`
const Text = styled.div`
font-size: 12px;
color: var(--color-text-1);
`
export default TokenCount

View File

@ -72,7 +72,7 @@ const CodeHeader = styled.div`
font-size: 14px;
font-weight: bold;
background-color: var(--color-code-background);
height: 40px;
height: 36px;
padding: 0 10px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;

View File

@ -103,7 +103,6 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, setActiv
unCheckedChildren={<i className="iconfont icon-theme icon-theme-light" />}
checked={theme === 'dark'}
onChange={toggleTheme}
style={{ opacity: theme === 'dark' ? 0.6 : 1 }}
/>
{topicPosition === 'right' && (
<NewButton onClick={toggleShowTopics}>

View File

@ -1,4 +1,5 @@
import { BarsOutlined, SettingOutlined } from '@ant-design/icons'
import { useSettings } from '@renderer/hooks/useSettings'
import { useShowTopics } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { Assistant, Topic } from '@renderer/types'
@ -19,9 +20,12 @@ interface Props {
const RightSidebar: FC<Props> = (props) => {
const [tab, setTab] = useState<'topic' | 'settings'>('topic')
const { showTopics, setShowTopics } = useShowTopics()
const { topicPosition } = useSettings()
const { t } = useTranslation()
const isTopicTab = tab === 'topic'
const isSettingsTab = tab === 'settings'
const borderStyle = '0.5px solid var(--color-border)'
useEffect(() => {
const unsubscribes = [
@ -55,7 +59,7 @@ const RightSidebar: FC<Props> = (props) => {
}
return (
<Container>
<Container style={topicPosition === 'left' ? { borderRight: borderStyle } : { borderLeft: borderStyle }}>
<Segmented
value={tab}
style={{ borderRadius: 0, padding: '10px', gap: 5, borderBottom: '0.5px solid var(--color-border)' }}
@ -79,7 +83,6 @@ const Container = styled.div`
flex-direction: column;
width: var(--topic-list-width);
height: calc(100vh - var(--navbar-height));
border-left: 0.5px solid var(--color-border);
.collapsed {
width: 0;
border-left: none;
@ -91,6 +94,7 @@ const TabContent = styled.div`
flex: 1;
flex-direction: column;
overflow-y: auto;
overflow-x: hidden;
`
export default RightSidebar

View File

@ -235,8 +235,10 @@ const Container = styled.div`
flex: 1;
flex-direction: column;
overflow: hidden;
min-width: 300px;
padding-bottom: 10px;
min-width: var(--topic-list-width);
max-width: var(--topic-list-width);
padding: 10px 15px;
`
const Label = styled.p`

View File

@ -2,7 +2,6 @@ import { DeleteOutlined, EditOutlined, OpenAIOutlined } from '@ant-design/icons'
import DragableList from '@renderer/components/DragableList'
import PromptPopup from '@renderer/components/Popups/PromptPopup'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useSettings } from '@renderer/hooks/useSettings'
import { fetchMessagesSummary } from '@renderer/services/api'
import LocalStorage from '@renderer/services/storage'
import { useAppSelector } from '@renderer/store'
@ -22,9 +21,6 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
const { assistant, removeTopic, updateTopic, updateTopics } = useAssistant(_assistant.id)
const { t } = useTranslation()
const generating = useAppSelector((state) => state.runtime.generating)
const { topicPosition } = useSettings()
const borderStyle = '0.5px solid var(--color-border)'
const getTopicMenuItems = useCallback(
(topic: Topic) => {
@ -92,7 +88,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
)
return (
<Container style={topicPosition === 'left' ? { borderRight: borderStyle } : { borderLeft: borderStyle }}>
<Container>
<DragableList list={assistant.topics} onUpdate={updateTopics}>
{(topic) => {
const isActive = topic.id === activeTopic?.id