mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-08 14:29:15 +08:00
refactor: standardize variable naming and improve tag calculation logic
- Renamed variables for consistency, changing `AssistantsTabSortType` to `assistantsTabSortType`. - Refactored tag calculation in `useTags` to utilize `uniq` and `flatMap` for better performance and readability. - Updated localization files to remove unnecessary characters in quick trigger messages. - Enhanced the `AssistantItem` component by extracting menu item creation logic and sorting functions for better maintainability.
This commit is contained in:
parent
8e2c938254
commit
6d76a23d06
@ -24,7 +24,6 @@ body[theme-mode='light'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: var(--color-scrollbar-thumb);
|
background: var(--color-scrollbar-thumb);
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -33,7 +32,6 @@ body[theme-mode='light'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre:not(.shiki)::-webkit-scrollbar-thumb {
|
pre:not(.shiki)::-webkit-scrollbar-thumb {
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
background: rgba(0, 0, 0, 0.08);
|
background: rgba(0, 0, 0, 0.08);
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@ -38,8 +38,9 @@ const PromptPopupContainer: React.FC<Props> = ({
|
|||||||
setOpen(false)
|
setOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClose = () => {
|
const onAfterClose = () => {
|
||||||
resolve(null)
|
resolve(null)
|
||||||
|
TopView.hide(TopViewKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAfterOpenChange = (visible: boolean) => {
|
const handleAfterOpenChange = (visible: boolean) => {
|
||||||
@ -61,7 +62,7 @@ const PromptPopupContainer: React.FC<Props> = ({
|
|||||||
open={open}
|
open={open}
|
||||||
onOk={onOk}
|
onOk={onOk}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
afterClose={onClose}
|
afterClose={onAfterClose}
|
||||||
afterOpenChange={handleAfterOpenChange}
|
afterOpenChange={handleAfterOpenChange}
|
||||||
transitionName="animation-move-down"
|
transitionName="animation-move-down"
|
||||||
centered>
|
centered>
|
||||||
@ -95,16 +96,7 @@ export default class PromptPopup {
|
|||||||
}
|
}
|
||||||
static show(props: PromptPopupShowParams) {
|
static show(props: PromptPopupShowParams) {
|
||||||
return new Promise<string>((resolve) => {
|
return new Promise<string>((resolve) => {
|
||||||
TopView.show(
|
TopView.show(<PromptPopupContainer {...props} resolve={resolve} />, 'PromptPopup')
|
||||||
<PromptPopupContainer
|
|
||||||
{...props}
|
|
||||||
resolve={(v) => {
|
|
||||||
resolve(v)
|
|
||||||
TopView.hide(TopViewKey)
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
'PromptPopup'
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,70 +0,0 @@
|
|||||||
import AssistantTagsSettings from '@renderer/pages/settings/AssistantSettings/AssistantTagsSettings'
|
|
||||||
import { Assistant } from '@renderer/types'
|
|
||||||
import { Modal } from 'antd'
|
|
||||||
import { useState } from 'react'
|
|
||||||
|
|
||||||
import { TopView } from '../TopView'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
assistant: Assistant
|
|
||||||
updateAssistant: (assistant: Assistant) => void
|
|
||||||
resolve: (data: any) => void
|
|
||||||
mode?: 'add' | 'manage'
|
|
||||||
}
|
|
||||||
|
|
||||||
const PopupContainer: React.FC<Props> = ({ assistant, updateAssistant, resolve, mode }) => {
|
|
||||||
const [open, setOpen] = useState(true)
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClose = () => {
|
|
||||||
resolve({})
|
|
||||||
}
|
|
||||||
|
|
||||||
TagsPopup.hide = onCancel
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
open={open}
|
|
||||||
onCancel={onCancel}
|
|
||||||
afterClose={onClose}
|
|
||||||
title="标签管理"
|
|
||||||
width="600px"
|
|
||||||
transitionName="animation-move-down"
|
|
||||||
styles={{
|
|
||||||
content: {
|
|
||||||
padding: '16px',
|
|
||||||
border: `1px solid var(--color-frame-border)`
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
centered
|
|
||||||
footer={null}>
|
|
||||||
<AssistantTagsSettings assistant={assistant} updateAssistant={updateAssistant} mode={mode} />
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class TagsPopup {
|
|
||||||
static topviewId = 0
|
|
||||||
static hide() {
|
|
||||||
TopView.hide('TagsPopup')
|
|
||||||
}
|
|
||||||
static show(assistant: Assistant, updateAssistant: (assistant: Assistant) => void, mode?: 'add' | 'manage') {
|
|
||||||
return new Promise<any>((resolve) => {
|
|
||||||
TopView.show(
|
|
||||||
<PopupContainer
|
|
||||||
assistant={assistant}
|
|
||||||
updateAssistant={updateAssistant}
|
|
||||||
resolve={(v) => {
|
|
||||||
resolve(v)
|
|
||||||
TopView.hide('TagsPopup')
|
|
||||||
}}
|
|
||||||
mode={mode}
|
|
||||||
/>,
|
|
||||||
'TagsPopup'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -18,6 +18,7 @@ interface ShowParams {
|
|||||||
text: string
|
text: string
|
||||||
textareaProps?: TextAreaProps
|
textareaProps?: TextAreaProps
|
||||||
modalProps?: ModalProps
|
modalProps?: ModalProps
|
||||||
|
showTranslate?: boolean
|
||||||
children?: (props: { onOk?: () => void; onCancel?: () => void }) => React.ReactNode
|
children?: (props: { onOk?: () => void; onCancel?: () => void }) => React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +26,14 @@ interface Props extends ShowParams {
|
|||||||
resolve: (data: any) => void
|
resolve: (data: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, resolve, children }) => {
|
const PopupContainer: React.FC<Props> = ({
|
||||||
|
text,
|
||||||
|
textareaProps,
|
||||||
|
modalProps,
|
||||||
|
resolve,
|
||||||
|
children,
|
||||||
|
showTranslate = true
|
||||||
|
}) => {
|
||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [textValue, setTextValue] = useState(text)
|
const [textValue, setTextValue] = useState(text)
|
||||||
@ -148,12 +156,14 @@ const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, reso
|
|||||||
onInput={resizeTextArea}
|
onInput={resizeTextArea}
|
||||||
onChange={(e) => setTextValue(e.target.value)}
|
onChange={(e) => setTextValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<TranslateButton
|
{showTranslate && (
|
||||||
onClick={handleTranslate}
|
<TranslateButton
|
||||||
aria-label="Translate text"
|
onClick={handleTranslate}
|
||||||
disabled={isTranslating || !textValue.trim()}>
|
aria-label="Translate text"
|
||||||
{isTranslating ? <LoadingOutlined spin /> : <Languages size={16} />}
|
disabled={isTranslating || !textValue.trim()}>
|
||||||
</TranslateButton>
|
{isTranslating ? <LoadingOutlined spin /> : <Languages size={16} />}
|
||||||
|
</TranslateButton>
|
||||||
|
)}
|
||||||
</TextAreaContainer>
|
</TextAreaContainer>
|
||||||
<ChildrenContainer>{children && children({ onOk, onCancel })}</ChildrenContainer>
|
<ChildrenContainer>{children && children({ onOk, onCancel })}</ChildrenContainer>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@ -31,11 +31,11 @@ export function useShowTopics() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useAssistantsTabSortType() {
|
export function useAssistantsTabSortType() {
|
||||||
const AssistantsTabSortType = useAppSelector((state) => state.settings.assistantsTabSortType)
|
const assistantsTabSortType = useAppSelector((state) => state.settings.assistantsTabSortType)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
AssistantsTabSortType,
|
assistantsTabSortType,
|
||||||
setAssistantsTabSortType: (sortType: AssistantsSortType) => dispatch(setAssistantsTabSortType(sortType))
|
setAssistantsTabSortType: (sortType: AssistantsSortType) => dispatch(setAssistantsTabSortType(sortType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Assistant } from '@renderer/types'
|
import { flatMap, groupBy, uniq } from 'lodash'
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { useAssistants } from './useAssistant'
|
import { useAssistants } from './useAssistant'
|
||||||
@ -10,63 +10,44 @@ import { useAssistants } from './useAssistant'
|
|||||||
|
|
||||||
export const useTags = () => {
|
export const useTags = () => {
|
||||||
const { assistants } = useAssistants()
|
const { assistants } = useAssistants()
|
||||||
const [allTags, setAllTags] = useState<string[]>([])
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
// 计算所有标签
|
// 计算所有标签
|
||||||
const calculateTags = useCallback(() => {
|
const allTags = useMemo(() => {
|
||||||
const tags = new Set<string>()
|
return uniq(flatMap(assistants, (assistant) => assistant.tags || []))
|
||||||
assistants.forEach((assistant) => {
|
|
||||||
assistant.tags?.forEach((tag) => tags.add(tag))
|
|
||||||
})
|
|
||||||
return Array.from(tags)
|
|
||||||
}, [assistants])
|
}, [assistants])
|
||||||
|
|
||||||
// 当assistants变化时重新计算标签
|
|
||||||
useEffect(() => {
|
|
||||||
setAllTags(calculateTags())
|
|
||||||
}, [assistants, calculateTags])
|
|
||||||
|
|
||||||
const getAssistantsByTag = useCallback(
|
const getAssistantsByTag = useCallback(
|
||||||
(tag: string) => {
|
(tag: string) => assistants.filter((assistant) => assistant.tags?.includes(tag)),
|
||||||
return assistants.filter((assistant) => assistant.tags?.includes(tag))
|
|
||||||
},
|
|
||||||
[assistants]
|
[assistants]
|
||||||
)
|
)
|
||||||
|
|
||||||
const addTag = useCallback((tag: string) => {
|
|
||||||
setAllTags((prev) => [...prev, tag])
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const getGroupedAssistants = useMemo(() => {
|
const getGroupedAssistants = useMemo(() => {
|
||||||
const grouped: { tag: string; assistants: Assistant[] }[] = []
|
// 按标签分组,处理多标签的情况
|
||||||
|
const assistantsByTags = flatMap(assistants, (assistant) => {
|
||||||
allTags.forEach((tag) => {
|
const tags = assistant.tags?.length ? assistant.tags : [t('assistants.tags.untagged')]
|
||||||
const taggedAssistants = assistants.filter((a) => a.tags?.includes(tag))
|
return tags.map((tag) => ({ tag, assistant }))
|
||||||
if (taggedAssistants.length > 0) {
|
|
||||||
grouped.push({
|
|
||||||
tag,
|
|
||||||
assistants: taggedAssistants.sort((a, b) => a.name.localeCompare(b.name))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grouped.sort((a, b) => a.tag.localeCompare(b.tag))
|
// 按标签分组并构建结果
|
||||||
|
const grouped = Object.entries(groupBy(assistantsByTags, 'tag')).map(([tag, group]) => ({
|
||||||
|
tag,
|
||||||
|
assistants: group.map((g) => g.assistant)
|
||||||
|
}))
|
||||||
|
|
||||||
const untagged = assistants.filter((a) => !a.tags?.length)
|
// 将未标记的组移到最前面
|
||||||
if (untagged.length > 0) {
|
const untaggedIndex = grouped.findIndex((g) => g.tag === t('assistants.tags.untagged'))
|
||||||
grouped.unshift({
|
if (untaggedIndex > -1) {
|
||||||
tag: t('assistants.tags.untagged'),
|
const [untagged] = grouped.splice(untaggedIndex, 1)
|
||||||
assistants: untagged
|
grouped.unshift(untagged)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return grouped
|
return grouped
|
||||||
}, [allTags, assistants, t])
|
}, [assistants, t])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
allTags,
|
allTags,
|
||||||
getAssistantsByTag,
|
getAssistantsByTag,
|
||||||
getGroupedAssistants,
|
getGroupedAssistants
|
||||||
addTag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -114,13 +114,9 @@
|
|||||||
"modify": "Modify Tag",
|
"modify": "Modify Tag",
|
||||||
"add": "Add Tag",
|
"add": "Add Tag",
|
||||||
"delete": "Delete Tag",
|
"delete": "Delete Tag",
|
||||||
|
"deleteConfirm": "Are you sure to delete this tag?",
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Tag Settings",
|
"title": "Tag Settings"
|
||||||
"current": "Current Tags",
|
|
||||||
"searchTagsPlaceholder": "Enter tag name to filter tags",
|
|
||||||
"addTagsPlaceholder": "Enter tag name to add",
|
|
||||||
"tagsLsitTitle": "Tag List",
|
|
||||||
"tagsLsitTitleTips": "Click tag to toggle"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -111,13 +111,9 @@
|
|||||||
"add": "タグ追加",
|
"add": "タグ追加",
|
||||||
"modify": "タグ修正",
|
"modify": "タグ修正",
|
||||||
"delete": "タグ削除",
|
"delete": "タグ削除",
|
||||||
|
"deleteConfirm": "このタグを削除してもよろしいですか?",
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "タグ設定",
|
"title": "タグ設定"
|
||||||
"current": "現在のタグ",
|
|
||||||
"searchTagsPlaceholder": "タグ名を入力してフィルタリング",
|
|
||||||
"addTagsPlaceholder": "追加するタグ名を入力",
|
|
||||||
"tagsLsitTitle": "タグ一覧",
|
|
||||||
"tagsLsitTitleTips": "タグをクリックで切り替え"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings.tool_use_mode": "工具調用方式",
|
"settings.tool_use_mode": "工具調用方式",
|
||||||
@ -1497,7 +1493,7 @@
|
|||||||
"messages.input.send_shortcuts": "送信ショートカット",
|
"messages.input.send_shortcuts": "送信ショートカット",
|
||||||
"messages.input.show_estimated_tokens": "推定トークン数を表示",
|
"messages.input.show_estimated_tokens": "推定トークン数を表示",
|
||||||
"messages.input.title": "入力設定",
|
"messages.input.title": "入力設定",
|
||||||
"messages.input.enable_quick_triggers": "'/' と '@' を有効にしてクイックメニューを表示します。",
|
"messages.input.enable_quick_triggers": "/ と @ を有効にしてクイックメニューを表示します。",
|
||||||
"messages.input.enable_delete_model": "バックスペースキーでモデル/添付ファイルを削除します。",
|
"messages.input.enable_delete_model": "バックスペースキーでモデル/添付ファイルを削除します。",
|
||||||
"messages.markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング",
|
"messages.markdown_rendering_input_message": "Markdownで入力メッセージをレンダリング",
|
||||||
"messages.math_engine": "数式エンジン",
|
"messages.math_engine": "数式エンジン",
|
||||||
|
|||||||
@ -114,14 +114,9 @@
|
|||||||
"add": "Добавить тег",
|
"add": "Добавить тег",
|
||||||
"modify": "Изменить тег",
|
"modify": "Изменить тег",
|
||||||
"delete": "Удалить тег",
|
"delete": "Удалить тег",
|
||||||
|
"deleteConfirm": "Вы уверены, что хотите удалить этот тег?",
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Настройки тегов",
|
"title": "Настройки тегов"
|
||||||
"current": "Текущие теги",
|
|
||||||
"searchTagsPlaceholder": "Введите название тега для фильтрации",
|
|
||||||
"addTagsPlaceholder": "Введите название тега для добавления",
|
|
||||||
"tagsLsitTitle": "Список тегов",
|
|
||||||
"tagsLsitTitleTips": "Нажмите на тег для переключения"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1498,7 +1493,7 @@
|
|||||||
"messages.input.send_shortcuts": "Горячие клавиши для отправки",
|
"messages.input.send_shortcuts": "Горячие клавиши для отправки",
|
||||||
"messages.input.show_estimated_tokens": "Показывать затраты токенов",
|
"messages.input.show_estimated_tokens": "Показывать затраты токенов",
|
||||||
"messages.input.title": "Настройки ввода",
|
"messages.input.title": "Настройки ввода",
|
||||||
"messages.input.enable_quick_triggers": "Включите '/' и '@', чтобы вызвать быстрое меню.",
|
"messages.input.enable_quick_triggers": "Включите / и @, чтобы вызвать быстрое меню.",
|
||||||
"messages.input.enable_delete_model": "Включите удаление модели/вложения с помощью клавиши Backspace",
|
"messages.input.enable_delete_model": "Включите удаление модели/вложения с помощью клавиши Backspace",
|
||||||
"messages.markdown_rendering_input_message": "Отображение ввода в формате Markdown",
|
"messages.markdown_rendering_input_message": "Отображение ввода в формате Markdown",
|
||||||
"messages.math_engine": "Математический движок",
|
"messages.math_engine": "Математический движок",
|
||||||
|
|||||||
@ -114,13 +114,9 @@
|
|||||||
"untagged": "未分组",
|
"untagged": "未分组",
|
||||||
"modify": "修改标签",
|
"modify": "修改标签",
|
||||||
"delete": "删除标签",
|
"delete": "删除标签",
|
||||||
|
"deleteConfirm": "确定要删除这个标签吗?",
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "标签设置",
|
"title": "标签设置"
|
||||||
"current": "当前标签",
|
|
||||||
"searchTagsPlaceholder": "请输入标签名称以过滤标签",
|
|
||||||
"addTagsPlaceholder": "请输入标签名称以添加",
|
|
||||||
"tagsLsitTitle": "标签列表",
|
|
||||||
"tagsLsitTitleTips": "点击标签可切换"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1501,7 +1497,7 @@
|
|||||||
"messages.input.send_shortcuts": "发送快捷键",
|
"messages.input.send_shortcuts": "发送快捷键",
|
||||||
"messages.input.show_estimated_tokens": "显示预估 Token 数",
|
"messages.input.show_estimated_tokens": "显示预估 Token 数",
|
||||||
"messages.input.title": "输入设置",
|
"messages.input.title": "输入设置",
|
||||||
"messages.input.enable_quick_triggers": "启用 '/' 和 '@' 触发快捷菜单",
|
"messages.input.enable_quick_triggers": "启用 / 和 @ 触发快捷菜单",
|
||||||
"messages.input.enable_delete_model": "启用删除键删除输入的模型/附件",
|
"messages.input.enable_delete_model": "启用删除键删除输入的模型/附件",
|
||||||
"messages.markdown_rendering_input_message": "Markdown 渲染输入消息",
|
"messages.markdown_rendering_input_message": "Markdown 渲染输入消息",
|
||||||
"messages.math_engine": "数学公式引擎",
|
"messages.math_engine": "数学公式引擎",
|
||||||
|
|||||||
@ -111,13 +111,9 @@
|
|||||||
"add": "添加標籤",
|
"add": "添加標籤",
|
||||||
"modify": "修改標籤",
|
"modify": "修改標籤",
|
||||||
"delete": "刪除標籤",
|
"delete": "刪除標籤",
|
||||||
|
"deleteConfirm": "確定要刪除這個標籤嗎?",
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "標籤設定",
|
"title": "標籤設定"
|
||||||
"current": "當前標籤",
|
|
||||||
"searchTagsPlaceholder": "請輸入標籤名稱以過濾標籤",
|
|
||||||
"addTagsPlaceholder": "請輸入標籤名稱以新增",
|
|
||||||
"tagsLsitTitle": "標籤列表",
|
|
||||||
"tagsLsitTitleTips": "點擊標籤可切換"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings.tool_use_mode": "工具調用方式",
|
"settings.tool_use_mode": "工具調用方式",
|
||||||
@ -1500,7 +1496,7 @@
|
|||||||
"messages.input.send_shortcuts": "傳送快捷鍵",
|
"messages.input.send_shortcuts": "傳送快捷鍵",
|
||||||
"messages.input.show_estimated_tokens": "顯示預估 Token 數",
|
"messages.input.show_estimated_tokens": "顯示預估 Token 數",
|
||||||
"messages.input.title": "輸入設定",
|
"messages.input.title": "輸入設定",
|
||||||
"messages.input.enable_quick_triggers": "啟用 '/' 和 '@' 觸發快捷選單",
|
"messages.input.enable_quick_triggers": "啟用 / 和 @ 觸發快捷選單",
|
||||||
"messages.input.enable_delete_model": "啟用刪除鍵刪除模型/附件",
|
"messages.input.enable_delete_model": "啟用刪除鍵刪除模型/附件",
|
||||||
"messages.markdown_rendering_input_message": "Markdown 渲染輸入訊息",
|
"messages.markdown_rendering_input_message": "Markdown 渲染輸入訊息",
|
||||||
"messages.math_engine": "數學公式引擎",
|
"messages.math_engine": "數學公式引擎",
|
||||||
|
|||||||
@ -26,7 +26,7 @@ interface Props {
|
|||||||
|
|
||||||
const Chat: FC<Props> = (props) => {
|
const Chat: FC<Props> = (props) => {
|
||||||
const { assistant } = useAssistant(props.assistant.id)
|
const { assistant } = useAssistant(props.assistant.id)
|
||||||
const { topicPosition, messageStyle } = useSettings()
|
const { topicPosition, messageStyle, showAssistants } = useSettings()
|
||||||
const { showTopics } = useShowTopics()
|
const { showTopics } = useShowTopics()
|
||||||
const { isMultiSelectMode } = useChatContext(props.activeTopic)
|
const { isMultiSelectMode } = useChatContext(props.activeTopic)
|
||||||
|
|
||||||
@ -36,9 +36,10 @@ const Chat: FC<Props> = (props) => {
|
|||||||
|
|
||||||
const maxWidth = useMemo(() => {
|
const maxWidth = useMemo(() => {
|
||||||
const showRightTopics = showTopics && topicPosition === 'right'
|
const showRightTopics = showTopics && topicPosition === 'right'
|
||||||
const minusRightTopicsWidth = showRightTopics ? `- var(--assistants-width)` : ''
|
const minusAssistantsWidth = showAssistants ? '- var(--assistants-width)' : ''
|
||||||
return `calc(100vw - var(--sidebar-width) - var(--assistants-width) ${minusRightTopicsWidth})`
|
const minusRightTopicsWidth = showRightTopics ? '- var(--assistants-width)' : ''
|
||||||
}, [showTopics, topicPosition])
|
return `calc(100vw - var(--sidebar-width) ${minusAssistantsWidth} ${minusRightTopicsWidth})`
|
||||||
|
}, [showAssistants, showTopics, topicPosition])
|
||||||
|
|
||||||
useHotkeys('esc', () => {
|
useHotkeys('esc', () => {
|
||||||
contentSearchRef.current?.disable()
|
contentSearchRef.current?.disable()
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const Assistants: FC<AssistantsTabProps> = ({
|
|||||||
const { addAgent } = useAgents()
|
const { addAgent } = useAgents()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { getGroupedAssistants } = useTags()
|
const { getGroupedAssistants } = useTags()
|
||||||
const { AssistantsTabSortType = 'list', setAssistantsTabSortType } = useAssistantsTabSortType()
|
const { assistantsTabSortType = 'list', setAssistantsTabSortType } = useAssistantsTabSortType()
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const onDelete = useCallback(
|
const onDelete = useCallback(
|
||||||
@ -51,9 +51,10 @@ const Assistants: FC<AssistantsTabProps> = ({
|
|||||||
},
|
},
|
||||||
[setAssistantsTabSortType]
|
[setAssistantsTabSortType]
|
||||||
)
|
)
|
||||||
return (
|
|
||||||
<Container className="assistants-tab" ref={containerRef}>
|
if (assistantsTabSortType === 'tags') {
|
||||||
{AssistantsTabSortType === 'tags' && (
|
return (
|
||||||
|
<Container className="assistants-tab" ref={containerRef}>
|
||||||
<div style={{ marginBottom: '8px' }}>
|
<div style={{ marginBottom: '8px' }}>
|
||||||
{getGroupedAssistants.map((group) => (
|
{getGroupedAssistants.map((group) => (
|
||||||
<TagsContainer key={group.tag}>
|
<TagsContainer key={group.tag}>
|
||||||
@ -68,7 +69,7 @@ const Assistants: FC<AssistantsTabProps> = ({
|
|||||||
key={assistant.id}
|
key={assistant.id}
|
||||||
assistant={assistant}
|
assistant={assistant}
|
||||||
isActive={assistant.id === activeAssistant.id}
|
isActive={assistant.id === activeAssistant.id}
|
||||||
sortBy={AssistantsTabSortType}
|
sortBy={assistantsTabSortType}
|
||||||
onSwitch={setActiveAssistant}
|
onSwitch={setActiveAssistant}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
addAgent={addAgent}
|
addAgent={addAgent}
|
||||||
@ -80,30 +81,39 @@ const Assistants: FC<AssistantsTabProps> = ({
|
|||||||
</TagsContainer>
|
</TagsContainer>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
<AssistantAddItem onClick={onCreateAssistant}>
|
||||||
{AssistantsTabSortType === 'list' && (
|
<AssistantName>
|
||||||
<DragableList
|
<PlusOutlined style={{ color: 'var(--color-text-2)', marginRight: 4 }} />
|
||||||
list={assistants}
|
{t('chat.add.assistant.title')}
|
||||||
onUpdate={updateAssistants}
|
</AssistantName>
|
||||||
style={{ paddingBottom: dragging ? '34px' : 0 }}
|
</AssistantAddItem>
|
||||||
onDragStart={() => setDragging(true)}
|
</Container>
|
||||||
onDragEnd={() => setDragging(false)}>
|
)
|
||||||
{(assistant) => (
|
}
|
||||||
<AssistantItem
|
|
||||||
key={assistant.id}
|
return (
|
||||||
assistant={assistant}
|
<Container className="assistants-tab" ref={containerRef}>
|
||||||
isActive={assistant.id === activeAssistant.id}
|
<DragableList
|
||||||
sortBy={AssistantsTabSortType}
|
list={assistants}
|
||||||
onSwitch={setActiveAssistant}
|
onUpdate={updateAssistants}
|
||||||
onDelete={onDelete}
|
style={{ paddingBottom: dragging ? '34px' : 0 }}
|
||||||
addAgent={addAgent}
|
onDragStart={() => setDragging(true)}
|
||||||
addAssistant={addAssistant}
|
onDragEnd={() => setDragging(false)}>
|
||||||
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
{(assistant) => (
|
||||||
handleSortByChange={handleSortByChange}
|
<AssistantItem
|
||||||
/>
|
key={assistant.id}
|
||||||
)}
|
assistant={assistant}
|
||||||
</DragableList>
|
isActive={assistant.id === activeAssistant.id}
|
||||||
)}
|
sortBy={assistantsTabSortType}
|
||||||
|
onSwitch={setActiveAssistant}
|
||||||
|
onDelete={onDelete}
|
||||||
|
addAgent={addAgent}
|
||||||
|
addAssistant={addAssistant}
|
||||||
|
onCreateDefaultAssistant={onCreateDefaultAssistant}
|
||||||
|
handleSortByChange={handleSortByChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DragableList>
|
||||||
{!dragging && (
|
{!dragging && (
|
||||||
<AssistantAddItem onClick={onCreateAssistant}>
|
<AssistantAddItem onClick={onCreateAssistant}>
|
||||||
<AssistantName>
|
<AssistantName>
|
||||||
@ -128,7 +138,6 @@ const TagsContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: px;
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const AssistantAddItem = styled.div`
|
const AssistantAddItem = styled.div`
|
||||||
@ -158,6 +167,7 @@ const GroupTitle = styled.div`
|
|||||||
color: var(--color-text-2);
|
color: var(--color-text-2);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
margin-bottom: -8px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const GroupTitleName = styled.div`
|
const GroupTitleName = styled.div`
|
||||||
|
|||||||
@ -1,21 +1,18 @@
|
|||||||
import {
|
import {
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
MenuOutlined,
|
|
||||||
MinusCircleOutlined,
|
MinusCircleOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
SaveOutlined,
|
SaveOutlined,
|
||||||
SmileOutlined,
|
SmileOutlined,
|
||||||
SortAscendingOutlined,
|
SortAscendingOutlined,
|
||||||
SortDescendingOutlined,
|
SortDescendingOutlined
|
||||||
TagsOutlined
|
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
|
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
|
||||||
import EmojiIcon from '@renderer/components/EmojiIcon'
|
import EmojiIcon from '@renderer/components/EmojiIcon'
|
||||||
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
||||||
import TagsPopup from '@renderer/components/Popups/TagsPopup'
|
import PromptPopup from '@renderer/components/Popups/PromptPopup'
|
||||||
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
|
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { modelGenerating } from '@renderer/hooks/useRuntime'
|
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useTags } from '@renderer/hooks/useTags'
|
import { useTags } from '@renderer/hooks/useTags'
|
||||||
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
|
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
|
||||||
@ -24,14 +21,16 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
|||||||
import { Assistant, AssistantsSortType } from '@renderer/types'
|
import { Assistant, AssistantsSortType } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
import { uuid } from '@renderer/utils'
|
||||||
import { hasTopicPendingRequests } from '@renderer/utils/queue'
|
import { hasTopicPendingRequests } from '@renderer/utils/queue'
|
||||||
import { Dropdown } from 'antd'
|
import { Dropdown, MenuProps } from 'antd'
|
||||||
import { ItemType } from 'antd/es/menu/interface'
|
|
||||||
import { omit } from 'lodash'
|
import { omit } from 'lodash'
|
||||||
import { FC, startTransition, useCallback, useEffect, useState } from 'react'
|
import { AlignJustify, Plus, Settings2, Tag, Tags } from 'lucide-react'
|
||||||
|
import { FC, memo, startTransition, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import * as tinyPinyin from 'tiny-pinyin'
|
import * as tinyPinyin from 'tiny-pinyin'
|
||||||
|
|
||||||
|
import AssistantTagsPopup from './AssistantTagsPopup'
|
||||||
|
|
||||||
interface AssistantItemProps {
|
interface AssistantItemProps {
|
||||||
assistant: Assistant
|
assistant: Assistant
|
||||||
isActive: boolean
|
isActive: boolean
|
||||||
@ -57,219 +56,70 @@ const AssistantItem: FC<AssistantItemProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { allTags } = useTags()
|
const { allTags } = useTags()
|
||||||
const { removeAllTopics } = useAssistant(assistant.id) // 使用当前助手的ID
|
const { removeAllTopics } = useAssistant(assistant.id)
|
||||||
const { clickAssistantToShowTopic, topicPosition, assistantIconType, setAssistantIconType } = useSettings()
|
const { clickAssistantToShowTopic, topicPosition, assistantIconType, setAssistantIconType } = useSettings()
|
||||||
const defaultModel = getDefaultModel()
|
const defaultModel = getDefaultModel()
|
||||||
const { assistants, updateAssistants } = useAssistants()
|
const { assistants, updateAssistants } = useAssistants()
|
||||||
|
|
||||||
const [isPending, setIsPending] = useState(false)
|
const [isPending, setIsPending] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
setIsPending(false)
|
setIsPending(false)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasPending = assistant.topics.some((topic) => hasTopicPendingRequests(topic.id))
|
const hasPending = assistant.topics.some((topic) => hasTopicPendingRequests(topic.id))
|
||||||
if (hasPending) {
|
setIsPending(hasPending)
|
||||||
setIsPending(true)
|
|
||||||
}
|
|
||||||
}, [isActive, assistant.topics])
|
}, [isActive, assistant.topics])
|
||||||
|
|
||||||
const sortByPinyinAsc = useCallback(() => {
|
const sortByPinyinAsc = useCallback(() => {
|
||||||
const sorted = [...assistants].sort((a, b) => {
|
updateAssistants(sortAssistantsByPinyin(assistants, true))
|
||||||
const pinyinA = tinyPinyin.convertToPinyin(a.name, '', true)
|
|
||||||
const pinyinB = tinyPinyin.convertToPinyin(b.name, '', true)
|
|
||||||
return pinyinA.localeCompare(pinyinB)
|
|
||||||
})
|
|
||||||
updateAssistants(sorted)
|
|
||||||
}, [assistants, updateAssistants])
|
}, [assistants, updateAssistants])
|
||||||
|
|
||||||
const sortByPinyinDesc = useCallback(() => {
|
const sortByPinyinDesc = useCallback(() => {
|
||||||
const sorted = [...assistants].sort((a, b) => {
|
updateAssistants(sortAssistantsByPinyin(assistants, false))
|
||||||
const pinyinA = tinyPinyin.convertToPinyin(a.name, '', true)
|
|
||||||
const pinyinB = tinyPinyin.convertToPinyin(b.name, '', true)
|
|
||||||
return pinyinB.localeCompare(pinyinA)
|
|
||||||
})
|
|
||||||
updateAssistants(sorted)
|
|
||||||
}, [assistants, updateAssistants])
|
}, [assistants, updateAssistants])
|
||||||
|
|
||||||
const getMenuItems = useCallback(
|
const menuItems = useMemo(
|
||||||
(assistant: Assistant): ItemType[] => [
|
() =>
|
||||||
{
|
getMenuItems({
|
||||||
label: t('assistants.edit.title'),
|
assistant,
|
||||||
key: 'edit',
|
t,
|
||||||
icon: <EditOutlined />,
|
allTags,
|
||||||
onClick: () => AssistantSettingsPopup.show({ assistant })
|
assistants,
|
||||||
},
|
updateAssistants,
|
||||||
{
|
addAgent,
|
||||||
label: t('assistants.copy.title'),
|
addAssistant,
|
||||||
key: 'duplicate',
|
onSwitch,
|
||||||
icon: <CopyIcon />,
|
onDelete,
|
||||||
onClick: async () => {
|
removeAllTopics,
|
||||||
const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic(assistant.id)] }
|
setAssistantIconType,
|
||||||
addAssistant(_assistant)
|
sortBy,
|
||||||
onSwitch(_assistant)
|
handleSortByChange,
|
||||||
}
|
sortByPinyinAsc,
|
||||||
},
|
sortByPinyinDesc
|
||||||
{
|
}),
|
||||||
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'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('assistants.icon.type'),
|
|
||||||
key: 'icon-type',
|
|
||||||
icon: <SmileOutlined />,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: t('settings.assistant.icon.type.model'),
|
|
||||||
key: 'model',
|
|
||||||
onClick: () => setAssistantIconType('model')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('settings.assistant.icon.type.emoji'),
|
|
||||||
key: 'emoji',
|
|
||||||
onClick: () => setAssistantIconType('emoji')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('settings.assistant.icon.type.none'),
|
|
||||||
key: 'none',
|
|
||||||
onClick: () => setAssistantIconType('none')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
label: t('assistants.tags.manage'),
|
|
||||||
key: 'all-tags',
|
|
||||||
icon: <PlusOutlined />,
|
|
||||||
children: [
|
|
||||||
...allTags.map((tag) => ({
|
|
||||||
label: tag,
|
|
||||||
icon: assistant.tags?.includes(tag) ? <DeleteOutlined /> : <TagsOutlined />,
|
|
||||||
danger: assistant.tags?.includes(tag) ? true : false,
|
|
||||||
key: `all-tag-${tag}`,
|
|
||||||
onClick: () => {
|
|
||||||
if (assistant.tags?.includes(tag)) {
|
|
||||||
// 如果已有该标签,则移除
|
|
||||||
updateAssistants(assistants.map((a) => (a.id === assistant.id ? { ...a, tags: [] } : a)))
|
|
||||||
} else {
|
|
||||||
// 如果没有该标签,则切换到该标签分类
|
|
||||||
updateAssistants(assistants.map((a) => (a.id === assistant.id ? { ...a, tags: [tag] } : a)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
allTags.length > 0 ? { type: 'divider' } : null,
|
|
||||||
{
|
|
||||||
label: t('assistants.tags.add'),
|
|
||||||
key: 'new-tag',
|
|
||||||
onClick: () => {
|
|
||||||
TagsPopup.show(
|
|
||||||
assistant,
|
|
||||||
(updated) => {
|
|
||||||
updateAssistants(assistants.map((a) => (a.id === assistant.id ? updated : a)))
|
|
||||||
},
|
|
||||||
'add'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
allTags.length > 0
|
|
||||||
? {
|
|
||||||
label: t('assistants.tags.manage'),
|
|
||||||
key: 'manage-tags',
|
|
||||||
onClick: () => {
|
|
||||||
TagsPopup.show(
|
|
||||||
assistant,
|
|
||||||
(updated) => {
|
|
||||||
updateAssistants(assistants.map((a) => (a.id === assistant.id ? updated : a)))
|
|
||||||
},
|
|
||||||
'manage'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: sortBy === 'list' ? t('assistants.list.showByTags') : t('assistants.list.showByList'),
|
|
||||||
key: 'switch-view',
|
|
||||||
icon: sortBy === 'list' ? <TagsOutlined /> : <MenuOutlined />,
|
|
||||||
onClick: () => {
|
|
||||||
sortBy === 'list' ? handleSortByChange?.('tags') : handleSortByChange?.('list')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('common.sort.pinyin.asc'),
|
|
||||||
key: 'sort-asc',
|
|
||||||
icon: <SortAscendingOutlined />,
|
|
||||||
onClick: () => sortByPinyinAsc()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('common.sort.pinyin.desc'),
|
|
||||||
key: 'sort-desc',
|
|
||||||
icon: <SortDescendingOutlined />,
|
|
||||||
onClick: () => sortByPinyinDesc()
|
|
||||||
},
|
|
||||||
{ 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,
|
assistant,
|
||||||
addAssistant,
|
t,
|
||||||
allTags,
|
allTags,
|
||||||
assistants,
|
assistants,
|
||||||
handleSortByChange,
|
updateAssistants,
|
||||||
onDelete,
|
addAgent,
|
||||||
|
addAssistant,
|
||||||
onSwitch,
|
onSwitch,
|
||||||
|
onDelete,
|
||||||
removeAllTopics,
|
removeAllTopics,
|
||||||
setAssistantIconType,
|
setAssistantIconType,
|
||||||
sortBy,
|
sortBy,
|
||||||
|
handleSortByChange,
|
||||||
sortByPinyinAsc,
|
sortByPinyinAsc,
|
||||||
sortByPinyinDesc,
|
sortByPinyinDesc
|
||||||
t,
|
|
||||||
updateAssistants
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleSwitch = useCallback(async () => {
|
const handleSwitch = useCallback(async () => {
|
||||||
await modelGenerating()
|
|
||||||
|
|
||||||
if (clickAssistantToShowTopic) {
|
if (clickAssistantToShowTopic) {
|
||||||
if (topicPosition === 'left') {
|
if (topicPosition === 'left') {
|
||||||
EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)
|
EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)
|
||||||
@ -282,11 +132,14 @@ const AssistantItem: FC<AssistantItemProps> = ({
|
|||||||
}
|
}
|
||||||
}, [clickAssistantToShowTopic, onSwitch, assistant, topicPosition])
|
}, [clickAssistantToShowTopic, onSwitch, assistant, topicPosition])
|
||||||
|
|
||||||
const assistantName = assistant.name || t('chat.default.name')
|
const assistantName = useMemo(() => assistant.name || t('chat.default.name'), [assistant.name, t])
|
||||||
const fullAssistantName = assistant.emoji ? `${assistant.emoji} ${assistantName}` : assistantName
|
const fullAssistantName = useMemo(
|
||||||
|
() => (assistant.emoji ? `${assistant.emoji} ${assistantName}` : assistantName),
|
||||||
|
[assistant.emoji, assistantName]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown menu={{ items: getMenuItems(assistant) }} trigger={['contextMenu']}>
|
<Dropdown menu={{ items: menuItems }} trigger={['contextMenu']}>
|
||||||
<Container onClick={handleSwitch} className={isActive ? 'active' : ''}>
|
<Container onClick={handleSwitch} className={isActive ? 'active' : ''}>
|
||||||
<AssistantNameRow className="name" title={fullAssistantName}>
|
<AssistantNameRow className="name" title={fullAssistantName}>
|
||||||
{assistantIconType === 'model' ? (
|
{assistantIconType === 'model' ? (
|
||||||
@ -315,6 +168,217 @@ const AssistantItem: FC<AssistantItemProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提取排序相关的工具函数
|
||||||
|
const sortAssistantsByPinyin = (assistants: Assistant[], isAscending: boolean) => {
|
||||||
|
return [...assistants].sort((a, b) => {
|
||||||
|
const pinyinA = tinyPinyin.convertToPinyin(a.name, '', true)
|
||||||
|
const pinyinB = tinyPinyin.convertToPinyin(b.name, '', true)
|
||||||
|
return isAscending ? pinyinA.localeCompare(pinyinB) : pinyinB.localeCompare(pinyinA)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取标签相关的操作函数
|
||||||
|
const handleTagOperation = (
|
||||||
|
tag: string,
|
||||||
|
assistant: Assistant,
|
||||||
|
assistants: Assistant[],
|
||||||
|
updateAssistants: (assistants: Assistant[]) => void
|
||||||
|
) => {
|
||||||
|
if (assistant.tags?.includes(tag)) {
|
||||||
|
updateAssistants(assistants.map((a) => (a.id === assistant.id ? { ...a, tags: [] } : a)))
|
||||||
|
} else {
|
||||||
|
updateAssistants(assistants.map((a) => (a.id === assistant.id ? { ...a, tags: [tag] } : a)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取创建菜单项的函数
|
||||||
|
const createTagMenuItems = (
|
||||||
|
allTags: string[],
|
||||||
|
assistant: Assistant,
|
||||||
|
assistants: Assistant[],
|
||||||
|
updateAssistants: (assistants: Assistant[]) => void,
|
||||||
|
t: (key: string) => string
|
||||||
|
): MenuProps['items'] => {
|
||||||
|
const items: MenuProps['items'] = [
|
||||||
|
...allTags.map((tag) => ({
|
||||||
|
label: tag,
|
||||||
|
icon: assistant.tags?.includes(tag) ? <DeleteOutlined size={14} /> : <Tag size={12} />,
|
||||||
|
danger: assistant.tags?.includes(tag),
|
||||||
|
key: `all-tag-${tag}`,
|
||||||
|
onClick: () => handleTagOperation(tag, assistant, assistants, updateAssistants)
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
|
||||||
|
if (allTags.length > 0) {
|
||||||
|
items.push({ type: 'divider' })
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
label: t('assistants.tags.add'),
|
||||||
|
key: 'new-tag',
|
||||||
|
icon: <Plus size={16} />,
|
||||||
|
onClick: async () => {
|
||||||
|
const tagName = await PromptPopup.show({
|
||||||
|
title: t('assistants.tags.add'),
|
||||||
|
message: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
if (tagName && tagName.trim()) {
|
||||||
|
updateAssistants(assistants.map((a) => (a.id === assistant.id ? { ...a, tags: [tagName.trim()] } : a)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (allTags.length > 0) {
|
||||||
|
items.push({
|
||||||
|
label: t('assistants.tags.manage'),
|
||||||
|
key: 'manage-tags',
|
||||||
|
icon: <Settings2 size={16} />,
|
||||||
|
onClick: () => {
|
||||||
|
AssistantTagsPopup.show({ title: t('assistants.tags.manage') })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取创建菜单配置的函数
|
||||||
|
function getMenuItems({
|
||||||
|
assistant,
|
||||||
|
t,
|
||||||
|
allTags,
|
||||||
|
assistants,
|
||||||
|
updateAssistants,
|
||||||
|
addAgent,
|
||||||
|
addAssistant,
|
||||||
|
onSwitch,
|
||||||
|
onDelete,
|
||||||
|
removeAllTopics,
|
||||||
|
setAssistantIconType,
|
||||||
|
sortBy,
|
||||||
|
handleSortByChange,
|
||||||
|
sortByPinyinAsc,
|
||||||
|
sortByPinyinDesc
|
||||||
|
}): MenuProps['items'] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('assistants.icon.type'),
|
||||||
|
key: 'icon-type',
|
||||||
|
icon: <SmileOutlined />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: t('settings.assistant.icon.type.model'),
|
||||||
|
key: 'model',
|
||||||
|
onClick: () => setAssistantIconType('model')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('settings.assistant.icon.type.emoji'),
|
||||||
|
key: 'emoji',
|
||||||
|
onClick: () => setAssistantIconType('emoji')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('settings.assistant.icon.type.none'),
|
||||||
|
key: 'none',
|
||||||
|
onClick: () => setAssistantIconType('none')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'divider'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('assistants.tags.manage'),
|
||||||
|
key: 'all-tags',
|
||||||
|
icon: <PlusOutlined />,
|
||||||
|
children: createTagMenuItems(allTags, assistant, assistants, updateAssistants, t)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: sortBy === 'list' ? t('assistants.list.showByTags') : t('assistants.list.showByList'),
|
||||||
|
key: 'switch-view',
|
||||||
|
icon: sortBy === 'list' ? <Tags size={14} /> : <AlignJustify size={14} />,
|
||||||
|
onClick: () => {
|
||||||
|
sortBy === 'list' ? handleSortByChange?.('tags') : handleSortByChange?.('list')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('common.sort.pinyin.asc'),
|
||||||
|
key: 'sort-asc',
|
||||||
|
icon: <SortAscendingOutlined />,
|
||||||
|
onClick: sortByPinyinAsc
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('common.sort.pinyin.desc'),
|
||||||
|
key: 'sort-desc',
|
||||||
|
icon: <SortDescendingOutlined />,
|
||||||
|
onClick: sortByPinyinDesc
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -336,8 +400,6 @@ const Container = styled.div`
|
|||||||
&.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 {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -382,4 +444,4 @@ const TopicCount = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
`
|
`
|
||||||
|
|
||||||
export default AssistantItem
|
export default memo(AssistantItem)
|
||||||
|
|||||||
@ -0,0 +1,123 @@
|
|||||||
|
import { Box } from '@renderer/components/Layout'
|
||||||
|
import { TopView } from '@renderer/components/TopView'
|
||||||
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
|
import { useTags } from '@renderer/hooks/useTags'
|
||||||
|
import { Button, Empty, Modal } from 'antd'
|
||||||
|
import { isEmpty } from 'lodash'
|
||||||
|
import { Trash } from 'lucide-react'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
interface ShowParams {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props extends ShowParams {
|
||||||
|
resolve: (data: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
const { allTags, getAssistantsByTag } = useTags()
|
||||||
|
const { assistants, updateAssistants } = useAssistants()
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const onOk = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
resolve({})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = (removedTag: string) => {
|
||||||
|
window.modal.confirm({
|
||||||
|
title: t('assistants.tags.deleteConfirm'),
|
||||||
|
centered: true,
|
||||||
|
onOk: () => {
|
||||||
|
const relatedAssistants = getAssistantsByTag(removedTag)
|
||||||
|
if (!isEmpty(relatedAssistants)) {
|
||||||
|
updateAssistants(
|
||||||
|
assistants.map((assistant) => {
|
||||||
|
const findedAssitant = relatedAssistants.find((_assistant) => _assistant.id === assistant.id)
|
||||||
|
return findedAssitant ? { ...findedAssitant, tags: [] } : assistant
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
AssistantTagsPopup.hide = onCancel
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={title}
|
||||||
|
open={open}
|
||||||
|
onOk={onOk}
|
||||||
|
onCancel={onCancel}
|
||||||
|
afterClose={onClose}
|
||||||
|
footer={null}
|
||||||
|
transitionName="animation-move-down"
|
||||||
|
centered>
|
||||||
|
<Container>
|
||||||
|
{allTags.map((tag) => (
|
||||||
|
<TagItem key={tag}>
|
||||||
|
<Box mr={8}>{tag}</Box>
|
||||||
|
<Button type="text" icon={<Trash size={16} />} danger onClick={() => onDelete(tag)} />
|
||||||
|
</TagItem>
|
||||||
|
))}
|
||||||
|
{allTags.length === 0 && <Empty description="" />}
|
||||||
|
</Container>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
padding: 12px 0;
|
||||||
|
height: 50vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const TagItem = 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;
|
||||||
|
`
|
||||||
|
|
||||||
|
const TopViewKey = 'AssistantTagsPopup'
|
||||||
|
|
||||||
|
export default class AssistantTagsPopup {
|
||||||
|
static topviewId = 0
|
||||||
|
static hide() {
|
||||||
|
TopView.hide(TopViewKey)
|
||||||
|
}
|
||||||
|
static show(props: ShowParams) {
|
||||||
|
return new Promise<any>((resolve) => {
|
||||||
|
TopView.show(
|
||||||
|
<PopupContainer
|
||||||
|
{...props}
|
||||||
|
resolve={(v) => {
|
||||||
|
resolve(v)
|
||||||
|
TopView.hide(TopViewKey)
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
TopViewKey
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,171 +0,0 @@
|
|||||||
import { CheckOutlined, CloseOutlined } from '@ant-design/icons'
|
|
||||||
import { Box } from '@renderer/components/Layout'
|
|
||||||
import { useAssistants } from '@renderer/hooks/useAssistant'
|
|
||||||
import { useTags } from '@renderer/hooks/useTags'
|
|
||||||
import { Assistant } from '@renderer/types'
|
|
||||||
import type { InputRef } from 'antd'
|
|
||||||
import { Divider, Input, Space, Tag } from 'antd'
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import styled from 'styled-components'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
assistant: Assistant
|
|
||||||
updateAssistant: (assistant: Assistant) => void
|
|
||||||
mode?: 'add' | 'manage'
|
|
||||||
}
|
|
||||||
|
|
||||||
const AssistantTagsSettings: React.FC<Props> = ({ assistant, updateAssistant, mode = 'manage' }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { allTags } = useTags()
|
|
||||||
const [showMode, setShowMode] = useState<'add' | 'manage'>(mode)
|
|
||||||
const [filteredTags, setFilteredTags] = useState<string[]>(allTags)
|
|
||||||
const [currentTags, setCurrentTags] = useState<string[]>([...(assistant.tags || [])])
|
|
||||||
const [tempTag, setTempTag] = useState<string | undefined>(mode === 'add' ? '' : assistant.tags?.[0])
|
|
||||||
const [inputTag, setInputTag] = useState<string>('')
|
|
||||||
const { assistants, updateAssistants } = useAssistants()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFilteredTags(allTags)
|
|
||||||
}, [allTags])
|
|
||||||
|
|
||||||
const inputRef = useRef<InputRef>(null)
|
|
||||||
|
|
||||||
const { getAssistantsByTag } = useTags()
|
|
||||||
|
|
||||||
const handleClose = (removedTag: string) => {
|
|
||||||
// 更新所有关联该tag的助手
|
|
||||||
const relatedAssistants = getAssistantsByTag(removedTag)
|
|
||||||
setCurrentTags(currentTags.filter((tag) => tag !== removedTag)) // 点击下面移除是不需要缓存的
|
|
||||||
updateAssistants(
|
|
||||||
assistants.map((assistant) => {
|
|
||||||
const findedAssitant = relatedAssistants.find((_assistant) => _assistant.id === assistant.id)
|
|
||||||
if (findedAssitant) {
|
|
||||||
return { ...findedAssitant, tags: [] }
|
|
||||||
}
|
|
||||||
return assistant
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
{showMode === 'manage' && (
|
|
||||||
<>
|
|
||||||
<Box mb={8} style={{ fontWeight: 'bold' }}>
|
|
||||||
{t('assistants.tags.settings.title')}
|
|
||||||
</Box>
|
|
||||||
<TagsContainer>
|
|
||||||
<span>{t('assistants.tags.settings.current')}:</span>
|
|
||||||
{tempTag && (
|
|
||||||
<Tag
|
|
||||||
key={tempTag}
|
|
||||||
closeIcon={<CloseOutlined />}
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer'
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setTempTag('')
|
|
||||||
// 防止删除以后,没有tag的助手被删除,写个暂存的。方便恢复回去
|
|
||||||
setCurrentTags([...new Set([...currentTags, tempTag])])
|
|
||||||
updateAssistant({ ...assistant, tags: [] })
|
|
||||||
}}>
|
|
||||||
{tempTag}
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{!tempTag && <span style={{ color: 'var(--color-text-3)' }}>{t('assistants.tags.none')}</span>}
|
|
||||||
</TagsContainer>
|
|
||||||
<Divider style={{ margin: 8 }}></Divider>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Space.Compact direction="vertical" style={{ width: '100%', gap: 8 }}>
|
|
||||||
{showMode === 'add' && (
|
|
||||||
<>
|
|
||||||
<Box mb={8} style={{ fontWeight: 'bold' }}>
|
|
||||||
{t('assistants.tags.settings.addTagsPlaceholder')}
|
|
||||||
</Box>
|
|
||||||
<Input
|
|
||||||
ref={inputRef}
|
|
||||||
value={inputTag}
|
|
||||||
autoFocus
|
|
||||||
onChange={(e) => setInputTag(e.target.value)}
|
|
||||||
suffix={
|
|
||||||
<>
|
|
||||||
{+inputTag?.length > 0 && (
|
|
||||||
<CheckOutlined
|
|
||||||
onClick={() => {
|
|
||||||
if (inputTag) {
|
|
||||||
setInputTag('')
|
|
||||||
setTempTag(inputTag)
|
|
||||||
updateAssistant({ ...assistant, tags: [inputTag] })
|
|
||||||
setShowMode('manage')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{ color: 'var(--color-primary)', cursor: 'pointer' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{showMode === 'manage' && (
|
|
||||||
<div>
|
|
||||||
<Box mb={8} style={{ fontWeight: 'bold' }}>
|
|
||||||
{t('assistants.tags.settings.searchTagsPlaceholder')}
|
|
||||||
</Box>
|
|
||||||
<Input
|
|
||||||
placeholder={t('assistants.tags.settings.searchTagsPlaceholder')}
|
|
||||||
style={{ marginBottom: 8 }}
|
|
||||||
onChange={(e) => {
|
|
||||||
const searchValue = e.target.value.toLowerCase()
|
|
||||||
setFilteredTags(allTags?.filter((tag) => tag.toLowerCase().includes(searchValue)))
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Box mb={8} style={{ fontWeight: 'bold' }}>
|
|
||||||
{t('assistants.tags.settings.tagsLsitTitle')}
|
|
||||||
<em style={{ fontSize: '12px' }}>({t('assistants.tags.settings.tagsLsitTitleTips')})</em>
|
|
||||||
</Box>
|
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
|
|
||||||
{[...new Set([...filteredTags, ...currentTags])]
|
|
||||||
.filter((_) => _ !== tempTag)
|
|
||||||
?.map((tag) => (
|
|
||||||
<Tag
|
|
||||||
key={tag}
|
|
||||||
closeIcon={<CloseOutlined style={{ color: 'var(--color-text)' }} />}
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
background: 'var(--color-background-mute)',
|
|
||||||
color: 'var(--color-text)'
|
|
||||||
}}
|
|
||||||
onClose={() => handleClose(tag)}
|
|
||||||
onClick={() => {
|
|
||||||
setTempTag(tag)
|
|
||||||
updateAssistant({ ...assistant, tags: [tag] })
|
|
||||||
}}>
|
|
||||||
{tag}
|
|
||||||
</Tag>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* <PlusCircleOutlined onClick={handleAddClick} style={{ color: 'var(--color-primary)', cursor: 'pointer' }} /> */}
|
|
||||||
</Space.Compact>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Container = styled.div`
|
|
||||||
padding: 10px;
|
|
||||||
`
|
|
||||||
|
|
||||||
const TagsContainer = styled.div`
|
|
||||||
margin: 10px 0;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
`
|
|
||||||
|
|
||||||
export default AssistantTagsSettings
|
|
||||||
@ -15,14 +15,13 @@ import AssistantMessagesSettings from './AssistantMessagesSettings'
|
|||||||
import AssistantModelSettings from './AssistantModelSettings'
|
import AssistantModelSettings from './AssistantModelSettings'
|
||||||
import AssistantPromptSettings from './AssistantPromptSettings'
|
import AssistantPromptSettings from './AssistantPromptSettings'
|
||||||
import AssistantRegularPromptsSettings from './AssistantRegularPromptsSettings'
|
import AssistantRegularPromptsSettings from './AssistantRegularPromptsSettings'
|
||||||
import AssistantTagsSettings from './AssistantTagsSettings'
|
|
||||||
|
|
||||||
interface AssistantSettingPopupShowParams {
|
interface AssistantSettingPopupShowParams {
|
||||||
assistant: Assistant
|
assistant: Assistant
|
||||||
tab?: AssistantSettingPopupTab
|
tab?: AssistantSettingPopupTab
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssistantSettingPopupTab = 'prompt' | 'model' | 'messages' | 'knowledge_base' | 'mcp' | 'regular_phrases' | 'tags'
|
type AssistantSettingPopupTab = 'prompt' | 'model' | 'messages' | 'knowledge_base' | 'mcp' | 'regular_phrases'
|
||||||
|
|
||||||
interface Props extends AssistantSettingPopupShowParams {
|
interface Props extends AssistantSettingPopupShowParams {
|
||||||
resolve: (assistant: Assistant) => void
|
resolve: (assistant: Assistant) => void
|
||||||
@ -79,10 +78,6 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
|||||||
{
|
{
|
||||||
key: 'regular_phrases',
|
key: 'regular_phrases',
|
||||||
label: t('assistants.settings.regular_phrases.title', 'Regular Prompts')
|
label: t('assistants.settings.regular_phrases.title', 'Regular Prompts')
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'tags',
|
|
||||||
label: t('assistants.tags.settings.title')
|
|
||||||
}
|
}
|
||||||
].filter(Boolean) as { key: string; label: string }[]
|
].filter(Boolean) as { key: string; label: string }[]
|
||||||
|
|
||||||
@ -155,7 +150,6 @@ const AssistantSettingPopupContainer: React.FC<Props> = ({ resolve, tab, ...prop
|
|||||||
{menu === 'regular_phrases' && (
|
{menu === 'regular_phrases' && (
|
||||||
<AssistantRegularPromptsSettings assistant={assistant} updateAssistant={updateAssistant} />
|
<AssistantRegularPromptsSettings assistant={assistant} updateAssistant={updateAssistant} />
|
||||||
)}
|
)}
|
||||||
{menu === 'tags' && <AssistantTagsSettings assistant={assistant} updateAssistant={updateAssistant} />}
|
|
||||||
</Settings>
|
</Settings>
|
||||||
</HStack>
|
</HStack>
|
||||||
</StyledModal>
|
</StyledModal>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user