From 1866b00265a67c0437331b82f07b70956253e1e1 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Fri, 16 Aug 2024 17:19:18 +0800 Subject: [PATCH] feat: add user edit modal & add prompt block --- src/renderer/src/assets/styles/index.scss | 4 +- .../Popups}/AddAssistantPopup.tsx | 0 .../src/components/Popups/UserPopup.tsx | 108 ++++++++++++++++++ src/renderer/src/components/app/Sidebar.tsx | 11 +- src/renderer/src/pages/agents/AgentsPage.tsx | 4 +- .../src/pages/agents/components/AgentCard.tsx | 21 ++-- .../pages/agents/components/UserAgents.tsx | 4 +- src/renderer/src/pages/home/Assistants.tsx | 2 +- src/renderer/src/pages/home/Chat.tsx | 2 +- src/renderer/src/pages/home/HomePage.tsx | 2 +- .../src/pages/home/Inputbar/Inputbar.tsx | 2 +- .../src/pages/home/{ => Messages}/Message.tsx | 6 +- .../pages/home/{ => Messages}/Messages.tsx | 20 +--- .../src/pages/home/Messages/Prompt.tsx | 54 +++++++++ .../src/pages/home/RightSidebar/TopicsTab.tsx | 3 +- .../ProviderSettings/ProviderSetting.tsx | 6 +- .../src/pages/translate/TranslatePage.tsx | 2 +- src/renderer/src/providers/AntdProvider.tsx | 3 +- src/renderer/src/services/api.ts | 2 +- 19 files changed, 203 insertions(+), 53 deletions(-) rename src/renderer/src/{pages/home/components => components/Popups}/AddAssistantPopup.tsx (100%) create mode 100644 src/renderer/src/components/Popups/UserPopup.tsx rename src/renderer/src/pages/home/{ => Messages}/Message.tsx (98%) rename src/renderer/src/pages/home/{ => Messages}/Messages.tsx (88%) create mode 100644 src/renderer/src/pages/home/Messages/Prompt.tsx diff --git a/src/renderer/src/assets/styles/index.scss b/src/renderer/src/assets/styles/index.scss index 1a046469..904ce2f4 100644 --- a/src/renderer/src/assets/styles/index.scss +++ b/src/renderer/src/assets/styles/index.scss @@ -31,7 +31,8 @@ --color-text: var(--color-text-1); --color-icon: #ffffff99; --color-icon-white: #ffffff; - --color-border: #ffffff20; + --color-border: #000; + --color-border-soft: #ffffff20; --color-error: #f44336; --color-link: #1677ff; --color-code-background: #323232; @@ -80,6 +81,7 @@ body[theme-mode='light'] { --color-icon: #00000099; --color-icon-white: #000000; --color-border: #00000028; + --color-border-soft: #00000028; --color-error: #f44336; --color-link: #1677ff; --color-code-background: #e3e3e3; diff --git a/src/renderer/src/pages/home/components/AddAssistantPopup.tsx b/src/renderer/src/components/Popups/AddAssistantPopup.tsx similarity index 100% rename from src/renderer/src/pages/home/components/AddAssistantPopup.tsx rename to src/renderer/src/components/Popups/AddAssistantPopup.tsx diff --git a/src/renderer/src/components/Popups/UserPopup.tsx b/src/renderer/src/components/Popups/UserPopup.tsx new file mode 100644 index 00000000..79ee2652 --- /dev/null +++ b/src/renderer/src/components/Popups/UserPopup.tsx @@ -0,0 +1,108 @@ +import useAvatar from '@renderer/hooks/useAvatar' +import { useSettings } from '@renderer/hooks/useSettings' +import LocalStorage from '@renderer/services/storage' +import { useAppDispatch } from '@renderer/store' +import { setAvatar } from '@renderer/store/runtime' +import { setUserName } from '@renderer/store/settings' +import { compressImage } from '@renderer/utils' +import { Avatar, Input, Modal, Upload } from 'antd' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +import { Center, HStack } from '../Layout' +import { TopView } from '../TopView' + +interface Props { + resolve: (data: any) => void +} + +const PopupContainer: React.FC = ({ resolve }) => { + const [open, setOpen] = useState(true) + const { t } = useTranslation() + const { userName } = useSettings() + const dispatch = useAppDispatch() + const avatar = useAvatar() + + const onOk = () => { + setOpen(false) + } + + const onCancel = () => { + setOpen(false) + } + + const onClose = () => { + resolve({}) + } + + return ( + +
+ {}} + accept="image/png, image/jpeg" + itemRender={() => null} + maxCount={1} + onChange={async ({ file }) => { + try { + const _file = file.originFileObj as File + const compressedFile = await compressImage(_file) + await LocalStorage.storeImage('avatar', compressedFile) + dispatch(setAvatar(await LocalStorage.getImage('avatar'))) + } catch (error: any) { + window.message.error(error.message) + } + }}> + + +
+ + dispatch(setUserName(e.target.value))} + style={{ flex: 1, textAlign: 'center', width: '100%' }} + maxLength={30} + /> + +
+ ) +} + +const UserAvatar = styled(Avatar)` + cursor: pointer; + width: 80px; + height: 80px; + transition: opacity 0.3s ease; + &:hover { + opacity: 0.8; + } +` + +export default class UserPopup { + static topviewId = 0 + static hide() { + TopView.hide('UserPopup') + } + static show() { + return new Promise((resolve) => { + TopView.show( + { + resolve(v) + this.hide() + }} + />, + 'UserPopup' + ) + }) + } +} diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx index 1d98a69f..58bc37d5 100644 --- a/src/renderer/src/components/app/Sidebar.tsx +++ b/src/renderer/src/components/app/Sidebar.tsx @@ -8,6 +8,8 @@ import { FC } from 'react' import { Link, useLocation } from 'react-router-dom' import styled from 'styled-components' +import UserPopup from '../Popups/UserPopup' + const Sidebar: FC = () => { const { pathname } = useLocation() const avatar = useAvatar() @@ -15,11 +17,13 @@ const Sidebar: FC = () => { const isRoute = (path: string): string => (pathname === path ? 'active' : '') + const onEditUser = () => { + UserPopup.show() + } + return ( - - - + @@ -71,6 +75,7 @@ const AvatarImg = styled(Avatar)` margin-bottom: ${isMac ? '12px' : '12px'}; margin-top: ${isMac ? '5px' : '2px'}; border: none; + cursor: pointer; ` const MainMenus = styled.div` display: flex; diff --git a/src/renderer/src/pages/agents/AgentsPage.tsx b/src/renderer/src/pages/agents/AgentsPage.tsx index bf389311..634fbe00 100644 --- a/src/renderer/src/pages/agents/AgentsPage.tsx +++ b/src/renderer/src/pages/agents/AgentsPage.tsx @@ -56,13 +56,13 @@ const AppsPage: FC = () => { - {t('agents.my_agents')} + {t('agents.my_agents')} {agents.length > 0 && } {Object.keys(agentGroups).map((group) => (
- + <Title level={4} key={group} style={{ marginBottom: 16 }}> {group} diff --git a/src/renderer/src/pages/agents/components/AgentCard.tsx b/src/renderer/src/pages/agents/components/AgentCard.tsx index cf262432..f12f1399 100644 --- a/src/renderer/src/pages/agents/components/AgentCard.tsx +++ b/src/renderer/src/pages/agents/components/AgentCard.tsx @@ -1,5 +1,5 @@ import { Agent } from '@renderer/types' -import { Col, Typography } from 'antd' +import { Col } from 'antd' import styled from 'styled-components' interface Props { @@ -7,17 +7,13 @@ interface Props { onClick?: () => void } -const { Title } = Typography - const AgentCard: React.FC = ({ agent, onClick }) => { return ( {agent.emoji && {agent.emoji}} - - {agent.name} - + {agent.name} {agent.prompt} @@ -41,14 +37,14 @@ const Container = styled.div` } ` const EmojiHeader = styled.div` - width: 25px; + width: 20px; display: flex; flex-direction: row; justify-content: center; align-items: center; margin-right: 5px; - font-size: 25px; - line-height: 25px; + font-size: 20px; + line-height: 20px; ` const AgentHeader = styled.div` @@ -58,15 +54,13 @@ const AgentHeader = styled.div` align-items: center; ` -const AgentName = styled(Title)` - font-size: 18px; +const AgentName = styled.div` line-height: 1.2; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; - color: var(--color-white); - font-weight: 900; + color: var(--color-text-1); ` const AgentCardPrompt = styled.div` @@ -76,6 +70,7 @@ const AgentCardPrompt = styled.div` -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; + font-size: 12px; ` export default AgentCard diff --git a/src/renderer/src/pages/agents/components/UserAgents.tsx b/src/renderer/src/pages/agents/components/UserAgents.tsx index 880c1345..2eb738ce 100644 --- a/src/renderer/src/pages/agents/components/UserAgents.tsx +++ b/src/renderer/src/pages/agents/components/UserAgents.tsx @@ -41,10 +41,10 @@ const AssistantCardContainer = styled.div` align-items: center; justify-content: center; padding: 20px; - border: 1px dashed var(--color-border); + border: 1px dashed var(--color-border-soft); border-radius: 10px; cursor: pointer; - min-height: 84px; + min-height: 72px; .anticon { font-size: 16px; color: var(--color-icon); diff --git a/src/renderer/src/pages/home/Assistants.tsx b/src/renderer/src/pages/home/Assistants.tsx index 5c165e38..9a1ba0de 100644 --- a/src/renderer/src/pages/home/Assistants.tsx +++ b/src/renderer/src/pages/home/Assistants.tsx @@ -145,7 +145,7 @@ const AssistantItem = styled.div` flex-direction: column; padding: 7px 10px; position: relative; - border-radius: 8px; + border-radius: 4px; cursor: pointer; font-family: Ubuntu; .anticon { diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx index 7e27f840..70e576b1 100644 --- a/src/renderer/src/pages/home/Chat.tsx +++ b/src/renderer/src/pages/home/Chat.tsx @@ -6,7 +6,7 @@ import { FC } from 'react' import styled from 'styled-components' import Inputbar from './Inputbar/Inputbar' -import Messages from './Messages' +import Messages from './Messages/Messages' import RightSidebar from './RightSidebar' interface Props { diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index e4225506..0bcab92d 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -9,9 +9,9 @@ import { Switch } from 'antd' import { FC, useState } from 'react' import styled from 'styled-components' +import AddAssistantPopup from '../../components/Popups/AddAssistantPopup' import Assistants from './Assistants' import Chat from './Chat' -import AddAssistantPopup from './components/AddAssistantPopup' import Navigation from './Header' let _activeAssistant: Assistant diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 25ccc698..e35ee8a9 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -240,7 +240,7 @@ const Container = styled.div` display: flex; flex-direction: column; height: var(--input-bar-height); - border: 1px solid var(--color-border); + border: 1px solid var(--color-border-soft); transition: all 0.3s ease; position: relative; margin: 0 20px 15px 20px; diff --git a/src/renderer/src/pages/home/Message.tsx b/src/renderer/src/pages/home/Messages/Message.tsx similarity index 98% rename from src/renderer/src/pages/home/Message.tsx rename to src/renderer/src/pages/home/Messages/Message.tsx index 168d95c8..24f3448f 100644 --- a/src/renderer/src/pages/home/Message.tsx +++ b/src/renderer/src/pages/home/Messages/Message.tsx @@ -23,8 +23,8 @@ import { FC, memo, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' -import SelectModelDropdown from './components/SelectModelDropdown' -import Markdown from './Markdown/Markdown' +import SelectModelDropdown from '../components/SelectModelDropdown' +import Markdown from '../Markdown/Markdown' interface Props { message: Message @@ -208,7 +208,7 @@ const MessageContainer = styled.div` &.user { position: absolute; top: 10px; - right: 10px; + right: 15px; } } &:hover { diff --git a/src/renderer/src/pages/home/Messages.tsx b/src/renderer/src/pages/home/Messages/Messages.tsx similarity index 88% rename from src/renderer/src/pages/home/Messages.tsx rename to src/renderer/src/pages/home/Messages/Messages.tsx index 9a6720f2..f608e192 100644 --- a/src/renderer/src/pages/home/Messages.tsx +++ b/src/renderer/src/pages/home/Messages/Messages.tsx @@ -10,11 +10,12 @@ import { getBriefInfo, runAsyncFunction, uuid } from '@renderer/utils' import { t } from 'i18next' import localforage from 'localforage' import { last, reverse } from 'lodash' -import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { FC, useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' -import Suggestions from './components/Suggestions' +import Suggestions from '../components/Suggestions' import MessageItem from './Message' +import Prompt from './Prompt' interface Props { assistant: Assistant @@ -28,19 +29,6 @@ const Messages: FC = ({ assistant, topic }) => { const containerRef = useRef(null) const { updateTopic } = useAssistant(assistant.id) - const assistantDefaultMessage: Message = useMemo( - () => ({ - id: 'assistant', - role: 'assistant', - content: assistant.description || assistant.prompt || t('chat.default.description'), - assistantId: assistant.id, - topicId: topic.id, - status: 'pending', - createdAt: new Date().toISOString() - }), - [assistant.description, assistant.id, assistant.prompt, topic.id] - ) - const onSendMessage = useCallback( (message: Message) => { const _messages = [...messages, message] @@ -123,7 +111,7 @@ const Messages: FC = ({ assistant, topic }) => { {reverse([...messages]).map((message, index) => ( ))} - + ) } diff --git a/src/renderer/src/pages/home/Messages/Prompt.tsx b/src/renderer/src/pages/home/Messages/Prompt.tsx new file mode 100644 index 00000000..214a6eb1 --- /dev/null +++ b/src/renderer/src/pages/home/Messages/Prompt.tsx @@ -0,0 +1,54 @@ +import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' +import { useAssistant } from '@renderer/hooks/useAssistant' +import { syncAsistantToAgent } from '@renderer/services/assistant' +import { Assistant } from '@renderer/types' +import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +interface Props { + assistant: Assistant +} + +const Prompt: FC = ({ assistant }) => { + const { t } = useTranslation() + const { updateAssistant } = useAssistant(assistant.id) + + const prompt = assistant.prompt || t('chat.default.description') + + const onEdit = async () => { + const _assistant = await AssistantSettingPopup.show({ assistant }) + updateAssistant(_assistant) + syncAsistantToAgent(_assistant) + } + + if (!prompt) { + return null + } + + return ( + + {prompt} + + ) +} + +const Container = styled.div` + padding: 10px 20px; + background-color: var(--color-background-soft); + margin-bottom: 20px; + margin: 0 20px 20px 20px; + border-radius: 6px; + cursor: pointer; +` + +const Text = styled.div` + color: var(--color-text-3); + font-size: 12px; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +` + +export default Prompt diff --git a/src/renderer/src/pages/home/RightSidebar/TopicsTab.tsx b/src/renderer/src/pages/home/RightSidebar/TopicsTab.tsx index 3b173417..a506137a 100644 --- a/src/renderer/src/pages/home/RightSidebar/TopicsTab.tsx +++ b/src/renderer/src/pages/home/RightSidebar/TopicsTab.tsx @@ -142,12 +142,11 @@ const Container = styled.div` const TopicListItem = styled.div` padding: 7px 10px; cursor: pointer; - border-radius: 8px; + border-radius: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: Ubuntu; - transition: all 0.3s; &:hover { background-color: var(--color-background-soft); } diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx index e2da4dbe..b8f07c40 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx @@ -95,7 +95,7 @@ const ProviderSetting: FC = ({ provider: _provider }) => { {t('settings.provider.api_key')} - + = ({ provider: _provider }) => { )} {t('settings.provider.api_host')} - + = ({ provider: _provider }) => { {apiEditable && } {provider.id === 'ollama' && } - {t('common.models')} + {t('common.models')} {Object.keys(modelGroups).map((group) => ( {modelGroups[group].map((model) => ( diff --git a/src/renderer/src/pages/translate/TranslatePage.tsx b/src/renderer/src/pages/translate/TranslatePage.tsx index 449feddb..5b60152e 100644 --- a/src/renderer/src/pages/translate/TranslatePage.tsx +++ b/src/renderer/src/pages/translate/TranslatePage.tsx @@ -257,7 +257,7 @@ const InputContainer = styled.div` flex: 1; flex-direction: column; height: 100%; - border: 1px solid var(--color-border); + border: 1px solid var(--color-border-soft); border-radius: 10px; ` diff --git a/src/renderer/src/providers/AntdProvider.tsx b/src/renderer/src/providers/AntdProvider.tsx index 009ff57f..1a9e8e78 100644 --- a/src/renderer/src/providers/AntdProvider.tsx +++ b/src/renderer/src/providers/AntdProvider.tsx @@ -23,8 +23,7 @@ const AntdProvider: FC = ({ children }) => { } }, token: { - colorPrimary: '#00b96b', - borderRadius: 8 + colorPrimary: '#00b96b' } }}> {children} diff --git a/src/renderer/src/services/api.ts b/src/renderer/src/services/api.ts index 9a61753e..896f427a 100644 --- a/src/renderer/src/services/api.ts +++ b/src/renderer/src/services/api.ts @@ -123,7 +123,7 @@ export async function fetchMessagesSummary({ messages, assistant }: { messages: const providerSdk = new ProviderSDK(provider) try { - return await providerSdk.summaries(messages, assistant) + return await providerSdk.summaries(filterMessages(messages), assistant) } catch (error: any) { return null }