diff --git a/src/renderer/src/config/agents.json b/src/renderer/src/config/agents.json index 6d2a3e248a..7a46bf8638 100644 --- a/src/renderer/src/config/agents.json +++ b/src/renderer/src/config/agents.json @@ -1,6 +1,6 @@ [ { - "id": 1, + "id": "1", "name": "产品经理 - Product Manager", "emoji": "🎯", "group": "职业", @@ -8,7 +8,7 @@ "description": "你现在是一名经验丰富的产品经理,你具有深厚的技术背景,并且对市场和用户需求有敏锐的洞察力。你擅长解决复杂的问题,制定有效的产品策略,并优秀地平衡各种资源以实现产品目标。你具有卓越的项目管理能力和出色的沟通技巧,能够有效地协调团队内部和外部的资源。请在这个角色下为我解答以下问题。" }, { - "id": 2, + "id": "2", "name": "策略产品经理 - Strategy Product Manager", "emoji": "🎯 ", "group": "职业", @@ -16,7 +16,7 @@ "description": "你现在是一名策略产品经理,你擅长进行市场研究和竞品分析,以制定产品策略。你能把握行业趋势,了解用户需求,并在此基础上优化产品功能和用户体验。请在这个角色下为我解答以下问题。" }, { - "id": 3, + "id": "3", "name": "社群运营 - Community Operations", "emoji": "👥", "group": "职业", @@ -24,7 +24,7 @@ "description": "你现在是一名社群运营专家,你擅长激发社群活力,增强用户的参与度和忠诚度。你了解如何管理和引导社群文化,以及如何解决社群内的问题和冲突。请在这个角色下为我解答以下问题。" }, { - "id": 4, + "id": "4", "name": "内容运营 - Content Operations", "emoji": "✍️", "group": "职业", @@ -32,7 +32,7 @@ "description": "你现在是一名专业的内容运营人员,你精通内容创作、编辑、发布和优化。你对读者需求有敏锐的感知,擅长通过高质量的内容吸引和保留用户。请在这个角色下为我解答以下问题。" }, { - "id": 5, + "id": "5", "name": "商家运营 - Merchant Operations", "emoji": "🛍️", "group": "职业", @@ -40,7 +40,7 @@ "description": "你现在是一名经验丰富的商家运营专家,你擅长管理商家关系,优化商家业务流程,提高商家满意度。你对电商行业有深入的了解,并有优秀的商业洞察力。请在这个角色下为我解答以下问题。" }, { - "id": 6, + "id": "6", "name": "产品运营 - Product Operations", "emoji": "🚀", "group": "职业", @@ -48,7 +48,7 @@ "description": "你现在是一名经验丰富的产品运营专家,你擅长分析市场和用户需求,并对产品生命周期各阶段的运营策略有深刻的理解。你有出色的团队协作能力和沟通技巧,能在不同部门间进行有效的协调。请在这个角色下为我解答以下问题。\n" }, { - "id": 7, + "id": "7", "name": "销售运营 - Sales Operations", "emoji": "💼", "group": "职业", @@ -56,7 +56,7 @@ "description": "你现在是一名销售运营经理,你懂得如何优化销售流程,管理销售数据,提升销售效率。你能制定销售预测和目标,管理销售预算,并提供销售支持。请在这个角色下为我解答以下问题。" }, { - "id": 8, + "id": "8", "name": "用户运营 - User Operations", "emoji": "👨‍💻", "group": "职业", @@ -64,7 +64,7 @@ "description": "你现在是一名用户运营专家,你了解用户行为和需求,能够制定并执行针对性的用户运营策略。你有出色的用户服务能力,能有效处理用户反馈和投诉。请在这个角色下为我解答以下问题。\n" }, { - "id": 9, + "id": "9", "name": "市场营销 - Marketing", "emoji": "📢", "group": "职业", @@ -72,7 +72,7 @@ "description": "你现在是一名专业的市场营销专家,你对营销策略和品牌推广有深入的理解。你熟知如何有效利用不同的渠道和工具来达成营销目标,并对消费者心理有深入的理解。请在这个角色下为我解答以下问题。" }, { - "id": 10, + "id": "10", "name": "商业数据分析 - Business Data Analysis", "emoji": "📈", "group": "职业", @@ -80,7 +80,7 @@ "description": "你现在是一名商业数据分析师,你精通数据分析方法和工具,能够从大量数据中提取出有价值的商业洞察。你对业务运营有深入的理解,并能提供数据驱动的优化建议。请在这个角色下为我解答以下问题。" }, { - "id": 11, + "id": "11", "name": "项目管理 - Project Management", "emoji": "🗂️", "group": "职业", @@ -88,7 +88,7 @@ "description": "你现在是一名资深的项目经理,你精通项目管理的各个方面,包括规划、组织、执行和控制。你擅长处理项目风险,解决问题,并有效地协调团队成员以实现项目目标。请在这个角色下为我解答以下问题。" }, { - "id": 12, + "id": "12", "name": "SEO专家 - SEO Expert", "emoji": "🔎", "group": "职业", @@ -96,7 +96,7 @@ "description": "你现在是一名知识丰富的SEO专家,你了解搜索引擎的工作原理,熟知如何优化网页以提高其在搜索引擎中的排名。你对关键词研究、内容优化、链接建设等SEO策略有深入的了解。请在这个角色下为我解答以下问题。" }, { - "id": 13, + "id": "13", "name": "网站运营数据分析 - Website Operations Data Analysis", "emoji": "💻", "group": "职业", @@ -104,7 +104,7 @@ "description": "你现在是一名网站运营数据分析师,你擅长收集和分析网站数据,以了解用户行为和网站性能。你可以提供关于网站设计、内容和营销策略的数据支持。请在这个角色下为我解答以下问题。\n" }, { - "id": 14, + "id": "14", "name": "数据分析师 - Data Analyst", "emoji": "📊", "group": "职业", @@ -112,7 +112,7 @@ "description": "你现在是一名数据分析师,你精通各种统计分析方法,懂得如何清洗、处理和解析数据以获得有价值的洞察。你擅长利用数据驱动的方式来解决问题和提升决策效率。请在这个角色下为我解答以下问题。" }, { - "id": 15, + "id": "15", "name": "前端工程师 - Frontend Engineer", "emoji": "🖥️", "group": "职业", @@ -120,7 +120,7 @@ "description": "你现在是一名专业的前端工程师,你对HTML、CSS、JavaScript等前端技术有深入的了解,能够制作和优化用户界面。你能够解决浏览器兼容性问题,提升网页性能,并实现优秀的用户体验。请在这个角色下为我解答以下问题。\n" }, { - "id": 16, + "id": "16", "name": "运维工程师 - Operations Engineer", "emoji": "🛠️", "group": "职业", @@ -128,7 +128,7 @@ "description": "你现在是一名运维工程师,你负责保障系统和服务的正常运行。你熟悉各种监控工具,能够高效地处理故障和进行系统优化。你还懂得如何进行数据备份和恢复,以保证数据安全。请在这个角色下为我解答以下问题。" }, { - "id": 17, + "id": "17", "name": "开发工程师 - Software Engineer", "emoji": "💻", "group": "职业", @@ -136,7 +136,7 @@ "description": "你现在是一名资深的软件工程师,你熟悉多种编程语言和开发框架,对软件开发的生命周期有深入的理解。你擅长解决技术问题,并具有优秀的逻辑思维能力。请在这个角色下为我解答以下问题。" }, { - "id": 18, + "id": "18", "name": "测试工程师 - Test Engineer", "emoji": "🧪", "group": "职业", @@ -144,7 +144,7 @@ "description": "你现在是一名专业的测试工程师,你对软件测试方法论和测试工具有深入的了解。你的主要任务是发现和记录软件的缺陷,并确保软件的质量。你在寻找和解决问题上有出色的技能。请在这个角色下为我解答以下问题。" }, { - "id": 19, + "id": "19", "name": "HR人力资源管理 - Human Resources Management", "emoji": "👥", "group": "职业", @@ -152,7 +152,7 @@ "description": "你现在是一名人力资源管理专家,你了解如何招聘、培训、评估和激励员工。你精通劳动法规,擅长处理员工关系,并且在组织发展和变革管理方面有深入的见解。请在这个角色下为我解答以下问题。" }, { - "id": 20, + "id": "20", "name": "行政 - Administration", "emoji": "📋", "group": "职业", @@ -160,7 +160,7 @@ "description": "你现在是一名行政专员,你擅长组织和管理公司的日常运营事务,包括文件管理、会议安排、办公设施管理等。你有良好的人际沟通和组织能力,能在多任务环境中有效工作。请在这个角色下为我解答以下问题。" }, { - "id": 21, + "id": "21", "name": "财务顾问 - Financial Advisor", "emoji": "💰", "group": "职业", @@ -168,7 +168,7 @@ "description": "你现在是一名财务顾问,你对金融市场、投资策略和财务规划有深厚的理解。你能提供财务咨询服务,帮助客户实现其财务目标。你擅长理解和解决复杂的财务问题。请在这个角色下为我解答以下问题。" }, { - "id": 22, + "id": "22", "name": "医生 - Doctor", "emoji": "🩺", "group": "职业", @@ -176,7 +176,7 @@ "description": "你现在是一名医生,具备丰富的医学知识和临床经验。你擅长诊断和治疗各种疾病,能为病人提供专业的医疗建议。你有良好的沟通技巧,能与病人和他们的家人建立信任关系。请在这个角色下为我解答以下问题。" }, { - "id": 23, + "id": "23", "name": "编辑 - Editor", "emoji": "✒️", "group": "职业", @@ -184,7 +184,7 @@ "description": "你现在是一名编辑,你对文字有敏锐的感觉,擅长审校和修订稿件以确保其质量。你有出色的语言和沟通技巧,能与作者有效地合作以改善他们的作品。你对出版流程有深入的了解。请在这个角色下为我解答以下问题。\n" }, { - "id": 24, + "id": "24", "name": "哲学家 - Philosopher", "emoji": "🧠", "group": "职业", @@ -192,7 +192,7 @@ "description": "你现在是一名哲学家,你对世界的本质和人类存在的意义有深入的思考。你熟悉多种哲学流派,并能从哲学的角度分析和解决问题。你具有深刻的思维和出色的逻辑分析能力。请在这个角色下为我解答以下问题。\n" }, { - "id": 25, + "id": "25", "name": "采购 - Procurement", "emoji": "🛒", "group": "职业", @@ -200,7 +200,7 @@ "description": "你现在是一名采购经理,你熟悉供应链管理,擅长进行供应商评估和价格谈判。你负责制定和执行采购策略,以保证货物的质量和供应的稳定。请在这个角色下为我解答以下问题。\n" }, { - "id": 26, + "id": "26", "name": "法务 - Legal Affairs", "emoji": "⚖️", "group": "职业", @@ -208,7 +208,7 @@ "description": "你现在是一名法务专家,你了解公司法、合同法等相关法律,能为企业提供法律咨询和风险评估。你还擅长处理法律争端,并能起草和审核合同。请在这个角色下为我解答以下问题。" }, { - "id": 27, + "id": "27", "name": "翻译成中文 - Chinese", "emoji": "🇨🇳", "group": "语言", @@ -216,7 +216,7 @@ "description": "" }, { - "id": 28, + "id": "28", "name": "翻译成英文 - English", "emoji": "🌐", "group": "语言", @@ -224,7 +224,7 @@ "description": "" }, { - "id": 29, + "id": "29", "name": "英语单词背诵助手", "emoji": "📕", "group": "语言", @@ -232,7 +232,7 @@ "description": "" }, { - "id": 30, + "id": "30", "name": "文章总结 - Summarize", "emoji": "📖", "group": "阅读", diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index 28b306f71a..75d4d0039c 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -26,7 +26,8 @@ const resources = { you: 'You', save: 'Save', footnotes: 'References', - select: 'Select' + select: 'Select', + search: 'Search' }, button: { add: 'Add', @@ -80,7 +81,8 @@ const resources = { 'settings.reset': 'Reset', 'settings.set_as_default': 'Apply to default assistant', 'settings.max': 'Max', - 'suggestions.title': 'Suggested Questions' + 'suggestions.title': 'Suggested Questions', + 'add.assistant.title': 'Add Assistant' }, agents: { title: 'Agents', @@ -93,7 +95,10 @@ const resources = { 'add.prompt.placeholder': 'Enter prompt', 'add.button': 'Add', 'manage.title': 'Manage Agents', - 'delete.popup.content': 'Are you sure you want to delete this agent?' + 'delete.popup.content': 'Are you sure you want to delete this agent?', + 'tag.default': 'Default', + 'tag.system': 'System', + 'tag.user': 'Mine' }, provider: { openai: 'OpenAI', @@ -228,7 +233,8 @@ const resources = { provider: '提供商', you: '用户', footnote: '引用内容', - select: '选择' + select: '选择', + search: '搜索' }, button: { add: '添加', @@ -283,7 +289,8 @@ const resources = { 'settings.reset': '重置', 'settings.set_as_default': '应用到默认助手', 'settings.max': '不限', - 'suggestions.title': '建议的问题' + 'suggestions.title': '建议的问题', + 'add.assistant.title': '添加智能体' }, agents: { title: '智能体', @@ -296,7 +303,10 @@ const resources = { 'add.prompt.placeholder': '输入提示词', 'add.button': '添加', 'manage.title': '管理智能体', - 'delete.popup.content': '确定要删除此智能体吗?' + 'delete.popup.content': '确定要删除此智能体吗?', + 'tag.default': '默认', + 'tag.system': '系统', + 'tag.user': '我的' }, provider: { openai: 'OpenAI', diff --git a/src/renderer/src/pages/agents/AgentsPage.tsx b/src/renderer/src/pages/agents/AgentsPage.tsx index 67333914a7..bf3893117e 100644 --- a/src/renderer/src/pages/agents/AgentsPage.tsx +++ b/src/renderer/src/pages/agents/AgentsPage.tsx @@ -4,7 +4,7 @@ import { HStack } from '@renderer/components/Layout' import Agents from '@renderer/config/agents.json' import { useAgents } from '@renderer/hooks/useAgents' import { useAssistants } from '@renderer/hooks/useAssistant' -import { getDefaultAssistant } from '@renderer/services/assistant' +import { covertAgentToAssistant } from '@renderer/services/assistant' import { Agent } from '@renderer/types' import { Col, Row, Typography } from 'antd' import { find, groupBy } from 'lodash' @@ -21,10 +21,7 @@ const { Title } = Typography const AppsPage: FC = () => { const { assistants, addAssistant } = useAssistants() const { agents } = useAgents() - const agentGroups = groupBy( - Agents.map((a) => ({ ...a, id: String(a.id) })), - 'group' - ) + const agentGroups = groupBy(Agents, 'group') const { t } = useTranslation() const onAddAgentConfirm = (agent: Agent) => { @@ -43,12 +40,7 @@ const AppsPage: FC = () => { } const onAddAgent = (agent: Agent) => { - addAssistant({ - ...getDefaultAssistant(), - ...agent, - name: agent.emoji ? agent.emoji + ' ' + agent.name : agent.name, - id: String(agent.id) - }) + addAssistant(covertAgentToAssistant(agent)) window.message.success({ content: t('message.assistant.added.content'), key: 'agent-added', diff --git a/src/renderer/src/pages/agents/components/AddAgentPopup.tsx b/src/renderer/src/pages/agents/components/AddAgentPopup.tsx index 54b1d64a14..a87cb94f65 100644 --- a/src/renderer/src/pages/agents/components/AddAgentPopup.tsx +++ b/src/renderer/src/pages/agents/components/AddAgentPopup.tsx @@ -3,6 +3,7 @@ import 'emoji-picker-element' import EmojiPicker from '@renderer/components/EmojiPicker' import { TopView } from '@renderer/components/TopView' import { useAgents } from '@renderer/hooks/useAgents' +import { syncAgentToAssistant } from '@renderer/services/assistant' import { Agent } from '@renderer/types' import { getLeadingEmoji, uuid } from '@renderer/utils' import { Button, Form, FormInstance, Input, Modal, Popover } from 'antd' @@ -44,6 +45,7 @@ const PopupContainer: React.FC = ({ agent, resolve }) => { prompt: values.prompt } updateAgent(_agent) + syncAgentToAssistant(_agent) resolve(_agent) setOpen(false) return diff --git a/src/renderer/src/pages/agents/components/ManageAgentsPopup.tsx b/src/renderer/src/pages/agents/components/ManageAgentsPopup.tsx index 87e28838c9..adaab65624 100644 --- a/src/renderer/src/pages/agents/components/ManageAgentsPopup.tsx +++ b/src/renderer/src/pages/agents/components/ManageAgentsPopup.tsx @@ -71,7 +71,7 @@ const PopupContainer: React.FC = () => { } const Container = styled.div` - padding: 16px; + padding: 12px 0; height: 50vh; overflow-y: auto; &::-webkit-scrollbar { diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index e174279076..4888f170e3 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -9,6 +9,7 @@ import { Switch } from 'antd' import { FC, useState } from 'react' import styled from 'styled-components' +import AddAssistantPopup from './components/AddAssistantPopup' import Assistants from './components/Assistants' import Chat from './components/Chat' import Navigation from './components/NavigationCenter' @@ -25,12 +26,17 @@ const HomePage: FC = () => { _activeAssistant = activeAssistant - const onCreateAssistant = () => { + const onCreateDefaultAssistant = () => { const assistant = { ...defaultAssistant, id: uuid() } addAssistant(assistant) setActiveAssistant(assistant) } + const onCreateAssistant = async () => { + const assistant = await AddAssistantPopup.show() + assistant && setActiveAssistant(assistant) + } + return ( @@ -62,7 +68,7 @@ const HomePage: FC = () => { )} diff --git a/src/renderer/src/pages/home/components/AddAssistantPopup.tsx b/src/renderer/src/pages/home/components/AddAssistantPopup.tsx new file mode 100644 index 0000000000..3ec6ebaf92 --- /dev/null +++ b/src/renderer/src/pages/home/components/AddAssistantPopup.tsx @@ -0,0 +1,127 @@ +import { TopView } from '@renderer/components/TopView' +import systemAgents from '@renderer/config/agents.json' +import { useAgents } from '@renderer/hooks/useAgents' +import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant' +import { covertAgentToAssistant } from '@renderer/services/assistant' +import { Agent, Assistant } from '@renderer/types' +import { Input, Modal, Tag } from 'antd' +import { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +interface Props { + resolve: (value: Assistant | undefined) => void +} + +const PopupContainer: React.FC = ({ resolve }) => { + const [open, setOpen] = useState(true) + const { t } = useTranslation() + const { agents: userAgents } = useAgents() + const [searchText, setSearchText] = useState('') + const { defaultAssistant } = useDefaultAssistant() + const { assistants, addAssistant } = useAssistants() + + const defaultAgent: Agent = useMemo( + () => ({ + id: defaultAssistant.id, + name: defaultAssistant.name, + emoji: '', + prompt: defaultAssistant.prompt, + group: 'system' + }), + [defaultAssistant.id, defaultAssistant.name, defaultAssistant.prompt] + ) + + const agents = useMemo(() => { + const allAgents = [defaultAgent, ...userAgents, ...systemAgents] as Agent[] + const list = allAgents.filter((agent) => !assistants.map((a) => a.id).includes(agent.id)) + return searchText ? list.filter((agent) => agent.name.includes(searchText)) : list + }, [assistants, defaultAgent, searchText, userAgents]) + + const onCreateAssistant = (agent: Agent) => { + if (assistants.map((a) => a.id).includes(String(agent.id))) return + const assistant = covertAgentToAssistant(agent) + addAssistant(assistant) + resolve(assistant) + setOpen(false) + } + + const onCancel = () => { + setOpen(false) + } + + const onClose = async () => { + resolve(undefined) + AddAssistantPopup.hide() + } + + return ( + + setSearchText(e.target.value)} + allowClear + autoFocus + style={{ marginBottom: 16 }} + /> + + {agents.map((agent) => ( + onCreateAssistant(agent)}> + {agent.emoji} {agent.name} + {agent.group === 'system' && {t('agents.tag.system')}} + {agent.group === 'user' && {t('agents.tag.user')}} + + ))} + + + ) +} + +const Container = styled.div` + height: 50vh; + overflow-y: auto; + &::-webkit-scrollbar { + display: none; + } +` + +const AgentItem = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 8px; + border-radius: 8px; + user-select: none; + background-color: var(--color-background-soft); + margin-bottom: 8px; + cursor: pointer; + .anticon { + font-size: 16px; + color: var(--color-icon); + } + &:hover { + background-color: var(--color-background-mute); + } +` + +export default class AddAssistantPopup { + static topviewId = 0 + static hide() { + TopView.hide('AddAssistantPopup') + } + static show() { + return new Promise((resolve) => { + TopView.show(, 'AddAssistantPopup') + }) + } +} diff --git a/src/renderer/src/pages/home/components/Assistants.tsx b/src/renderer/src/pages/home/components/Assistants.tsx index 3798e96718..2e871c5805 100644 --- a/src/renderer/src/pages/home/components/Assistants.tsx +++ b/src/renderer/src/pages/home/components/Assistants.tsx @@ -2,7 +2,7 @@ import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons' import { DragDropContext, Draggable, Droppable, DropResult } from '@hello-pangea/dnd' import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant' -import { getDefaultTopic } from '@renderer/services/assistant' +import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { useAppSelector } from '@renderer/store' import { Assistant } from '@renderer/types' @@ -45,6 +45,7 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs async onClick() { const _assistant = await AssistantSettingPopup.show({ assistant }) updateAssistant(_assistant) + syncAsistantToAgent(_assistant) } }, { diff --git a/src/renderer/src/services/assistant.ts b/src/renderer/src/services/assistant.ts index afb814e389..2ed7d46427 100644 --- a/src/renderer/src/services/assistant.ts +++ b/src/renderer/src/services/assistant.ts @@ -1,7 +1,9 @@ import i18n from '@renderer/i18n' import store from '@renderer/store' -import { Assistant, Model, Provider, Topic } from '@renderer/types' -import { uuid } from '@renderer/utils' +import { updateAgent } from '@renderer/store/agents' +import { updateAssistant } from '@renderer/store/assistants' +import { Agent, Assistant, Model, Provider, Topic } from '@renderer/types' +import { getLeadingEmoji, removeLeadingEmoji, uuid } from '@renderer/utils' export function getDefaultAssistant(): Assistant { return { @@ -53,3 +55,47 @@ export function getProviderByModelId(modelId?: string) { const _modelId = modelId || getDefaultModel().id return providers.find((p) => p.models.find((m) => m.id === _modelId)) as Provider } + +export function covertAgentToAssistant(agent: Agent): Assistant { + return { + ...getDefaultAssistant(), + ...agent, + name: getAssistantNameWithAgent(agent), + id: agent.group === 'system' ? uuid() : String(agent.id) + } +} + +export function getAssistantNameWithAgent(agent: Agent) { + return agent.emoji ? agent.emoji + ' ' + agent.name : agent.name +} + +export function syncAsistantToAgent(assistant: Assistant) { + const agents = store.getState().agents.agents + const agent = agents.find((a) => a.id === assistant.id) + + if (agent) { + store.dispatch( + updateAgent({ + ...agent, + emoji: getLeadingEmoji(assistant.name), + name: removeLeadingEmoji(assistant.name), + prompt: assistant.prompt + }) + ) + } +} + +export function syncAgentToAssistant(agent: Agent) { + const assistants = store.getState().assistants.assistants + const assistant = assistants.find((a) => a.id === agent.id) + + if (assistant) { + store.dispatch( + updateAssistant({ + ...assistant, + name: getAssistantNameWithAgent(agent), + prompt: agent.prompt + }) + ) + } +} diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts index 986654b344..a9d0712dac 100644 --- a/src/renderer/src/utils/index.ts +++ b/src/renderer/src/utils/index.ts @@ -98,7 +98,7 @@ export function firstLetter(str: string): string { export function removeLeadingEmoji(str: string): string { const emojiRegex = /^(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)+/u - return str.replace(emojiRegex, '') + return str.replace(emojiRegex, '').trim() } export function getLeadingEmoji(str: string): string {