fix:清空话题总是修复当前话题 (#2167)
This commit is contained in:
parent
cacd0a1387
commit
fc59144b1d
197
src/renderer/src/pages/home/Tabs/AssistantItemComponent.tsx
Normal file
197
src/renderer/src/pages/home/Tabs/AssistantItemComponent.tsx
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import CopyIcon from "@renderer/components/Icons/CopyIcon"
|
||||||
|
import { useAssistant } from "@renderer/hooks/useAssistant"
|
||||||
|
import { modelGenerating } from "@renderer/hooks/useRuntime"
|
||||||
|
import { useSettings } from "@renderer/hooks/useSettings"
|
||||||
|
import AssistantSettingsPopup from "@renderer/pages/settings/AssistantSettings"
|
||||||
|
import { getDefaultTopic } from "@renderer/services/AssistantService"
|
||||||
|
|
||||||
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
|
import { Assistant } from "@renderer/types"
|
||||||
|
import { uuid } from "@renderer/utils"
|
||||||
|
import { Dropdown } from "antd"
|
||||||
|
import { ItemType } from "antd/es/menu/interface"
|
||||||
|
import { omit } from "lodash"
|
||||||
|
import { FC, useCallback } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import { DeleteOutlined, EditOutlined, MinusCircleOutlined, SaveOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
import styled from "styled-components"
|
||||||
|
|
||||||
|
interface AssistantItemProps {
|
||||||
|
assistant: Assistant
|
||||||
|
isActive: boolean
|
||||||
|
onSwitch: (assistant: Assistant) => void
|
||||||
|
onDelete: (assistant: Assistant) => void
|
||||||
|
onCreateDefaultAssistant: () => void
|
||||||
|
addAgent: (agent: any) => void
|
||||||
|
addAssistant: (assistant: Assistant) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssistantItemComponent: FC<AssistantItemProps> = ({
|
||||||
|
assistant,
|
||||||
|
isActive,
|
||||||
|
onSwitch,
|
||||||
|
onDelete,
|
||||||
|
addAgent,
|
||||||
|
addAssistant
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { removeAllTopics } = useAssistant(assistant.id) // 使用当前助手的ID
|
||||||
|
const { clickAssistantToShowTopic, topicPosition } = useSettings()
|
||||||
|
|
||||||
|
const getMenuItems = useCallback(
|
||||||
|
(assistant: Assistant): ItemType[] => [
|
||||||
|
{
|
||||||
|
label: t('assistants.edit.title'),
|
||||||
|
key: 'edit',
|
||||||
|
icon: <EditOutlined />,
|
||||||
|
onClick: () => AssistantSettingsPopup.show({ assistant })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('assistants.copy.title'),
|
||||||
|
key: 'duplicate',
|
||||||
|
icon: <CopyIcon />,
|
||||||
|
onClick: async () => {
|
||||||
|
const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic(assistant.id)] }
|
||||||
|
addAssistant(_assistant)
|
||||||
|
onSwitch(_assistant)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('assistants.clear.title'),
|
||||||
|
key: 'clear',
|
||||||
|
icon: <MinusCircleOutlined />,
|
||||||
|
onClick: () => {
|
||||||
|
window.modal.confirm({
|
||||||
|
title: t('assistants.clear.title'),
|
||||||
|
content: t('assistants.clear.content'),
|
||||||
|
centered: true,
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
onOk: () => removeAllTopics() // 使用当前助手的removeAllTopics
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('assistants.save.title'),
|
||||||
|
key: 'save-to-agent',
|
||||||
|
icon: <SaveOutlined />,
|
||||||
|
onClick: async () => {
|
||||||
|
const agent = omit(assistant, ['model', 'emoji'])
|
||||||
|
agent.id = uuid()
|
||||||
|
agent.type = 'agent'
|
||||||
|
addAgent(agent)
|
||||||
|
window.message.success({
|
||||||
|
content: t('assistants.save.success'),
|
||||||
|
key: 'save-to-agent'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
label: t('common.delete'),
|
||||||
|
key: 'delete',
|
||||||
|
icon: <DeleteOutlined />,
|
||||||
|
danger: true,
|
||||||
|
onClick: () => {
|
||||||
|
window.modal.confirm({
|
||||||
|
title: t('assistants.delete.title'),
|
||||||
|
content: t('assistants.delete.content'),
|
||||||
|
centered: true,
|
||||||
|
okButtonProps: { danger: true },
|
||||||
|
onOk: () => onDelete(assistant)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[addAgent, addAssistant, onSwitch, removeAllTopics, t, onDelete]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleSwitch = useCallback(async () => {
|
||||||
|
await modelGenerating()
|
||||||
|
|
||||||
|
if (topicPosition === 'left' && clickAssistantToShowTopic) {
|
||||||
|
EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSwitch(assistant)
|
||||||
|
}, [clickAssistantToShowTopic, onSwitch, assistant, topicPosition])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}>
|
||||||
|
<AssistantItem onClick={handleSwitch} className={isActive ? 'active' : ''}>
|
||||||
|
<AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName>
|
||||||
|
{isActive && (
|
||||||
|
<MenuButton onClick={() => EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}>
|
||||||
|
<TopicCount className="topics-count">{assistant.topics.length}</TopicCount>
|
||||||
|
</MenuButton>
|
||||||
|
)}
|
||||||
|
</AssistantItem>
|
||||||
|
</Dropdown>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default AssistantItemComponent // 使用默认导出
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const AssistantItem = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 7px 12px;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 10px;
|
||||||
|
padding-right: 35px;
|
||||||
|
font-family: Ubuntu;
|
||||||
|
border-radius: var(--list-item-border-radius);
|
||||||
|
border: 0.5px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
.iconfont {
|
||||||
|
opacity: 0;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
.name {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const AssistantName = styled.div`
|
||||||
|
color: var(--color-text);
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 13px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const MenuButton = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
min-width: 22px;
|
||||||
|
min-height: 22px;
|
||||||
|
border-radius: 11px;
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--color-background);
|
||||||
|
right: 9px;
|
||||||
|
top: 6px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const TopicCount = styled.div`
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`
|
||||||
@ -1,170 +1,23 @@
|
|||||||
import { DeleteOutlined, EditOutlined, MinusCircleOutlined, PlusOutlined, SaveOutlined } from '@ant-design/icons'
|
import { useCallback, useState, FC } from "react"
|
||||||
import DragableList from '@renderer/components/DragableList'
|
import { useTranslation } from "react-i18next"
|
||||||
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
import { PlusOutlined } from '@ant-design/icons'
|
||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import DragableList from "@renderer/components/DragableList"
|
||||||
import { useAgents } from '@renderer/hooks/useAgents'
|
import Scrollbar from "@renderer/components/Scrollbar"
|
||||||
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
|
import { useAgents } from "@renderer/hooks/useAgents"
|
||||||
import { modelGenerating } from '@renderer/hooks/useRuntime'
|
import { useAssistants } from "@renderer/hooks/useAssistant"
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { Assistant } from "@renderer/types"
|
||||||
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
|
import styled from "styled-components"
|
||||||
import { getDefaultTopic } from '@renderer/services/AssistantService'
|
import AssistantItemComponent from "@renderer/pages/home/Tabs/AssistantItemComponent"
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
|
||||||
import { Assistant } from '@renderer/types'
|
|
||||||
import { uuid } from '@renderer/utils'
|
|
||||||
import { Dropdown } from 'antd'
|
|
||||||
import { ItemType } from 'antd/es/menu/interface'
|
|
||||||
import { last, omit } from 'lodash'
|
|
||||||
import { FC, useCallback, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
|
|
||||||
interface Props {
|
// 类型定义
|
||||||
|
interface AssistantsProps {
|
||||||
activeAssistant: Assistant
|
activeAssistant: Assistant
|
||||||
setActiveAssistant: (assistant: Assistant) => void
|
setActiveAssistant: (assistant: Assistant) => void
|
||||||
onCreateDefaultAssistant: () => void
|
|
||||||
onCreateAssistant: () => void
|
onCreateAssistant: () => void
|
||||||
|
onCreateDefaultAssistant: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const Assistants: FC<Props> = ({
|
// 样式组件(只定义一次)
|
||||||
activeAssistant,
|
|
||||||
setActiveAssistant,
|
|
||||||
onCreateAssistant,
|
|
||||||
onCreateDefaultAssistant
|
|
||||||
}) => {
|
|
||||||
const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants()
|
|
||||||
const [dragging, setDragging] = useState(false)
|
|
||||||
const { removeAllTopics } = useAssistant(activeAssistant.id)
|
|
||||||
const { clickAssistantToShowTopic, topicPosition } = useSettings()
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { addAgent } = useAgents()
|
|
||||||
|
|
||||||
const onDelete = useCallback(
|
|
||||||
(assistant: Assistant) => {
|
|
||||||
const _assistant: Assistant | undefined = last(assistants.filter((a) => a.id !== assistant.id))
|
|
||||||
_assistant ? setActiveAssistant(_assistant) : onCreateDefaultAssistant()
|
|
||||||
removeAssistant(assistant.id)
|
|
||||||
},
|
|
||||||
[assistants, onCreateDefaultAssistant, removeAssistant, setActiveAssistant]
|
|
||||||
)
|
|
||||||
|
|
||||||
const getMenuItems = useCallback(
|
|
||||||
(assistant: Assistant) =>
|
|
||||||
[
|
|
||||||
{
|
|
||||||
label: t('assistants.edit.title'),
|
|
||||||
key: 'edit',
|
|
||||||
icon: <EditOutlined />,
|
|
||||||
onClick: () => AssistantSettingsPopup.show({ assistant })
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('assistants.copy.title'),
|
|
||||||
key: 'duplicate',
|
|
||||||
icon: <CopyIcon />,
|
|
||||||
onClick: async () => {
|
|
||||||
const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic(assistant.id)] }
|
|
||||||
addAssistant(_assistant)
|
|
||||||
setActiveAssistant(_assistant)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('assistants.clear.title'),
|
|
||||||
key: 'clear',
|
|
||||||
icon: <MinusCircleOutlined />,
|
|
||||||
onClick: () => {
|
|
||||||
window.modal.confirm({
|
|
||||||
title: t('assistants.clear.title'),
|
|
||||||
content: t('assistants.clear.content'),
|
|
||||||
centered: true,
|
|
||||||
okButtonProps: { danger: true },
|
|
||||||
onOk: removeAllTopics
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('assistants.save.title'),
|
|
||||||
key: 'save-to-agent',
|
|
||||||
icon: <SaveOutlined />,
|
|
||||||
onClick: async () => {
|
|
||||||
const agent = omit(assistant, ['model', 'emoji'])
|
|
||||||
agent.id = uuid()
|
|
||||||
agent.type = 'agent'
|
|
||||||
addAgent(agent)
|
|
||||||
window.message.success({
|
|
||||||
content: t('assistants.save.success'),
|
|
||||||
key: 'save-to-agent'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
label: t('common.delete'),
|
|
||||||
key: 'delete',
|
|
||||||
icon: <DeleteOutlined />,
|
|
||||||
danger: true,
|
|
||||||
onClick: () => {
|
|
||||||
window.modal.confirm({
|
|
||||||
title: t('assistants.delete.title'),
|
|
||||||
content: t('assistants.delete.content'),
|
|
||||||
centered: true,
|
|
||||||
okButtonProps: { danger: true },
|
|
||||||
onOk: () => onDelete(assistant)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
] as ItemType[],
|
|
||||||
[addAgent, addAssistant, onDelete, removeAllTopics, setActiveAssistant, t]
|
|
||||||
)
|
|
||||||
|
|
||||||
const onSwitchAssistant = useCallback(
|
|
||||||
async (assistant: Assistant) => {
|
|
||||||
await modelGenerating()
|
|
||||||
|
|
||||||
if (topicPosition === 'left' && clickAssistantToShowTopic) {
|
|
||||||
EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)
|
|
||||||
}
|
|
||||||
|
|
||||||
setActiveAssistant(assistant)
|
|
||||||
},
|
|
||||||
[clickAssistantToShowTopic, setActiveAssistant, topicPosition]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container className="assistants-tab">
|
|
||||||
<DragableList
|
|
||||||
list={assistants}
|
|
||||||
onUpdate={updateAssistants}
|
|
||||||
style={{ paddingBottom: dragging ? '34px' : 0 }}
|
|
||||||
onDragStart={() => setDragging(true)}
|
|
||||||
onDragEnd={() => setDragging(false)}>
|
|
||||||
{(assistant) => {
|
|
||||||
const isCurrent = assistant.id === activeAssistant?.id
|
|
||||||
return (
|
|
||||||
<Dropdown key={assistant.id} menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}>
|
|
||||||
<AssistantItem onClick={() => onSwitchAssistant(assistant)} className={isCurrent ? 'active' : ''}>
|
|
||||||
<AssistantName className="name">{assistant.name || t('chat.default.name')}</AssistantName>
|
|
||||||
{isCurrent && (
|
|
||||||
<MenuButton onClick={() => EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}>
|
|
||||||
<TopicCount className="topics-count">{assistant.topics.length}</TopicCount>
|
|
||||||
</MenuButton>
|
|
||||||
)}
|
|
||||||
</AssistantItem>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</DragableList>
|
|
||||||
{!dragging && (
|
|
||||||
<AssistantItem onClick={onCreateAssistant}>
|
|
||||||
<AssistantName>
|
|
||||||
<PlusOutlined style={{ color: 'var(--color-text-2)', marginRight: 4 }} />
|
|
||||||
{t('chat.add.assistant.title')}
|
|
||||||
</AssistantName>
|
|
||||||
</AssistantItem>
|
|
||||||
)}
|
|
||||||
<div style={{ minHeight: 10 }}></div>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Container = styled(Scrollbar)`
|
const Container = styled(Scrollbar)`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -184,18 +37,14 @@ const AssistantItem = styled.div`
|
|||||||
border-radius: var(--list-item-border-radius);
|
border-radius: var(--list-item-border-radius);
|
||||||
border: 0.5px solid transparent;
|
border: 0.5px solid transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.iconfont {
|
|
||||||
opacity: 0;
|
|
||||||
color: var(--color-text-3);
|
|
||||||
}
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
border: 0.5px solid var(--color-border);
|
border: 0.5px solid var(--color-border);
|
||||||
.name {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -208,30 +57,60 @@ const AssistantName = styled.div`
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const MenuButton = styled.div`
|
const Assistants: FC<AssistantsProps> = ({
|
||||||
display: flex;
|
activeAssistant,
|
||||||
flex-direction: row;
|
setActiveAssistant,
|
||||||
justify-content: center;
|
onCreateAssistant,
|
||||||
align-items: center;
|
onCreateDefaultAssistant
|
||||||
min-width: 22px;
|
}) => {
|
||||||
height: 22px;
|
const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants()
|
||||||
min-width: 22px;
|
const [dragging, setDragging] = useState(false)
|
||||||
min-height: 22px;
|
const { addAgent } = useAgents()
|
||||||
border-radius: 11px;
|
const { t } = useTranslation()
|
||||||
position: absolute;
|
|
||||||
background-color: var(--color-background);
|
|
||||||
right: 9px;
|
|
||||||
top: 6px;
|
|
||||||
`
|
|
||||||
|
|
||||||
const TopicCount = styled.div`
|
const onDelete = useCallback(
|
||||||
color: var(--color-text);
|
(assistant: Assistant) => {
|
||||||
font-size: 10px;
|
const remaining = assistants.filter(a => a.id !== assistant.id)
|
||||||
border-radius: 10px;
|
const newActive = remaining[remaining.length - 1]
|
||||||
display: flex;
|
newActive ? setActiveAssistant(newActive) : onCreateDefaultAssistant()
|
||||||
flex-direction: row;
|
removeAssistant(assistant.id)
|
||||||
justify-content: center;
|
},
|
||||||
align-items: center;
|
[assistants, removeAssistant, setActiveAssistant, onCreateDefaultAssistant]
|
||||||
`
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className="assistants-tab">
|
||||||
|
<DragableList
|
||||||
|
list={assistants}
|
||||||
|
onUpdate={updateAssistants}
|
||||||
|
style={{ paddingBottom: dragging ? '34px' : 0 }}
|
||||||
|
onDragStart={() => setDragging(true)}
|
||||||
|
onDragEnd={() => setDragging(false)}
|
||||||
|
>
|
||||||
|
{(assistant) => (
|
||||||
|
<AssistantItemComponent
|
||||||
|
key={assistant.id}
|
||||||
|
assistant={assistant}
|
||||||
|
isActive={assistant.id === activeAssistant.id}
|
||||||
|
onSwitch={setActiveAssistant}
|
||||||
|
onDelete={onDelete}
|
||||||
|
addAgent={addAgent}
|
||||||
|
addAssistant={addAssistant}
|
||||||
|
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DragableList>
|
||||||
|
{!dragging && (
|
||||||
|
<AssistantItem onClick={onCreateAssistant}>
|
||||||
|
<AssistantName>
|
||||||
|
<PlusOutlined style={{ color: 'var(--color-text-2)', marginRight: 4 }} />
|
||||||
|
{t('chat.add.assistant.title')}
|
||||||
|
</AssistantName>
|
||||||
|
</AssistantItem>
|
||||||
|
)}
|
||||||
|
<div style={{ minHeight: 10 }}></div>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default Assistants
|
export default Assistants
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user