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:
parent
467e97ff4b
commit
eb18be200e
@ -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)
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
83
src/renderer/src/pages/home/Inputbar/TokenCount.tsx
Normal file
83
src/renderer/src/pages/home/Inputbar/TokenCount.tsx
Normal 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
|
||||
@ -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;
|
||||
|
||||
@ -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}>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user