mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-19 14:41:24 +08:00
feat(assistants): enhance ManageAssistantPresetsPopup with sort and batch delete modes (#11835)
* feat(assistants): enhance ManageAssistantPresetsPopup with sort and batch delete modes - Merge sorting and batch delete functionality into a single popup - Add Segmented control to switch between sort and delete modes - Sort mode: drag and drop to reorder assistants using DraggableList - Delete mode: select and batch delete assistants with checkbox - Add "+100" button for quick batch selection when there are many presets - Add manage button to AssistantPresetsPage header - Update AssistantPresetCard menu to use the new ManageAssistantPresetsPopup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(assistants): improve selection logic in ManageAssistantPresetsPopup - Update the "+100" button functionality to select the next 100 unselected presets starting from the last selected preset. - Enhance user experience by ensuring that the selection continues from the correct index, allowing for more intuitive batch selection of presets. * feat(assistants): adjust initial mode in ManageAssistantPresetsPopup based on preset count - Modify the initial state of the mode to switch between 'delete' and 'sort' based on the number of presets available, enhancing user experience by optimizing the default action for larger preset collections. --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a91c69982c
commit
595a0f194a
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "Enter JSON URL"
|
"url_placeholder": "Enter JSON URL"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "Batch Delete",
|
||||||
|
"confirm": "Are you sure you want to delete the selected {{count}} assistants?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "Delete",
|
||||||
|
"sort": "Sort"
|
||||||
|
},
|
||||||
"title": "Manage Assistants"
|
"title": "Manage Assistants"
|
||||||
},
|
},
|
||||||
"my_agents": "My Assistants",
|
"my_agents": "My Assistants",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
|
"select_all": "Select All",
|
||||||
"selected": "Selected",
|
"selected": "Selected",
|
||||||
"selectedItems": "Selected {{count}} items",
|
"selectedItems": "Selected {{count}} items",
|
||||||
"selectedMessages": "Selected {{count}} messages",
|
"selectedMessages": "Selected {{count}} messages",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "输入 JSON URL"
|
"url_placeholder": "输入 JSON URL"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "批量删除",
|
||||||
|
"confirm": "确定要删除选中的 {{count}} 个助手吗?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "删除",
|
||||||
|
"sort": "排序"
|
||||||
|
},
|
||||||
"title": "管理助手"
|
"title": "管理助手"
|
||||||
},
|
},
|
||||||
"my_agents": "我的助手",
|
"my_agents": "我的助手",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "已保存",
|
"saved": "已保存",
|
||||||
"search": "搜索",
|
"search": "搜索",
|
||||||
"select": "选择",
|
"select": "选择",
|
||||||
|
"select_all": "全选",
|
||||||
"selected": "已选择",
|
"selected": "已选择",
|
||||||
"selectedItems": "已选择 {{count}} 项",
|
"selectedItems": "已选择 {{count}} 项",
|
||||||
"selectedMessages": "选中 {{count}} 条消息",
|
"selectedMessages": "选中 {{count}} 条消息",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "輸入 JSON URL"
|
"url_placeholder": "輸入 JSON URL"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "批次刪除",
|
||||||
|
"confirm": "您確定要刪除所選的 {{count}} 個助理嗎?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "刪除",
|
||||||
|
"sort": "排序"
|
||||||
|
},
|
||||||
"title": "管理助手"
|
"title": "管理助手"
|
||||||
},
|
},
|
||||||
"my_agents": "我的助手",
|
"my_agents": "我的助手",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "已儲存",
|
"saved": "已儲存",
|
||||||
"search": "搜尋",
|
"search": "搜尋",
|
||||||
"select": "選擇",
|
"select": "選擇",
|
||||||
|
"select_all": "全選",
|
||||||
"selected": "已選擇",
|
"selected": "已選擇",
|
||||||
"selectedItems": "已選擇 {{count}} 項",
|
"selectedItems": "已選擇 {{count}} 項",
|
||||||
"selectedMessages": "選中 {{count}} 條訊息",
|
"selectedMessages": "選中 {{count}} 條訊息",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "JSON-URL eingeben"
|
"url_placeholder": "JSON-URL eingeben"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "Stapel löschen",
|
||||||
|
"confirm": "Sind Sie sicher, dass Sie die ausgewählten {{count}} Assistenten löschen möchten?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "Löschen",
|
||||||
|
"sort": "Sortieren"
|
||||||
|
},
|
||||||
"title": "Assistenten verwalten"
|
"title": "Assistenten verwalten"
|
||||||
},
|
},
|
||||||
"my_agents": "Meine Assistenten",
|
"my_agents": "Meine Assistenten",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "Gespeichert",
|
"saved": "Gespeichert",
|
||||||
"search": "Suchen",
|
"search": "Suchen",
|
||||||
"select": "Auswählen",
|
"select": "Auswählen",
|
||||||
|
"select_all": "Alle auswählen",
|
||||||
"selected": "Ausgewählt",
|
"selected": "Ausgewählt",
|
||||||
"selectedItems": "{{count}} Elemente ausgewählt",
|
"selectedItems": "{{count}} Elemente ausgewählt",
|
||||||
"selectedMessages": "{{count}} Nachrichten ausgewählt",
|
"selectedMessages": "{{count}} Nachrichten ausgewählt",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "Εισάγετε JSON URL"
|
"url_placeholder": "Εισάγετε JSON URL"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "Μαζική Διαγραφή",
|
||||||
|
"confirm": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τους επιλεγμένους {{count}} βοηθούς;"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "Διαγραφή",
|
||||||
|
"sort": "Ταξινόμηση"
|
||||||
|
},
|
||||||
"title": "Διαχείριση βοηθών"
|
"title": "Διαχείριση βοηθών"
|
||||||
},
|
},
|
||||||
"my_agents": "Οι βοηθοί μου",
|
"my_agents": "Οι βοηθοί μου",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "Αποθηκεύτηκε",
|
"saved": "Αποθηκεύτηκε",
|
||||||
"search": "Αναζήτηση",
|
"search": "Αναζήτηση",
|
||||||
"select": "Επιλογή",
|
"select": "Επιλογή",
|
||||||
|
"select_all": "Επιλογή Όλων",
|
||||||
"selected": "Επιλεγμένο",
|
"selected": "Επιλεγμένο",
|
||||||
"selectedItems": "Επιλέχθηκαν {{count}} αντικείμενα",
|
"selectedItems": "Επιλέχθηκαν {{count}} αντικείμενα",
|
||||||
"selectedMessages": "Επιλέχθηκαν {{count}} μηνύματα",
|
"selectedMessages": "Επιλέχθηκαν {{count}} μηνύματα",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "Introducir URL JSON"
|
"url_placeholder": "Introducir URL JSON"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "Eliminación por lotes",
|
||||||
|
"confirm": "¿Estás seguro de que quieres eliminar los {{count}} asistentes seleccionados?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "Eliminar",
|
||||||
|
"sort": "Ordenar"
|
||||||
|
},
|
||||||
"title": "Gestionar asistentes"
|
"title": "Gestionar asistentes"
|
||||||
},
|
},
|
||||||
"my_agents": "Mis asistentes",
|
"my_agents": "Mis asistentes",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "Guardado",
|
"saved": "Guardado",
|
||||||
"search": "Buscar",
|
"search": "Buscar",
|
||||||
"select": "Seleccionar",
|
"select": "Seleccionar",
|
||||||
|
"select_all": "Seleccionar todo",
|
||||||
"selected": "Seleccionado",
|
"selected": "Seleccionado",
|
||||||
"selectedItems": "{{count}} elementos seleccionados",
|
"selectedItems": "{{count}} elementos seleccionados",
|
||||||
"selectedMessages": "{{count}} mensajes seleccionados",
|
"selectedMessages": "{{count}} mensajes seleccionados",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "Saisir l'URL JSON"
|
"url_placeholder": "Saisir l'URL JSON"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "Suppression par lot",
|
||||||
|
"confirm": "Êtes-vous sûr de vouloir supprimer les {{count}} assistants sélectionnés ?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"sort": "Trier"
|
||||||
|
},
|
||||||
"title": "Gérer les assistants"
|
"title": "Gérer les assistants"
|
||||||
},
|
},
|
||||||
"my_agents": "Mes assistants",
|
"my_agents": "Mes assistants",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "enregistré",
|
"saved": "enregistré",
|
||||||
"search": "Rechercher",
|
"search": "Rechercher",
|
||||||
"select": "Sélectionner",
|
"select": "Sélectionner",
|
||||||
|
"select_all": "Tout sélectionner",
|
||||||
"selected": "Sélectionné",
|
"selected": "Sélectionné",
|
||||||
"selectedItems": "{{count}} éléments sélectionnés",
|
"selectedItems": "{{count}} éléments sélectionnés",
|
||||||
"selectedMessages": "{{count}} messages sélectionnés",
|
"selectedMessages": "{{count}} messages sélectionnés",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "JSON URLを入力"
|
"url_placeholder": "JSON URLを入力"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "バッチ削除",
|
||||||
|
"confirm": "選択した{{count}}件のアシスタントを削除してもよろしいですか?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "削除",
|
||||||
|
"sort": "並べ替え"
|
||||||
|
},
|
||||||
"title": "アシスタントを管理"
|
"title": "アシスタントを管理"
|
||||||
},
|
},
|
||||||
"my_agents": "マイアシスタント",
|
"my_agents": "マイアシスタント",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "保存されました",
|
"saved": "保存されました",
|
||||||
"search": "検索",
|
"search": "検索",
|
||||||
"select": "選択",
|
"select": "選択",
|
||||||
|
"select_all": "すべて選択",
|
||||||
"selected": "選択済み",
|
"selected": "選択済み",
|
||||||
"selectedItems": "{{count}}件の項目を選択しました",
|
"selectedItems": "{{count}}件の項目を選択しました",
|
||||||
"selectedMessages": "{{count}}件のメッセージを選択しました",
|
"selectedMessages": "{{count}}件のメッセージを選択しました",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "Inserir URL JSON"
|
"url_placeholder": "Inserir URL JSON"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "Exclusão em Lote",
|
||||||
|
"confirm": "Tem certeza de que deseja excluir os {{count}} assistentes selecionados?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "Excluir",
|
||||||
|
"sort": "Ordenar"
|
||||||
|
},
|
||||||
"title": "Gerir assistentes"
|
"title": "Gerir assistentes"
|
||||||
},
|
},
|
||||||
"my_agents": "Os meus assistentes",
|
"my_agents": "Os meus assistentes",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "Guardado",
|
"saved": "Guardado",
|
||||||
"search": "Pesquisar",
|
"search": "Pesquisar",
|
||||||
"select": "Selecionar",
|
"select": "Selecionar",
|
||||||
|
"select_all": "Selecionar Tudo",
|
||||||
"selected": "Selecionado",
|
"selected": "Selecionado",
|
||||||
"selectedItems": "{{count}} itens selecionados",
|
"selectedItems": "{{count}} itens selecionados",
|
||||||
"selectedMessages": "{{count}} mensagens selecionadas",
|
"selectedMessages": "{{count}} mensagens selecionadas",
|
||||||
|
|||||||
@ -485,6 +485,14 @@
|
|||||||
"url_placeholder": "Введите JSON URL"
|
"url_placeholder": "Введите JSON URL"
|
||||||
},
|
},
|
||||||
"manage": {
|
"manage": {
|
||||||
|
"batch_delete": {
|
||||||
|
"button": "Массовое удаление",
|
||||||
|
"confirm": "Вы уверены, что хотите удалить выбранных {{count}} ассистентов?"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"delete": "Удалить",
|
||||||
|
"sort": "Сортировать"
|
||||||
|
},
|
||||||
"title": "Управление помощниками"
|
"title": "Управление помощниками"
|
||||||
},
|
},
|
||||||
"my_agents": "Мои помощники",
|
"my_agents": "Мои помощники",
|
||||||
@ -1199,6 +1207,7 @@
|
|||||||
"saved": "Сохранено",
|
"saved": "Сохранено",
|
||||||
"search": "Поиск",
|
"search": "Поиск",
|
||||||
"select": "Выбрать",
|
"select": "Выбрать",
|
||||||
|
"select_all": "Выбрать все",
|
||||||
"selected": "Выбрано",
|
"selected": "Выбрано",
|
||||||
"selectedItems": "Выбрано {{count}} элементов",
|
"selectedItems": "Выбрано {{count}} элементов",
|
||||||
"selectedMessages": "Выбрано {{count}} сообщений",
|
"selectedMessages": "Выбрано {{count}} сообщений",
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import type { AssistantPreset } from '@renderer/types'
|
|||||||
import { uuid } from '@renderer/utils'
|
import { uuid } from '@renderer/utils'
|
||||||
import { Button, Empty, Flex, Input } from 'antd'
|
import { Button, Empty, Flex, Input } from 'antd'
|
||||||
import { omit } from 'lodash'
|
import { omit } from 'lodash'
|
||||||
import { Import, Plus, Rss, Search } from 'lucide-react'
|
import { Import, Plus, Rss, Search, Settings2 } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -25,6 +25,7 @@ import AssistantPresetCard from './components/AssistantPresetCard'
|
|||||||
import { AssistantPresetGroupIcon } from './components/AssistantPresetGroupIcon'
|
import { AssistantPresetGroupIcon } from './components/AssistantPresetGroupIcon'
|
||||||
import AssistantsSubscribeUrlSettings from './components/AssistantsSubscribeUrlSettings'
|
import AssistantsSubscribeUrlSettings from './components/AssistantsSubscribeUrlSettings'
|
||||||
import ImportAssistantPresetPopup from './components/ImportAssistantPresetPopup'
|
import ImportAssistantPresetPopup from './components/ImportAssistantPresetPopup'
|
||||||
|
import ManageAssistantPresetsPopup from './components/ManageAssistantPresetsPopup'
|
||||||
|
|
||||||
const AssistantPresetsPage: FC = () => {
|
const AssistantPresetsPage: FC = () => {
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
@ -185,6 +186,10 @@ const AssistantPresetsPage: FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleManageAgents = () => {
|
||||||
|
ManageAssistantPresetsPopup.show()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Navbar>
|
<Navbar>
|
||||||
@ -290,6 +295,9 @@ const AssistantPresetsPage: FC = () => {
|
|||||||
<Button type="text" onClick={handleSubscribeSettings} icon={<Rss size={18} color="var(--color-icon)" />}>
|
<Button type="text" onClick={handleSubscribeSettings} icon={<Rss size={18} color="var(--color-icon)" />}>
|
||||||
{t('assistants.presets.settings.title')}
|
{t('assistants.presets.settings.title')}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button type="text" onClick={handleManageAgents} icon={<Settings2 size={18} color="var(--color-icon)" />}>
|
||||||
|
{t('assistants.presets.manage.title')}
|
||||||
|
</Button>
|
||||||
<Button type="text" onClick={handleAddAgent} icon={<Plus size={18} color="var(--color-icon)" />}>
|
<Button type="text" onClick={handleAddAgent} icon={<Plus size={18} color="var(--color-icon)" />}>
|
||||||
{t('assistants.presets.add.title')}
|
{t('assistants.presets.add.title')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { getLeadingEmoji } from '@renderer/utils'
|
|||||||
import { Button, Dropdown } from 'antd'
|
import { Button, Dropdown } from 'antd'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { isArray } from 'lodash'
|
import { isArray } from 'lodash'
|
||||||
import { ArrowDownAZ, Ellipsis, PlusIcon, SquareArrowOutUpRight } from 'lucide-react'
|
import { Ellipsis, PlusIcon, Settings2, SquareArrowOutUpRight } from 'lucide-react'
|
||||||
import { type FC, memo, useCallback, useEffect, useRef, useState } from 'react'
|
import { type FC, memo, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@ -77,9 +77,9 @@ const AssistantPresetCard: FC<Props> = ({ preset, onClick, activegroup, getLocal
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'sort',
|
key: 'manage',
|
||||||
label: t('assistants.presets.sorting.title'),
|
label: t('assistants.presets.manage.title'),
|
||||||
icon: <ArrowDownAZ size={14} />,
|
icon: <Settings2 size={14} />,
|
||||||
onClick: (e: any) => {
|
onClick: (e: any) => {
|
||||||
e.domEvent.stopPropagation()
|
e.domEvent.stopPropagation()
|
||||||
ManageAssistantPresetsPopup.show()
|
ManageAssistantPresetsPopup.show()
|
||||||
|
|||||||
@ -1,21 +1,23 @@
|
|||||||
import { MenuOutlined } from '@ant-design/icons'
|
import { MenuOutlined } from '@ant-design/icons'
|
||||||
import { DraggableList } from '@renderer/components/DraggableList'
|
import { DraggableList } from '@renderer/components/DraggableList'
|
||||||
|
import { DeleteIcon } from '@renderer/components/Icons'
|
||||||
import { Box, HStack } from '@renderer/components/Layout'
|
import { Box, HStack } from '@renderer/components/Layout'
|
||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
|
import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets'
|
||||||
import { Empty, Modal } from 'antd'
|
import type { AssistantPreset } from '@renderer/types'
|
||||||
import { useEffect, useState } from 'react'
|
import { Button, Checkbox, Empty, Modal, Segmented } from 'antd'
|
||||||
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
type Mode = 'sort' | 'delete'
|
||||||
|
|
||||||
const PopupContainer: React.FC = () => {
|
const PopupContainer: React.FC = () => {
|
||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { presets, setAssistantPresets } = useAssistantPresets()
|
const { presets, setAssistantPresets } = useAssistantPresets()
|
||||||
|
const [mode, setMode] = useState<Mode>(() => (presets.length > 50 ? 'delete' : 'sort'))
|
||||||
const onOk = () => {
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
|
||||||
setOpen(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
@ -25,17 +27,74 @@ const PopupContainer: React.FC = () => {
|
|||||||
ManageAssistantPresetsPopup.hide()
|
ManageAssistantPresetsPopup.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
const handleModeChange = (value: Mode) => {
|
||||||
if (presets.length === 0) {
|
setMode(value)
|
||||||
setOpen(false)
|
setSelectedIds(new Set())
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelectAll = () => {
|
||||||
|
if (selectedIds.size === presets.length) {
|
||||||
|
setSelectedIds(new Set())
|
||||||
|
} else {
|
||||||
|
setSelectedIds(new Set(presets.map((p) => p.id)))
|
||||||
}
|
}
|
||||||
}, [presets])
|
}
|
||||||
|
|
||||||
|
const handleSelectNext100 = () => {
|
||||||
|
// Find the last selected preset's index
|
||||||
|
let startIndex = 0
|
||||||
|
if (selectedIds.size > 0) {
|
||||||
|
for (let i = presets.length - 1; i >= 0; i--) {
|
||||||
|
if (selectedIds.has(presets[i].id)) {
|
||||||
|
startIndex = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select next 100 unselected presets starting from startIndex
|
||||||
|
const newSelected = new Set(selectedIds)
|
||||||
|
let count = 0
|
||||||
|
for (let i = startIndex; i < presets.length && count < 100; i++) {
|
||||||
|
if (!newSelected.has(presets[i].id)) {
|
||||||
|
newSelected.add(presets[i].id)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSelectedIds(newSelected)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSelect = (preset: AssistantPreset) => {
|
||||||
|
const newSelected = new Set(selectedIds)
|
||||||
|
if (newSelected.has(preset.id)) {
|
||||||
|
newSelected.delete(preset.id)
|
||||||
|
} else {
|
||||||
|
newSelected.add(preset.id)
|
||||||
|
}
|
||||||
|
setSelectedIds(newSelected)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBatchDelete = () => {
|
||||||
|
if (selectedIds.size === 0) return
|
||||||
|
|
||||||
|
window.modal.confirm({
|
||||||
|
centered: true,
|
||||||
|
content: t('assistants.presets.manage.batch_delete.confirm', { count: selectedIds.size }),
|
||||||
|
onOk: () => {
|
||||||
|
const remainingPresets = presets.filter((p) => !selectedIds.has(p.id))
|
||||||
|
setAssistantPresets(remainingPresets)
|
||||||
|
setSelectedIds(new Set())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAllSelected = presets.length > 0 && selectedIds.size === presets.length
|
||||||
|
const isIndeterminate = selectedIds.size > 0 && selectedIds.size < presets.length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={t('assistants.presets.manage.title')}
|
title={t('assistants.presets.manage.title')}
|
||||||
open={open}
|
open={open}
|
||||||
onOk={onOk}
|
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
afterClose={onClose}
|
afterClose={onClose}
|
||||||
footer={null}
|
footer={null}
|
||||||
@ -43,18 +102,78 @@ const PopupContainer: React.FC = () => {
|
|||||||
centered>
|
centered>
|
||||||
<Container>
|
<Container>
|
||||||
{presets.length > 0 && (
|
{presets.length > 0 && (
|
||||||
<DraggableList list={presets} onUpdate={setAssistantPresets}>
|
<>
|
||||||
{(item) => (
|
<ActionBar>
|
||||||
<AgentItem>
|
{mode === 'delete' ? (
|
||||||
<Box mr={8}>
|
<HStack alignItems="center">
|
||||||
{item.emoji} {item.name}
|
<Checkbox checked={isAllSelected} indeterminate={isIndeterminate} onChange={handleSelectAll}>
|
||||||
</Box>
|
{t('common.select_all')}
|
||||||
<HStack gap="15px">
|
</Checkbox>
|
||||||
<MenuOutlined style={{ cursor: 'move' }} />
|
{presets.length > 100 && selectedIds.size < presets.length && (
|
||||||
|
<Button type="link" size="small" onClick={handleSelectNext100} style={{ padding: 0 }}>
|
||||||
|
+100
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
</AgentItem>
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
<HStack gap="8px" alignItems="center">
|
||||||
|
{mode === 'delete' && (
|
||||||
|
<Button
|
||||||
|
danger
|
||||||
|
type="text"
|
||||||
|
icon={<DeleteIcon size={14} />}
|
||||||
|
disabled={selectedIds.size === 0}
|
||||||
|
onClick={handleBatchDelete}>
|
||||||
|
{t('assistants.presets.manage.batch_delete.button')} ({selectedIds.size})
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Segmented
|
||||||
|
size="small"
|
||||||
|
value={mode}
|
||||||
|
onChange={(value) => handleModeChange(value as Mode)}
|
||||||
|
options={[
|
||||||
|
{ label: t('assistants.presets.manage.mode.sort'), value: 'sort' },
|
||||||
|
{ label: t('assistants.presets.manage.mode.delete'), value: 'delete' }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</ActionBar>
|
||||||
|
|
||||||
|
{mode === 'sort' ? (
|
||||||
|
<AgentList>
|
||||||
|
<DraggableList list={presets} onUpdate={setAssistantPresets}>
|
||||||
|
{(item) => (
|
||||||
|
<AgentItem>
|
||||||
|
<Box mr={8}>
|
||||||
|
{item.emoji} {item.name}
|
||||||
|
</Box>
|
||||||
|
<HStack gap="15px">
|
||||||
|
<MenuOutlined style={{ cursor: 'move' }} />
|
||||||
|
</HStack>
|
||||||
|
</AgentItem>
|
||||||
|
)}
|
||||||
|
</DraggableList>
|
||||||
|
</AgentList>
|
||||||
|
) : (
|
||||||
|
<AgentList>
|
||||||
|
{presets.map((item) => (
|
||||||
|
<SelectableAgentItem
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => handleSelect(item)}
|
||||||
|
$selected={selectedIds.has(item.id)}>
|
||||||
|
<HStack alignItems="center" gap="8px">
|
||||||
|
<Checkbox checked={selectedIds.has(item.id)} onChange={() => handleSelect(item)} />
|
||||||
|
<Box>
|
||||||
|
{item.emoji} {item.name}
|
||||||
|
</Box>
|
||||||
|
</HStack>
|
||||||
|
</SelectableAgentItem>
|
||||||
|
))}
|
||||||
|
</AgentList>
|
||||||
)}
|
)}
|
||||||
</DraggableList>
|
</>
|
||||||
)}
|
)}
|
||||||
{presets.length === 0 && <Empty description="" />}
|
{presets.length === 0 && <Empty description="" />}
|
||||||
</Container>
|
</Container>
|
||||||
@ -65,6 +184,21 @@ const PopupContainer: React.FC = () => {
|
|||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
height: 50vh;
|
height: 50vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ActionBar = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 8px 12px;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const AgentList = styled.div`
|
||||||
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
@ -90,6 +224,23 @@ const AgentItem = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const SelectableAgentItem = styled.div<{ $selected: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
user-select: none;
|
||||||
|
background-color: ${(props) => (props.$selected ? 'var(--color-primary-mute)' : 'var(--color-background-soft)')};
|
||||||
|
margin-bottom: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
&:hover {
|
||||||
|
background-color: ${(props) => (props.$selected ? 'var(--color-primary-mute)' : 'var(--color-background-mute)')};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default class ManageAssistantPresetsPopup {
|
export default class ManageAssistantPresetsPopup {
|
||||||
static topviewId = 0
|
static topviewId = 0
|
||||||
static hide() {
|
static hide() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user