diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx index cf55c0d68d..953ea52866 100644 --- a/web/app/components/app-sidebar/app-info.tsx +++ b/web/app/components/app-sidebar/app-info.tsx @@ -26,7 +26,6 @@ import { fetchWorkflowDraft } from '@/service/workflow' import ContentDialog from '@/app/components/base/content-dialog' import Button from '@/app/components/base/button' import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view' -import Divider from '../base/divider' import type { Operation } from './app-operations' import AppOperations from './app-operations' import dynamic from 'next/dynamic' @@ -197,7 +196,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx if (!appDetail) return null - const operations = [ + const primaryOperations = [ { id: 'edit', title: t('app.editApp'), @@ -224,7 +223,11 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx icon: , onClick: exportCheck, }, - (appDetail.mode !== 'agent-chat' && (appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow')) ? { + ] + + const secondaryOperations: Operation[] = [ + // Import DSL (conditional) + ...(appDetail.mode !== 'agent-chat' && (appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow')) ? [{ id: 'import', title: t('workflow.common.importDSL'), icon: , @@ -233,18 +236,39 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx onDetailExpand?.(false) setShowImportDSLModal(true) }, - } : undefined, - (appDetail.mode !== 'agent-chat' && (appDetail.mode === 'completion' || appDetail.mode === 'chat')) ? { - id: 'switch', - title: t('app.switch'), - icon: , + }] : [], + // Divider + { + id: 'divider-1', + title: '', + icon: <>, + onClick: () => { /* divider has no action */ }, + type: 'divider' as const, + }, + // Delete operation + { + id: 'delete', + title: t('common.operation.delete'), + icon: , onClick: () => { setOpen(false) onDetailExpand?.(false) - setShowSwitchModal(true) + setShowConfirmDelete(true) }, - } : undefined, - ].filter((op): op is Operation => Boolean(op)) + }, + ] + + // Keep the switch operation separate as it's not part of the main operations + const switchOperation = (appDetail.mode !== 'agent-chat' && (appDetail.mode === 'completion' || appDetail.mode === 'chat')) ? { + id: 'switch', + title: t('app.switch'), + icon: , + onClick: () => { + setOpen(false) + onDetailExpand?.(false) + setShowSwitchModal(true) + }, + } : null return (
@@ -322,7 +346,8 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx {/* operations */}
- -
- -
+ {/* Switch operation (if available) */} + {switchOperation && ( +
+ +
+ )} {showSwitchModal && ( void + id: string + title: string + icon: JSX.Element + onClick: () => void + type?: 'action' | 'divider' + className?: string } -const AppOperations = ({ operations, gap }: { - operations: Operation[] +const AppOperations = ({ primaryOperations, secondaryOperations, gap }: { + primaryOperations: Operation[] + secondaryOperations: Operation[] gap: number }) => { const { t } = useTranslation() - const [visibleOpreations, setVisibleOperations] = useState([]) - const [moreOperations, setMoreOperations] = useState([]) const [showMore, setShowMore] = useState(false) - const navRef = useRef(null) const handleTriggerMore = useCallback(() => { - setShowMore(true) - }, [setShowMore]) + setShowMore(prev => !prev) + }, []) - useEffect(() => { - const moreElement = document.getElementById('more') - const navElement = document.getElementById('nav') - let width = 0 - const containerWidth = navElement?.clientWidth ?? 0 - const moreWidth = moreElement?.clientWidth ?? 0 - - if (containerWidth === 0 || moreWidth === 0) return - - const updatedEntries: Record = operations.reduce((pre, cur) => { - pre[cur.id] = false - return pre - }, {} as Record) - const childrens = Array.from(navRef.current!.children).slice(0, -1) - for (let i = 0; i < childrens.length; i++) { - const child: any = childrens[i] - const id = child.dataset.targetid - if (!id) break - const childWidth = child.clientWidth - - if (width + gap + childWidth + moreWidth <= containerWidth) { - updatedEntries[id] = true - width += gap + childWidth - } - else { - if (i === childrens.length - 1 && width + childWidth <= containerWidth) - updatedEntries[id] = true - else - updatedEntries[id] = false - break - } + const renderSecondaryOperation = (operation: Operation, index: number) => { + if (operation.type === 'divider') { + return ( + + ) } - setVisibleOperations(operations.filter(item => updatedEntries[item.id])) - setMoreOperations(operations.filter(item => !updatedEntries[item.id])) - }, [operations, gap]) + + return ( +
+ {cloneElement(operation.icon, { + className: 'h-4 w-4 text-text-tertiary', + })} + + {operation.title} + +
+ ) + } return ( - <> - {!visibleOpreations.length &&