diff --git a/web/src/hooks/flow-hooks.ts b/web/src/hooks/flow-hooks.ts index 55abc2941..5acfd0649 100644 --- a/web/src/hooks/flow-hooks.ts +++ b/web/src/hooks/flow-hooks.ts @@ -1,79 +1,14 @@ -import { ResponseType } from '@/interfaces/database/base'; -import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; +import { DSL, IFlow } from '@/interfaces/database/flow'; import { IDebugSingleRequestBody } from '@/interfaces/request/flow'; import i18n from '@/locales/config'; import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks'; -import { BeginId } from '@/pages/flow/constant'; import flowService from '@/services/flow-service'; import { buildMessageListWithUuid } from '@/utils/chat'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { message } from 'antd'; import { set } from 'lodash'; import get from 'lodash/get'; -import { useTranslation } from 'react-i18next'; import { useParams } from 'umi'; -import { v4 as uuid } from 'uuid'; - -export const EmptyDsl = { - graph: { - nodes: [ - { - id: BeginId, - type: 'beginNode', - position: { - x: 50, - y: 200, - }, - data: { - label: 'Begin', - name: 'begin', - }, - sourcePosition: 'left', - targetPosition: 'right', - }, - ], - edges: [], - }, - components: { - begin: { - obj: { - component_name: 'Begin', - params: {}, - }, - downstream: ['Answer:China'], // other edge target is downstream, edge source is current node id - upstream: [], // edge source is upstream, edge target is current node id - }, - }, - messages: [], - reference: [], - history: [], - path: [], - answer: [], -}; - -export const useFetchFlowTemplates = (): ResponseType => { - const { t } = useTranslation(); - - const { data } = useQuery({ - queryKey: ['fetchFlowTemplates'], - initialData: [], - queryFn: async () => { - const { data } = await flowService.listTemplates(); - if (Array.isArray(data?.data)) { - data.data.unshift({ - id: uuid(), - title: t('flow.blank'), - description: t('flow.createFromNothing'), - dsl: EmptyDsl, - }); - } - - return data; - }, - }); - - return data; -}; export const useFetchFlowList = (): { data: IFlow[]; loading: boolean } => { const { data, isFetching: loading } = useQuery({ diff --git a/web/src/pages/flow/canvas/context-menu/index.less b/web/src/pages/flow/canvas/context-menu/index.less deleted file mode 100644 index 5594aa912..000000000 --- a/web/src/pages/flow/canvas/context-menu/index.less +++ /dev/null @@ -1,18 +0,0 @@ -.contextMenu { - background: rgba(255, 255, 255, 0.1); - border-style: solid; - box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%); - position: absolute; - z-index: 10; - button { - border: none; - display: block; - padding: 0.5em; - text-align: left; - width: 100%; - } - - button:hover { - background: rgba(255, 255, 255, 0.1); - } -} diff --git a/web/src/pages/flow/canvas/context-menu/index.tsx b/web/src/pages/flow/canvas/context-menu/index.tsx deleted file mode 100644 index 6cb306af9..000000000 --- a/web/src/pages/flow/canvas/context-menu/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { NodeMouseHandler, useReactFlow } from '@xyflow/react'; -import { useCallback, useRef, useState } from 'react'; - -import styles from './index.less'; - -export interface INodeContextMenu { - id: string; - top: number; - left: number; - right?: number; - bottom?: number; - [key: string]: unknown; -} - -export function NodeContextMenu({ - id, - top, - left, - right, - bottom, - ...props -}: INodeContextMenu) { - const { getNode, setNodes, addNodes, setEdges } = useReactFlow(); - - const duplicateNode = useCallback(() => { - const node = getNode(id); - const position = { - x: node?.position?.x || 0 + 50, - y: node?.position?.y || 0 + 50, - }; - - addNodes({ - ...(node || {}), - data: node?.data, - selected: false, - dragging: false, - id: `${node?.id}-copy`, - position, - }); - }, [id, getNode, addNodes]); - - const deleteNode = useCallback(() => { - setNodes((nodes) => nodes.filter((node) => node.id !== id)); - setEdges((edges) => edges.filter((edge) => edge.source !== id)); - }, [id, setNodes, setEdges]); - - return ( -
-

- node: {id} -

- - -
- ); -} - -/* @deprecated - */ -export const useHandleNodeContextMenu = (sideWidth: number) => { - const [menu, setMenu] = useState({} as INodeContextMenu); - const ref = useRef(null); - - const onNodeContextMenu: NodeMouseHandler = useCallback( - (event, node) => { - // Prevent native context menu from showing - event.preventDefault(); - - // Calculate position of the context menu. We want to make sure it - // doesn't get positioned off-screen. - const pane = ref.current?.getBoundingClientRect(); - // setMenu({ - // id: node.id, - // top: event.clientY < pane.height - 200 ? event.clientY : 0, - // left: event.clientX < pane.width - 200 ? event.clientX : 0, - // right: event.clientX >= pane.width - 200 ? pane.width - event.clientX : 0, - // bottom: - // event.clientY >= pane.height - 200 ? pane.height - event.clientY : 0, - // }); - - setMenu({ - id: node.id, - top: event.clientY - 144, - left: event.clientX - sideWidth, - // top: event.clientY < pane.height - 200 ? event.clientY - 72 : 0, - // left: event.clientX < pane.width - 200 ? event.clientX : 0, - }); - }, - [sideWidth], - ); - - // Close the context menu if it's open whenever the window is clicked. - const onPaneClick = useCallback( - () => setMenu({} as INodeContextMenu), - [setMenu], - ); - - return { onNodeContextMenu, menu, onPaneClick, ref }; -}; diff --git a/web/src/pages/flow/canvas/edge/index.less b/web/src/pages/flow/canvas/edge/index.less deleted file mode 100644 index 281b67251..000000000 --- a/web/src/pages/flow/canvas/edge/index.less +++ /dev/null @@ -1,31 +0,0 @@ -.edgeButton { - width: 14px; - height: 14px; - background: #eee; - border: 1px solid #fff; - padding: 0; - cursor: pointer; - border-radius: 50%; - font-size: 10px; - line-height: 1; -} - -.edgeButton:hover { - box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08); -} - -.edgeButtonDark { - width: 14px; - height: 14px; - background: #0e0c0c; - border: 1px solid #fff; - padding: 0; - cursor: pointer; - border-radius: 50%; - font-size: 10px; - line-height: 1; -} - -.edgeButtonDark:hover { - box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08); -} diff --git a/web/src/pages/flow/canvas/edge/index.tsx b/web/src/pages/flow/canvas/edge/index.tsx deleted file mode 100644 index 52f939b8d..000000000 --- a/web/src/pages/flow/canvas/edge/index.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { - BaseEdge, - EdgeLabelRenderer, - EdgeProps, - getBezierPath, -} from '@xyflow/react'; -import useGraphStore from '../../store'; - -import { useTheme } from '@/components/theme-provider'; -import { useFetchFlow } from '@/hooks/flow-hooks'; -import { useMemo } from 'react'; -import styles from './index.less'; - -export function ButtonEdge({ - id, - sourceX, - sourceY, - targetX, - targetY, - sourcePosition, - targetPosition, - source, - target, - style = {}, - markerEnd, - selected, -}: EdgeProps) { - const deleteEdgeById = useGraphStore((state) => state.deleteEdgeById); - const [edgePath, labelX, labelY] = getBezierPath({ - sourceX, - sourceY, - sourcePosition, - targetX, - targetY, - targetPosition, - }); - const { theme } = useTheme(); - const selectedStyle = useMemo(() => { - return selected ? { strokeWidth: 2, stroke: '#1677ff' } : {}; - }, [selected]); - - const onEdgeClick = () => { - deleteEdgeById(id); - }; - - // highlight the nodes that the workflow passes through - const { data: flowDetail } = useFetchFlow(); - - const graphPath = useMemo(() => { - // TODO: this will be called multiple times - const path = flowDetail?.dsl?.path ?? []; - // The second to last - const previousGraphPath: string[] = path.at(-2) ?? []; - let graphPath: string[] = path.at(-1) ?? []; - // The last of the second to last article - const previousLatestElement = previousGraphPath.at(-1); - if (previousGraphPath.length > 0 && previousLatestElement) { - graphPath = [previousLatestElement, ...graphPath]; - } - return graphPath; - }, [flowDetail.dsl?.path]); - - const highlightStyle = useMemo(() => { - const idx = graphPath.findIndex((x) => x === source); - if (idx !== -1) { - // The set of elements following source - const slicedGraphPath = graphPath.slice(idx + 1); - if (slicedGraphPath.some((x) => x === target)) { - return { strokeWidth: 2, stroke: 'red' }; - } - } - return {}; - }, [source, target, graphPath]); - - return ( - <> - - -
- -
-
- - ); -} diff --git a/web/src/pages/flow/canvas/index.less b/web/src/pages/flow/canvas/index.less deleted file mode 100644 index d824d88f1..000000000 --- a/web/src/pages/flow/canvas/index.less +++ /dev/null @@ -1,10 +0,0 @@ -.canvasWrapper { - position: relative; - height: 100%; - :global(.react-flow__node-group) { - .commonNode(); - padding: 0; - border: 0; - background-color: transparent; - } -} diff --git a/web/src/pages/flow/canvas/index.tsx b/web/src/pages/flow/canvas/index.tsx deleted file mode 100644 index e9ce10ac7..000000000 --- a/web/src/pages/flow/canvas/index.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/components/ui/tooltip'; -import { - Background, - ConnectionMode, - ControlButton, - Controls, - NodeTypes, - ReactFlow, -} from '@xyflow/react'; -import '@xyflow/react/dist/style.css'; -import { Book, FolderInput, FolderOutput } from 'lucide-react'; -import ChatDrawer from '../chat/drawer'; -import FormDrawer from '../flow-drawer'; -import { - useHandleDrop, - useSelectCanvasData, - useValidateConnection, - useWatchNodeFormDataChange, -} from '../hooks'; -import { useBeforeDelete } from '../hooks/use-before-delete'; -import { useHandleExportOrImportJsonFile } from '../hooks/use-export-json'; -import { useOpenDocument } from '../hooks/use-open-document'; -import { useShowDrawer } from '../hooks/use-show-drawer'; -import JsonUploadModal from '../json-upload-modal'; -import RunDrawer from '../run-drawer'; -import { ButtonEdge } from './edge'; -import styles from './index.less'; -import { RagNode } from './node'; -import { BeginNode } from './node/begin-node'; -import { CategorizeNode } from './node/categorize-node'; -import { EmailNode } from './node/email-node'; -import { GenerateNode } from './node/generate-node'; -import { InvokeNode } from './node/invoke-node'; -import { IterationNode, IterationStartNode } from './node/iteration-node'; -import { KeywordNode } from './node/keyword-node'; -import { LogicNode } from './node/logic-node'; -import { MessageNode } from './node/message-node'; -import NoteNode from './node/note-node'; -import { RelevantNode } from './node/relevant-node'; -import { RetrievalNode } from './node/retrieval-node'; -import { RewriteNode } from './node/rewrite-node'; -import { SwitchNode } from './node/switch-node'; -import { TemplateNode } from './node/template-node'; - -export const nodeTypes: NodeTypes = { - ragNode: RagNode, - categorizeNode: CategorizeNode, - beginNode: BeginNode, - relevantNode: RelevantNode, - logicNode: LogicNode, - noteNode: NoteNode, - switchNode: SwitchNode, - generateNode: GenerateNode, - retrievalNode: RetrievalNode, - messageNode: MessageNode, - rewriteNode: RewriteNode, - keywordNode: KeywordNode, - invokeNode: InvokeNode, - templateNode: TemplateNode, - emailNode: EmailNode, - group: IterationNode, - iterationStartNode: IterationStartNode, -}; - -export const edgeTypes = { - buttonEdge: ButtonEdge, -}; - -interface IProps { - drawerVisible: boolean; - hideDrawer(): void; -} - -function FlowCanvas({ drawerVisible, hideDrawer }: IProps) { - const { - nodes, - edges, - onConnect, - onEdgesChange, - onNodesChange, - onSelectionChange, - } = useSelectCanvasData(); - const isValidConnection = useValidateConnection(); - - const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop(); - - const { - handleExportJson, - handleImportJson, - fileUploadVisible, - onFileUploadOk, - hideFileUploadModal, - } = useHandleExportOrImportJsonFile(); - - const openDocument = useOpenDocument(); - - const { - onNodeClick, - onPaneClick, - clickedNode, - formDrawerVisible, - hideFormDrawer, - singleDebugDrawerVisible, - hideSingleDebugDrawer, - showSingleDebugDrawer, - chatVisible, - runVisible, - hideRunOrChatDrawer, - showChatModal, - } = useShowDrawer({ - drawerVisible, - hideDrawer, - }); - - const { handleBeforeDelete } = useBeforeDelete(); - - useWatchNodeFormDataChange(); - - return ( -
- - - - - - - - - - - - - - Import - - - - - - - - Export - - - - - - - - Document - - - - - {formDrawerVisible && ( - - )} - {chatVisible && ( - - )} - - {runVisible && ( - - )} - {fileUploadVisible && ( - - )} -
- ); -} - -export default FlowCanvas; diff --git a/web/src/pages/flow/canvas/node/begin-node.tsx b/web/src/pages/flow/canvas/node/begin-node.tsx deleted file mode 100644 index 83e36652b..000000000 --- a/web/src/pages/flow/canvas/node/begin-node.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IBeginNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import get from 'lodash/get'; -import { useTranslation } from 'react-i18next'; -import { - BeginQueryType, - BeginQueryTypeIconMap, - Operator, - operatorMap, -} from '../../constant'; -import { BeginQuery } from '../../interface'; -import OperatorIcon from '../../operator-icon'; -import { RightHandleStyle } from './handle-icon'; -import styles from './index.less'; - -// TODO: do not allow other nodes to connect to this node -export function BeginNode({ selected, data }: NodeProps) { - const { t } = useTranslation(); - const query: BeginQuery[] = get(data, 'form.query', []); - const { theme } = useTheme(); - return ( -
- - - - -
- {t(`flow.begin`)} -
-
- - {query.map((x, idx) => { - const Icon = BeginQueryTypeIconMap[x.type as BeginQueryType]; - return ( - - - - {x.name} - {x.optional ? 'Yes' : 'No'} - - ); - })} - -
- ); -} diff --git a/web/src/pages/flow/canvas/node/card.tsx b/web/src/pages/flow/canvas/node/card.tsx deleted file mode 100644 index 042ca45e0..000000000 --- a/web/src/pages/flow/canvas/node/card.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; - -export function CardWithForm() { - return ( - - - Create project - Deploy your new project in one-click. - - -
-
-
- - -
-
- - -
-
-
-
- - - - -
- ); -} diff --git a/web/src/pages/flow/canvas/node/categorize-handle.tsx b/web/src/pages/flow/canvas/node/categorize-handle.tsx deleted file mode 100644 index ce1fc3624..000000000 --- a/web/src/pages/flow/canvas/node/categorize-handle.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Handle, Position } from '@xyflow/react'; - -import React from 'react'; -import styles from './index.less'; - -const DEFAULT_HANDLE_STYLE = { - width: 6, - height: 6, - bottom: -5, - fontSize: 8, -}; - -interface IProps extends React.PropsWithChildren { - top: number; - right: number; - id: string; - idx?: number; -} - -const CategorizeHandle = ({ top, right, id, children }: IProps) => { - return ( - - {children || id} - - ); -}; - -export default CategorizeHandle; diff --git a/web/src/pages/flow/canvas/node/categorize-node.tsx b/web/src/pages/flow/canvas/node/categorize-node.tsx deleted file mode 100644 index 18c3cdff0..000000000 --- a/web/src/pages/flow/canvas/node/categorize-node.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import LLMLabel from '@/components/llm-select/llm-label'; -import { useTheme } from '@/components/theme-provider'; -import { ICategorizeNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { RightHandleStyle } from './handle-icon'; -import { useBuildCategorizeHandlePositions } from './hooks'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function CategorizeNode({ - id, - data, - selected, -}: NodeProps) { - const { positions } = useBuildCategorizeHandlePositions({ data, id }); - const { theme } = useTheme(); - return ( -
- - - - - -
- -
- {positions.map((position, idx) => { - return ( -
-
{position.text}
- -
- ); - })} -
-
- ); -} diff --git a/web/src/pages/flow/canvas/node/dropdown.tsx b/web/src/pages/flow/canvas/node/dropdown.tsx deleted file mode 100644 index dd5263abc..000000000 --- a/web/src/pages/flow/canvas/node/dropdown.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import OperateDropdown from '@/components/operate-dropdown'; -import { CopyOutlined } from '@ant-design/icons'; -import { Flex, MenuProps } from 'antd'; -import { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Operator } from '../../constant'; -import { useDuplicateNode } from '../../hooks'; -import useGraphStore from '../../store'; - -interface IProps { - id: string; - iconFontColor?: string; - label: string; -} - -const NodeDropdown = ({ id, iconFontColor, label }: IProps) => { - const { t } = useTranslation(); - const deleteNodeById = useGraphStore((store) => store.deleteNodeById); - const deleteIterationNodeById = useGraphStore( - (store) => store.deleteIterationNodeById, - ); - - const deleteNode = useCallback(() => { - if (label === Operator.Iteration) { - deleteIterationNodeById(id); - } else { - deleteNodeById(id); - } - }, [label, deleteIterationNodeById, id, deleteNodeById]); - - const duplicateNode = useDuplicateNode(); - - const items: MenuProps['items'] = [ - { - key: '2', - onClick: () => duplicateNode(id, label), - label: ( - - {t('common.copy')} - - - ), - }, - ]; - - return ( - - ); -}; - -export default NodeDropdown; diff --git a/web/src/pages/flow/canvas/node/email-node.tsx b/web/src/pages/flow/canvas/node/email-node.tsx deleted file mode 100644 index ae4af848c..000000000 --- a/web/src/pages/flow/canvas/node/email-node.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { IEmailNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { useState } from 'react'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function EmailNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const [showDetails, setShowDetails] = useState(false); - - return ( -
- - - - - -
setShowDetails(!showDetails)} - > -
- SMTP: - {data.form?.smtp_server} -
-
- Port: - {data.form?.smtp_port} -
-
- From: - {data.form?.email} -
-
{showDetails ? '▼' : '▶'}
-
- - {showDetails && ( -
-
Expected Input JSON:
-
-              {`{
-  "to_email": "...",
-  "cc_email": "...", 
-  "subject": "...",
-  "content": "..."
-}`}
-            
-
- )} -
-
- ); -} diff --git a/web/src/pages/flow/canvas/node/generate-node.tsx b/web/src/pages/flow/canvas/node/generate-node.tsx deleted file mode 100644 index 255eccd99..000000000 --- a/web/src/pages/flow/canvas/node/generate-node.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import LLMLabel from '@/components/llm-select/llm-label'; -import { useTheme } from '@/components/theme-provider'; -import { IGenerateNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function GenerateNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - - - -
- -
-
- ); -} diff --git a/web/src/pages/flow/canvas/node/handle-icon.tsx b/web/src/pages/flow/canvas/node/handle-icon.tsx deleted file mode 100644 index 36c7f3634..000000000 --- a/web/src/pages/flow/canvas/node/handle-icon.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { CSSProperties } from 'react'; - -export const HandleIcon = () => { - return ( - - ); -}; - -export const RightHandleStyle: CSSProperties = { - right: 0, -}; - -export const LeftHandleStyle: CSSProperties = { - left: 0, -}; - -export default HandleIcon; diff --git a/web/src/pages/flow/canvas/node/hooks.ts b/web/src/pages/flow/canvas/node/hooks.ts deleted file mode 100644 index fbea8f166..000000000 --- a/web/src/pages/flow/canvas/node/hooks.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { useUpdateNodeInternals } from '@xyflow/react'; -import get from 'lodash/get'; -import { useEffect, useMemo } from 'react'; -import { SwitchElseTo } from '../../constant'; - -import { - ICategorizeItemResult, - ISwitchCondition, - RAGFlowNodeType, -} from '@/interfaces/database/flow'; -import { generateSwitchHandleText } from '../../utils'; - -export const useBuildCategorizeHandlePositions = ({ - data, - id, -}: { - id: string; - data: RAGFlowNodeType['data']; -}) => { - const updateNodeInternals = useUpdateNodeInternals(); - - const categoryData: ICategorizeItemResult = useMemo(() => { - return get(data, `form.category_description`, {}); - }, [data]); - - const positions = useMemo(() => { - const list: Array<{ - text: string; - top: number; - idx: number; - }> = []; - - Object.keys(categoryData) - .sort((a, b) => categoryData[a].index - categoryData[b].index) - .forEach((x, idx) => { - list.push({ - text: x, - idx, - top: idx === 0 ? 98 + 20 : list[idx - 1].top + 8 + 26, - }); - }); - - return list; - }, [categoryData]); - - useEffect(() => { - updateNodeInternals(id); - }, [id, updateNodeInternals, categoryData]); - - return { positions }; -}; - -export const useBuildSwitchHandlePositions = ({ - data, - id, -}: { - id: string; - data: RAGFlowNodeType['data']; -}) => { - const updateNodeInternals = useUpdateNodeInternals(); - - const conditions: ISwitchCondition[] = useMemo(() => { - return get(data, 'form.conditions', []); - }, [data]); - - const positions = useMemo(() => { - const list: Array<{ - text: string; - top: number; - idx: number; - condition?: ISwitchCondition; - }> = []; - - [...conditions, ''].forEach((x, idx) => { - let top = idx === 0 ? 58 + 20 : list[idx - 1].top + 32; // case number (Case 1) height + flex gap - if (idx - 1 >= 0) { - const previousItems = conditions[idx - 1]?.items ?? []; - if (previousItems.length > 0) { - top += 12; // ConditionBlock padding - top += previousItems.length * 22; // condition variable height - top += (previousItems.length - 1) * 25; // operator height - } - } - - list.push({ - text: - idx < conditions.length - ? generateSwitchHandleText(idx) - : SwitchElseTo, - idx, - top, - condition: typeof x === 'string' ? undefined : x, - }); - }); - - return list; - }, [conditions]); - - useEffect(() => { - updateNodeInternals(id); - }, [id, updateNodeInternals, conditions]); - - return { positions }; -}; diff --git a/web/src/pages/flow/canvas/node/index.less b/web/src/pages/flow/canvas/node/index.less deleted file mode 100644 index 14d7e6077..000000000 --- a/web/src/pages/flow/canvas/node/index.less +++ /dev/null @@ -1,285 +0,0 @@ -.dark { - background: rgb(63, 63, 63) !important; -} -.ragNode { - .commonNode(); - .nodeName { - font-size: 10px; - color: black; - } - label { - display: block; - color: #777; - font-size: 12px; - } - .description { - font-size: 10px; - } - - .categorizeAnchorPointText { - position: absolute; - top: -4px; - left: 8px; - white-space: nowrap; - } -} - -@lightBackgroundColor: rgba(150, 150, 150, 0.1); -@darkBackgroundColor: rgba(150, 150, 150, 0.2); - -.selectedNode { - border: 1.5px solid rgb(59, 118, 244); -} - -.selectedIterationNode { - border-bottom: 1.5px solid rgb(59, 118, 244); - border-left: 1.5px solid rgb(59, 118, 244); - border-right: 1.5px solid rgb(59, 118, 244); -} - -.iterationHeader { - .commonNodeShadow(); -} - -.selectedHeader { - border-top: 1.9px solid rgb(59, 118, 244); - border-left: 1.9px solid rgb(59, 118, 244); - border-right: 1.9px solid rgb(59, 118, 244); -} - -.handle { - display: inline-flex; - align-items: center; - justify-content: center; - width: 12px; - height: 12px; - background: rgb(59, 88, 253); - border: 1px solid white; - z-index: 1; - background-image: url('@/assets/svg/plus.svg'); - background-size: cover; - background-position: center; -} - -.jsonView { - word-wrap: break-word; - overflow: auto; - max-width: 300px; - max-height: 500px; -} - -.logicNode { - .commonNode(); - - .nodeName { - font-size: 10px; - color: black; - } - label { - display: block; - color: #777; - font-size: 12px; - } - - .description { - font-size: 10px; - } - - .categorizeAnchorPointText { - position: absolute; - top: -4px; - left: 8px; - white-space: nowrap; - } - .relevantSourceLabel { - font-size: 10px; - } -} - -.noteNode { - .commonNode(); - min-width: 140px; - width: auto; - height: 100%; - padding: 8px; - border-radius: 10px; - min-height: 128px; - .noteTitle { - background-color: #edfcff; - font-size: 12px; - padding: 6px 6px 4px; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - } - .noteTitleDark { - background-color: #edfcff; - font-size: 12px; - padding: 6px 6px 4px; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - } - .noteForm { - margin-top: 4px; - height: calc(100% - 50px); - } - .noteName { - padding: 0px 4px; - } - .noteTextarea { - resize: none; - border: 0; - border-radius: 0; - height: 100%; - &:focus { - border: none; - box-shadow: none; - } - } -} - -.iterationNode { - .commonNodeShadow(); - border-bottom-left-radius: 10px; - border-bottom-right-radius: 10px; -} - -.nodeText { - padding-inline: 0.4em; - padding-block: 0.2em 0.1em; - background: @lightBackgroundColor; - border-radius: 3px; - min-height: 22px; - .textEllipsis(); -} - -.nodeHeader { - padding-bottom: 12px; -} - -.zeroDivider { - margin: 0 !important; -} - -.conditionBlock { - border-radius: 4px; - padding: 6px; - background: @lightBackgroundColor; -} - -.conditionLine { - border-radius: 4px; - padding: 0 4px; - background: @darkBackgroundColor; - .textEllipsis(); -} - -.conditionKey { - flex: 1; -} - -.conditionOperator { - padding: 0 2px; - text-align: center; -} - -.relevantLabel { - text-align: right; -} - -.knowledgeNodeName { - .textEllipsis(); -} - -.messageNodeContainer { - overflow-y: auto; - max-height: 300px; -} - -.generateParameters { - padding-top: 8px; - label { - flex: 2; - .textEllipsis(); - } - .parameterValue { - flex: 3; - .conditionLine; - } -} - -.emailNodeContainer { - padding: 8px; - font-size: 12px; - - .emailConfig { - background: rgba(0, 0, 0, 0.02); - border-radius: 4px; - padding: 8px; - position: relative; - cursor: pointer; - - &:hover { - background: rgba(0, 0, 0, 0.04); - } - - .configItem { - display: flex; - align-items: center; - margin-bottom: 4px; - - &:last-child { - margin-bottom: 0; - } - - .configLabel { - color: #666; - width: 45px; - flex-shrink: 0; - } - - .configValue { - color: #333; - word-break: break-all; - } - } - - .expandIcon { - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - color: #666; - font-size: 12px; - } - } - - .jsonExample { - background: #f5f5f5; - border-radius: 4px; - padding: 8px; - margin-top: 4px; - animation: slideDown 0.2s ease-out; - - .jsonTitle { - color: #666; - margin-bottom: 4px; - } - - .jsonContent { - margin: 0; - color: #333; - font-family: monospace; - } - } -} - -@keyframes slideDown { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} diff --git a/web/src/pages/flow/canvas/node/index.tsx b/web/src/pages/flow/canvas/node/index.tsx deleted file mode 100644 index 32191f5cc..000000000 --- a/web/src/pages/flow/canvas/node/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IRagNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function RagNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - -
- ); -} diff --git a/web/src/pages/flow/canvas/node/invoke-node.tsx b/web/src/pages/flow/canvas/node/invoke-node.tsx deleted file mode 100644 index 42d109f3d..000000000 --- a/web/src/pages/flow/canvas/node/invoke-node.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IInvokeNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { useTranslation } from 'react-i18next'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function InvokeNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { t } = useTranslation(); - const { theme } = useTheme(); - const url = get(data, 'form.url'); - return ( -
- - - - -
{t('flow.url')}
-
{url}
-
-
- ); -} diff --git a/web/src/pages/flow/canvas/node/iteration-node.tsx b/web/src/pages/flow/canvas/node/iteration-node.tsx deleted file mode 100644 index c15b4fc6c..000000000 --- a/web/src/pages/flow/canvas/node/iteration-node.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { - IIterationNode, - IIterationStartNode, -} from '@/interfaces/database/flow'; -import { cn } from '@/lib/utils'; -import { Handle, NodeProps, NodeResizeControl, Position } from '@xyflow/react'; -import { ListRestart } from 'lucide-react'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -function ResizeIcon() { - return ( - - - - - - - - ); -} - -const controlStyle = { - background: 'transparent', - border: 'none', - cursor: 'nwse-resize', -}; - -export function IterationNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - - return ( -
- - - - - - -
- ); -} - -export function IterationStartNode({ - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - - return ( -
- -
- -
-
- ); -} diff --git a/web/src/pages/flow/canvas/node/keyword-node.tsx b/web/src/pages/flow/canvas/node/keyword-node.tsx deleted file mode 100644 index f607d4317..000000000 --- a/web/src/pages/flow/canvas/node/keyword-node.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import LLMLabel from '@/components/llm-select/llm-label'; -import { useTheme } from '@/components/theme-provider'; -import { IKeywordNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function KeywordNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - - - -
- -
-
- ); -} diff --git a/web/src/pages/flow/canvas/node/logic-node.tsx b/web/src/pages/flow/canvas/node/logic-node.tsx deleted file mode 100644 index 28215617b..000000000 --- a/web/src/pages/flow/canvas/node/logic-node.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { ILogicNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function LogicNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - -
- ); -} diff --git a/web/src/pages/flow/canvas/node/message-node.tsx b/web/src/pages/flow/canvas/node/message-node.tsx deleted file mode 100644 index 5b3a1736e..000000000 --- a/web/src/pages/flow/canvas/node/message-node.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IMessageNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function MessageNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const messages: string[] = get(data, 'form.messages', []); - const { theme } = useTheme(); - return ( -
- - - 0, - })} - > - - - {messages.map((message, idx) => { - return ( -
- {message} -
- ); - })} -
-
- ); -} diff --git a/web/src/pages/flow/canvas/node/node-header.tsx b/web/src/pages/flow/canvas/node/node-header.tsx deleted file mode 100644 index 99a37dc1e..000000000 --- a/web/src/pages/flow/canvas/node/node-header.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useTranslate } from '@/hooks/common-hooks'; -import { Flex } from 'antd'; -import { Play } from 'lucide-react'; -import { Operator, operatorMap } from '../../constant'; -import OperatorIcon from '../../operator-icon'; -import { needsSingleStepDebugging } from '../../utils'; -import NodeDropdown from './dropdown'; -import { NextNodePopover } from './popover'; - -import { RunTooltip } from '../../flow-tooltip'; -interface IProps { - id: string; - label: string; - name: string; - gap?: number; - className?: string; - wrapperClassName?: string; -} - -const ExcludedRunStateOperators = [Operator.Answer]; - -export function RunStatus({ id, name, label }: IProps) { - const { t } = useTranslate('flow'); - return ( -
- {needsSingleStepDebugging(label) && ( - - - // data-play is used to trigger single step debugging - )} - - - {t('operationResults')} - - -
- ); -} - -const NodeHeader = ({ - label, - id, - name, - gap = 4, - className, - wrapperClassName, -}: IProps) => { - return ( -
- {!ExcludedRunStateOperators.includes(label as Operator) && ( - - )} - - - - {name} - - - -
- ); -}; - -export default NodeHeader; diff --git a/web/src/pages/flow/canvas/node/note-node.tsx b/web/src/pages/flow/canvas/node/note-node.tsx deleted file mode 100644 index 1917a8150..000000000 --- a/web/src/pages/flow/canvas/node/note-node.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { NodeProps, NodeResizeControl } from '@xyflow/react'; -import { Flex, Form, Input } from 'antd'; -import classNames from 'classnames'; -import NodeDropdown from './dropdown'; - -import SvgIcon from '@/components/svg-icon'; -import { useTheme } from '@/components/theme-provider'; -import { INoteNode } from '@/interfaces/database/flow'; -import { memo, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { - useHandleFormValuesChange, - useHandleNodeNameChange, -} from '../../hooks'; -import styles from './index.less'; - -const { TextArea } = Input; - -const controlStyle = { - background: 'transparent', - border: 'none', -}; - -function NoteNode({ data, id }: NodeProps) { - const { t } = useTranslation(); - const [form] = Form.useForm(); - const { theme } = useTheme(); - - const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ - id, - data, - }); - const { handleValuesChange } = useHandleFormValuesChange(id); - - useEffect(() => { - form.setFieldsValue(data?.form); - }, [form, data?.form]); - - return ( - <> - - - -
- - - - - -
- -