mirror of
https://github.com/csunny/DB-GPT.git
synced 2026-07-17 10:16:49 +00:00
feat: awel flow layout
This commit is contained in:
@@ -182,6 +182,16 @@ const en = {
|
||||
choice_agent_tip: 'Please choose an agent',
|
||||
no_context_tip: 'Please enter your question',
|
||||
Terminal: 'Terminal',
|
||||
awel_flow: 'AWEL Flow',
|
||||
save: 'Save',
|
||||
add_node: 'Add Node',
|
||||
no_node: 'No Node',
|
||||
connect_warning: 'Nodes cannot be connected',
|
||||
flow_modal_title: 'Save Flow',
|
||||
flow_name: 'Flow Name',
|
||||
flow_name_required: 'Please enter the flow name',
|
||||
save_flow_success: 'Save flow success',
|
||||
delete_flow_confirm: 'Are you sure you want to delete this flow?',
|
||||
} as const;
|
||||
|
||||
export type I18nKeys = keyof typeof en;
|
||||
@@ -370,6 +380,16 @@ const zh: Resources['translation'] = {
|
||||
choice_agent_tip: '请选择代理',
|
||||
no_context_tip: '请输入你的问题',
|
||||
Terminal: '终端',
|
||||
awel_flow: 'AWEL 工作流',
|
||||
save: '保存',
|
||||
add_node: '添加节点',
|
||||
no_node: '没有可编排节点',
|
||||
connect_warning: '节点无法连接',
|
||||
flow_modal_title: '保存工作流',
|
||||
flow_name: '工作流名称',
|
||||
flow_name_required: '请输入工作流名称',
|
||||
save_flow_success: '保存工作流成功',
|
||||
delete_flow_confirm: '确定删除该工作流吗?',
|
||||
} as const;
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { GET, POST } from '.';
|
||||
import { DELETE, GET, POST, PUT } from '.';
|
||||
import { DbListResponse, DbSupportTypeResponse, PostDbParams, ChatFeedBackSchema } from '@/types/db';
|
||||
import { DialogueListResponse, IChatDialogueSchema, NewDialogueParam, SceneResponse, ChatHistoryResponse, FeedBack, IDB } from '@/types/chat';
|
||||
import { IModelData, StartModelParams, BaseModelParams, SupportModel } from '@/types/model';
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
ISyncBatchResponse,
|
||||
} from '@/types/knowledge';
|
||||
import { UpdatePromptParams, IPrompt, PromptParams } from '@/types/prompt';
|
||||
import { IFlow, IFlowNode, IFlowResponse, IFlowUpdateParam } from '@/types/flow';
|
||||
|
||||
/** App */
|
||||
export const postScenes = () => {
|
||||
@@ -249,3 +250,28 @@ export const updatePrompt = (data: UpdatePromptParams) => {
|
||||
export const addPrompt = (data: UpdatePromptParams) => {
|
||||
return POST<UpdatePromptParams, []>('/prompt/add', data);
|
||||
};
|
||||
|
||||
/** AWEL Flow */
|
||||
export const addFlow = (data: IFlowUpdateParam) => {
|
||||
return POST<IFlowUpdateParam, IFlow>('/api/v1/serve/awel/flows', data);
|
||||
};
|
||||
|
||||
export const getFlows = () => {
|
||||
return GET<null, IFlowResponse>('/api/v1/serve/awel/flows');
|
||||
};
|
||||
|
||||
export const getFlowById = (id: string) => {
|
||||
return GET<null, IFlow>(`/api/v1/serve/awel/flows/${id}`);
|
||||
};
|
||||
|
||||
export const updateFlowById = (id: string, data: IFlowUpdateParam) => {
|
||||
return PUT<IFlowUpdateParam, IFlow>(`/api/v1/serve/awel/flows/${id}`, data);
|
||||
};
|
||||
|
||||
export const deleteFlowById = (id: string) => {
|
||||
return DELETE<null, null>(`/api/v1/serve/awel/flows/${id}`);
|
||||
};
|
||||
|
||||
export const getFlowNodes = () => {
|
||||
return GET<null, Array<IFlowNode>>(`/api/v1/serve/awel/nodes`);
|
||||
};
|
||||
|
||||
120
web/components/flow/add-nodes.tsx
Normal file
120
web/components/flow/add-nodes.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { apiInterceptors, getFlowNodes } from '@/client/api';
|
||||
import { IFlowNode } from '@/types/flow';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Avatar, Badge, Button, Collapse, CollapseProps, Divider, Empty, Input, List, Popover } from 'antd';
|
||||
import React, { DragEvent, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const { Search } = Input;
|
||||
|
||||
const AddNodes: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [nodes, setNodes] = useState<Array<IFlowNode>>([]);
|
||||
const [operators, setOperators] = useState<Array<IFlowNode>>([]);
|
||||
const [resources, setResources] = useState<Array<IFlowNode>>([]);
|
||||
|
||||
useEffect(() => {
|
||||
getNodes();
|
||||
}, []);
|
||||
|
||||
async function getNodes() {
|
||||
const [_, data] = await apiInterceptors(getFlowNodes());
|
||||
if (data && data.length > 0) {
|
||||
setNodes(data);
|
||||
groupNodes(data);
|
||||
}
|
||||
}
|
||||
|
||||
function groupNodes(data: IFlowNode[]) {
|
||||
// show operator nodes first, then show resource nodes
|
||||
setOperators((data || []).filter((node) => node.flow_type === 'operator'));
|
||||
setResources((data || []).filter((node) => node.flow_type === 'resource'));
|
||||
}
|
||||
|
||||
const items: CollapseProps['items'] = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'operator',
|
||||
label: 'Operator',
|
||||
children: renderNodes(operators),
|
||||
extra: <Badge showZero count={operators.length || 0} style={{ backgroundColor: operators.length > 0 ? '#52c41a' : '#7f9474' }} />,
|
||||
},
|
||||
{
|
||||
key: 'resource',
|
||||
label: 'Resource',
|
||||
children: renderNodes(resources),
|
||||
extra: <Badge showZero count={resources.length || 0} style={{ backgroundColor: resources.length > 0 ? '#52c41a' : '#7f9474' }} />,
|
||||
},
|
||||
],
|
||||
[operators, resources],
|
||||
);
|
||||
|
||||
function searchNode(val: string) {
|
||||
if (!val) {
|
||||
groupNodes(nodes);
|
||||
} else {
|
||||
const lowerSearchTerm = val.toLowerCase();
|
||||
const searchOperators = operators.filter((node) => node.label.toLowerCase().includes(lowerSearchTerm));
|
||||
const searchResources = resources.filter((node) => node.label.toLowerCase().includes(lowerSearchTerm));
|
||||
setOperators(searchOperators);
|
||||
setResources(searchResources);
|
||||
}
|
||||
}
|
||||
|
||||
function onDragStart(event: DragEvent, node: IFlowNode) {
|
||||
event.dataTransfer.setData('application/reactflow', JSON.stringify(node));
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
}
|
||||
|
||||
function renderNodes(nodes: Array<IFlowNode>) {
|
||||
if (nodes?.length > 0) {
|
||||
return (
|
||||
<List
|
||||
className="overflow-hidden overflow-y-auto"
|
||||
itemLayout="horizontal"
|
||||
dataSource={nodes}
|
||||
renderItem={(node) => (
|
||||
<List.Item
|
||||
className="cursor-move hover:bg-[#F1F5F9] dark:hover:bg-theme-dark p-0 py-2"
|
||||
draggable
|
||||
onDragStart={(event) => onDragStart(event, node)}
|
||||
>
|
||||
<List.Item.Meta
|
||||
className="flex items-center justify-center"
|
||||
avatar={<Avatar src={node.icon || '/icons/node/default_node_icon.svg'} size={'large'} />}
|
||||
title={<p className="line-clamp-1 font-medium">{node.label}</p>}
|
||||
description={<p className="line-clamp-2">{node.description}</p>}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <Empty className="px-2" description={t('no_node')} />;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover
|
||||
placement="bottom"
|
||||
trigger={['click']}
|
||||
content={
|
||||
<div className="w-[320px] overflow-hidden overflow-y-auto scrollbar-default">
|
||||
<p className="my-4 font-bold">{t('add_node')}</p>
|
||||
<Search placeholder="Search node" onSearch={searchNode} />
|
||||
<Divider className="my-2" />
|
||||
<Collapse className="max-h-[538px]" size="small" defaultActiveKey={['operator']} ghost items={items} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
className="flex items-center justify-center rounded-full left-4 top-4"
|
||||
style={{ zIndex: 1050 }}
|
||||
icon={<PlusOutlined />}
|
||||
></Button>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNodes;
|
||||
41
web/components/flow/button-edge.tsx
Normal file
41
web/components/flow/button-edge.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { getBezierPath, EdgeProps, BaseEdge, useReactFlow } from 'reactflow';
|
||||
|
||||
const ButtonEdge: React.FC<EdgeProps> = ({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, style = {}, data, markerEnd }) => {
|
||||
const [edgePath, edgeCenterX, edgeCenterY] = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
});
|
||||
const reactFlow = useReactFlow();
|
||||
|
||||
function onEdgeClick(event: React.MouseEvent, id: string) {
|
||||
event.stopPropagation();
|
||||
reactFlow.setEdges(reactFlow.getEdges().filter((edge) => edge.id !== id));
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<BaseEdge id={id} style={style} path={edgePath} markerEnd={markerEnd} />
|
||||
<foreignObject
|
||||
width={40}
|
||||
height={40}
|
||||
x={edgeCenterX - 40 / 2}
|
||||
y={edgeCenterY - 40 / 2}
|
||||
className="bg-transparent w-10 h-10 relative"
|
||||
requiredExtensions="http://www.w3.org/1999/xhtml"
|
||||
>
|
||||
<button
|
||||
className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-5 h-5 rounded-full bg-stone-400 dark:bg-zinc-700 cursor-pointer text-sm"
|
||||
onClick={(event) => onEdgeClick(event, id)}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</foreignObject>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonEdge;
|
||||
122
web/components/flow/canvas-node.tsx
Normal file
122
web/components/flow/canvas-node.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { IFlowNode, IFlowNodeParameter } from '@/types/flow';
|
||||
import Image from 'next/image';
|
||||
import NodeParamHandler from './node-param-handler';
|
||||
import classNames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import NodeHandler from './node-handler';
|
||||
import { Popover } from 'antd';
|
||||
import { CopyOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
import { useReactFlow } from 'reactflow';
|
||||
|
||||
type CanvasNodeProps = {
|
||||
data: IFlowNode;
|
||||
};
|
||||
|
||||
const ICON_PATH_PREFIX = '/icons/node/';
|
||||
|
||||
function TypeLabel({ label }: { label: string }) {
|
||||
return <div className="w-full h-8 bg-stone-100 dark:bg-zinc-700 px-2 flex items-center justify-center">{label}</div>;
|
||||
}
|
||||
|
||||
const CanvasNode: React.FC<CanvasNodeProps> = ({ data }) => {
|
||||
const node = data;
|
||||
const { inputs, outputs, flow_type: flowType } = node;
|
||||
const parameters = orderParams(node.parameters || []);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const reactFlow = useReactFlow();
|
||||
|
||||
function orderParams(params: Array<IFlowNodeParameter>) {
|
||||
// we show resource params first, and build-int params last
|
||||
const resourceParams = params.filter((param) => param.category === 'resource');
|
||||
const commonParams = params.filter((param) => param.category === 'common');
|
||||
return [...resourceParams, ...commonParams];
|
||||
}
|
||||
|
||||
function onHover() {
|
||||
setIsHovered(true);
|
||||
}
|
||||
|
||||
function onLeave() {
|
||||
setIsHovered(false);
|
||||
}
|
||||
|
||||
function copyNode() {}
|
||||
|
||||
function deleteNode() {
|
||||
const nodes = reactFlow.getNodes();
|
||||
console.log('delete node', nodes);
|
||||
reactFlow.setNodes((nodes) => {
|
||||
const list = nodes.filter((item) => item.id !== node.id);
|
||||
return list;
|
||||
});
|
||||
reactFlow.setEdges((edges) => edges.filter((edge) => edge.source !== node.id && edge.target !== node.id));
|
||||
}
|
||||
|
||||
function renderOutput(data: IFlowNode) {
|
||||
if (flowType === 'operator' && outputs?.length > 0) {
|
||||
return (
|
||||
<>
|
||||
<TypeLabel label="Outputs" />
|
||||
{(outputs || []).map((output, index) => (
|
||||
<NodeHandler key={`${data.id}_input_${index}`} node={data} data={output} type="source" label="outputs" index={index} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
} else if (flowType === 'resource') {
|
||||
// resource nodes show output default
|
||||
return (
|
||||
<>
|
||||
<TypeLabel label="Outputs" />
|
||||
<NodeHandler key={`${data.id}_input_0`} node={data} data={data} type="source" label="outputs" index={0} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover
|
||||
placement="rightTop"
|
||||
content={
|
||||
<div>
|
||||
<CopyOutlined className="block text-lg cursor-pointer" onClick={copyNode} />
|
||||
<DeleteOutlined className="block text-lg cursor-pointer" onClick={deleteNode} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={classNames('w-72 h-auto rounded-xl shadow-md p-0 border bg-white dark:bg-zinc-800 cursor-grab', {
|
||||
'border-blue-500': node.selected || isHovered,
|
||||
'border-stone-400 dark:border-white': !node.selected && !isHovered,
|
||||
'border-dashed': flowType !== 'operator',
|
||||
})}
|
||||
onMouseEnter={onHover}
|
||||
onMouseLeave={onLeave}
|
||||
>
|
||||
{/* icon and label */}
|
||||
<div className="flex flex-row items-center p-2">
|
||||
<Image src={node.icon || `${ICON_PATH_PREFIX}${node.name || 'default_node_icon'}.svg`} width={24} height={24} alt="" />
|
||||
<p className="ml-2 text-lg font-bold text-ellipsis overflow-hidden whitespace-nowrap">{node.label}</p>
|
||||
</div>
|
||||
{inputs && inputs.length > 0 && (
|
||||
<>
|
||||
<TypeLabel label="Inputs" />
|
||||
{(inputs || []).map((input, index) => (
|
||||
<NodeHandler key={`${node.id}_input_${index}`} node={node} data={input} type="target" label="inputs" index={index} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{parameters && parameters.length > 0 && (
|
||||
<>
|
||||
<TypeLabel label="Parameters" />
|
||||
{(parameters || []).map((parameter, index) => (
|
||||
<NodeParamHandler key={`${node.id}_param_${index}`} node={node} data={parameter} label="parameters" index={index} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{renderOutput(node)}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default CanvasNode;
|
||||
57
web/components/flow/flow-card.tsx
Normal file
57
web/components/flow/flow-card.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { apiInterceptors, deleteFlowById } from '@/client/api';
|
||||
import { IFlow } from '@/types/flow';
|
||||
import { DeleteFilled, WarningOutlined } from '@ant-design/icons';
|
||||
import { Modal } from 'antd';
|
||||
import Link from 'next/link';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface FlowCardProps {
|
||||
flow: IFlow;
|
||||
deleteCallback: (uid: string) => void;
|
||||
}
|
||||
|
||||
const FlowCard: React.FC<FlowCardProps> = ({ flow, deleteCallback }) => {
|
||||
const { t } = useTranslation();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
|
||||
async function deleteFlow() {
|
||||
const [res] = await apiInterceptors(deleteFlowById(flow.uid));
|
||||
deleteCallback && deleteCallback(flow.uid);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link href={`/flow/canvas?id=${flow.uid}`}>
|
||||
<div className="relative flex flex-col p-4 w-72 h-32 rounded justify-between cursor-pointer text-black bg-white shadow-[0_8px_16px_-10px_rgba(100,100,100,.08)] hover:shadow-[0_14px_20px_-10px_rgba(100,100,100,.15)] dark:bg-[#232734] dark:text-white dark:hover:border-white transition-[transfrom_shadow] duration-300 hover:-translate-y-1">
|
||||
<div className="flex items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="text-lg font-semibold">{flow.name}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<DeleteFilled
|
||||
className="absolute top-4 right-4 text-[#ff1b2e] !text-lg"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
modal.confirm({
|
||||
title: t('Tips'),
|
||||
icon: <WarningOutlined />,
|
||||
content: t('delete_flow_confirm'),
|
||||
okText: 'Yes',
|
||||
okType: 'danger',
|
||||
cancelText: 'No',
|
||||
async onOk() {
|
||||
deleteFlow();
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
{contextHolder}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FlowCard;
|
||||
67
web/components/flow/node-handler.tsx
Normal file
67
web/components/flow/node-handler.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { Tooltip, Typography, message } from 'antd';
|
||||
import React from 'react';
|
||||
import { Connection, Handle, Position, useReactFlow } from 'reactflow';
|
||||
import RequiredIcon from './required-icon';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { IFlowNode, IFlowNodeInput, IFlowNodeOutput, IFlowNodeParameter } from '@/types/flow';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface NodeHandlerProps {
|
||||
node: IFlowNode;
|
||||
data: IFlowNodeInput | IFlowNodeParameter | IFlowNodeOutput;
|
||||
type: 'source' | 'target';
|
||||
label: 'inputs' | 'outputs' | 'parameters';
|
||||
index: number;
|
||||
}
|
||||
|
||||
// render react flow handle item
|
||||
const NodeHandler: React.FC<NodeHandlerProps> = ({ node, data, type, label, index }) => {
|
||||
const { t } = useTranslation();
|
||||
const reactflow = useReactFlow();
|
||||
|
||||
function isValidConnection(connection: Connection) {
|
||||
const { sourceHandle, targetHandle, source, target } = connection;
|
||||
const sourceNode = reactflow.getNode(source!);
|
||||
const targetNode = reactflow.getNode(target!);
|
||||
const { flow_type: sourceFlowType } = sourceNode?.data;
|
||||
const { flow_type: targetFlowType } = targetNode?.data;
|
||||
const sourceLabel = sourceHandle?.split('|')[1];
|
||||
const targetLabel = targetHandle?.split('|')[1];
|
||||
const sourceIndex = sourceHandle?.split('|')[2];
|
||||
const targetIndex = targetHandle?.split('|')[2];
|
||||
const targetTypeCls = targetNode?.data[targetLabel!][targetIndex!].type_cls;
|
||||
if (sourceFlowType === targetFlowType && sourceFlowType === 'operator') {
|
||||
// operator to operator, only type_cls matched can be connected
|
||||
const sourceTypeCls = sourceNode?.data[sourceLabel!][sourceIndex!].type_cls;
|
||||
return sourceTypeCls === targetTypeCls;
|
||||
} else if (sourceFlowType === 'resource' && targetFlowType === 'operator') {
|
||||
// resource to operator, check operator type_cls and resource parent_cls
|
||||
const sourceParentCls = sourceNode?.data.parent_cls;
|
||||
return sourceParentCls.includes(targetTypeCls);
|
||||
}
|
||||
message.warning(t('connect_warning'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center">
|
||||
<Handle
|
||||
className="w-2 h-2"
|
||||
type={type}
|
||||
position={type === 'source' ? Position.Right : Position.Left}
|
||||
id={`${node.id}|${label}|${index}`}
|
||||
isValidConnection={(connection) => isValidConnection(connection)}
|
||||
/>
|
||||
<Typography className="p-2">
|
||||
{data.label}:<RequiredIcon optional={data.optional} />
|
||||
{data.description && (
|
||||
<Tooltip title={data.description}>
|
||||
<InfoCircleOutlined className="ml-2" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NodeHandler;
|
||||
74
web/components/flow/node-param-handler.tsx
Normal file
74
web/components/flow/node-param-handler.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { IFlowNode, IFlowNodeParameter } from '@/types/flow';
|
||||
import { Checkbox, Input, InputNumber } from 'antd';
|
||||
import React from 'react';
|
||||
import RequiredIcon from './required-icon';
|
||||
import NodeHandler from './node-handler';
|
||||
|
||||
interface NodeParamHandlerProps {
|
||||
node: IFlowNode;
|
||||
data: IFlowNodeParameter;
|
||||
label: 'inputs' | 'outputs' | 'parameters';
|
||||
index: number; // index of array
|
||||
}
|
||||
|
||||
// render node parameters item
|
||||
const NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label, index }) => {
|
||||
function handleChange(value: any) {
|
||||
data.value = value;
|
||||
}
|
||||
|
||||
if (data.category === 'resource') {
|
||||
return <NodeHandler node={node} data={data} type="target" label={label} index={index} />;
|
||||
} else if (data.category === 'common') {
|
||||
const defaultValue = data.default || data.value;
|
||||
switch (data.type_name) {
|
||||
case 'int':
|
||||
return (
|
||||
<div className="p-2 text-sm">
|
||||
<p>
|
||||
{data.label}:<RequiredIcon optional={data.optional} />
|
||||
</p>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => {
|
||||
handleChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case 'str':
|
||||
return (
|
||||
<div className="p-2 text-sm">
|
||||
<p>
|
||||
{data.label}:<RequiredIcon optional={data.optional} />
|
||||
</p>
|
||||
<Input
|
||||
className="w-full"
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => {
|
||||
handleChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case 'bool':
|
||||
return (
|
||||
<div className="p-2 text-sm">
|
||||
<p>
|
||||
{data.label}:<RequiredIcon optional={data.optional} />
|
||||
<Checkbox
|
||||
className="ml-2"
|
||||
defaultChecked={defaultValue}
|
||||
onChange={(e) => {
|
||||
handleChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default NodeParamHandler;
|
||||
10
web/components/flow/required-icon.tsx
Normal file
10
web/components/flow/required-icon.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
const RequiredIcon: React.FC<{ optional?: boolean | undefined }> = ({ optional }) => {
|
||||
if (optional) {
|
||||
return null;
|
||||
}
|
||||
return <span className="text-red-600 align-middle inline-block"> *</span>;
|
||||
};
|
||||
|
||||
export default RequiredIcon;
|
||||
@@ -16,6 +16,7 @@ import Icon, {
|
||||
MenuOutlined,
|
||||
SettingOutlined,
|
||||
BuildOutlined,
|
||||
ForkOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Modal, message, Tooltip, Dropdown } from 'antd';
|
||||
import { ItemType } from 'antd/es/menu/hooks/useItems';
|
||||
@@ -42,7 +43,7 @@ type RouteItem = {
|
||||
};
|
||||
|
||||
function menuItemStyle(active?: boolean) {
|
||||
return `flex items-center h-12 hover:bg-[#F1F5F9] dark:hover:bg-theme-dark text-base w-full transition-colors whitespace-nowrap px-4 ${
|
||||
return `flex items-center h-10 hover:bg-[#F1F5F9] dark:hover:bg-theme-dark text-base w-full transition-colors whitespace-nowrap px-4 ${
|
||||
active ? 'bg-[#F1F5F9] dark:bg-theme-dark' : ''
|
||||
}`;
|
||||
}
|
||||
@@ -64,10 +65,10 @@ function SideBar() {
|
||||
const routes = useMemo(() => {
|
||||
const items: RouteItem[] = [
|
||||
{
|
||||
key: 'prompt',
|
||||
name: t('Prompt'),
|
||||
icon: <MessageOutlined />,
|
||||
path: '/prompt',
|
||||
key: 'flow',
|
||||
name: t('awel_flow'),
|
||||
icon: <ForkOutlined />,
|
||||
path: '/flow',
|
||||
},
|
||||
{
|
||||
key: 'database',
|
||||
@@ -93,6 +94,12 @@ function SideBar() {
|
||||
path: '/agent',
|
||||
icon: <BuildOutlined />,
|
||||
},
|
||||
{
|
||||
key: 'prompt',
|
||||
name: t('Prompt'),
|
||||
icon: <MessageOutlined />,
|
||||
path: '/prompt',
|
||||
},
|
||||
];
|
||||
return items;
|
||||
}, [i18n.language]);
|
||||
@@ -318,7 +325,7 @@ function SideBar() {
|
||||
</div>
|
||||
{/* Settings */}
|
||||
<div className="pt-4">
|
||||
<div className="max-h-52 overflow-y-auto">
|
||||
<div className="max-h-52 overflow-y-auto scrollbar-default">
|
||||
{routes.map((item) => (
|
||||
<Link key={item.key} href={item.path} className={`${menuItemStyle(pathname === item.path)} overflow-hidden`}>
|
||||
<>
|
||||
|
||||
437
web/package-lock.json
generated
437
web/package-lock.json
generated
@@ -34,6 +34,7 @@
|
||||
"react-i18next": "^13.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"reactflow": "^11.10.2",
|
||||
"rehype-raw": "6.1.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sql-formatter": "^12.2.4",
|
||||
@@ -2427,6 +2428,102 @@
|
||||
"react-dom": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/background": {
|
||||
"version": "11.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@reactflow/background/-/background-11.3.7.tgz",
|
||||
"integrity": "sha512-PhkvoFtO/NXJgFtBvfbPwdR/6/dl25egQlFhKWS3T4aYa7rh80dvf6dF3t6+JXJS4q5ToYJizD2/n8/qylo1yQ==",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.10.2",
|
||||
"classcat": "^5.0.3",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/controls": {
|
||||
"version": "11.2.7",
|
||||
"resolved": "https://registry.npmmirror.com/@reactflow/controls/-/controls-11.2.7.tgz",
|
||||
"integrity": "sha512-mugzVALH/SuKlVKk+JCRm1OXQ+p8e9+k8PCTIaqL+nBl+lPF8KA4uMm8ApsOvhuSAb2A80ezewpyvYHr0qSYVA==",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.10.2",
|
||||
"classcat": "^5.0.3",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/core": {
|
||||
"version": "11.10.2",
|
||||
"resolved": "https://registry.npmmirror.com/@reactflow/core/-/core-11.10.2.tgz",
|
||||
"integrity": "sha512-/cbTxtFpfkIGReSVkcnQhS4Jx4VFY2AhPlJ5n0sbPtnR7OWowF9zodh5Yyzr4j1NOUoBgJ9h+UqGEwwY2dbAlw==",
|
||||
"dependencies": {
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/d3-drag": "^3.0.1",
|
||||
"@types/d3-selection": "^3.0.3",
|
||||
"@types/d3-zoom": "^3.0.1",
|
||||
"classcat": "^5.0.3",
|
||||
"d3-drag": "^3.0.0",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/minimap": {
|
||||
"version": "11.7.7",
|
||||
"resolved": "https://registry.npmmirror.com/@reactflow/minimap/-/minimap-11.7.7.tgz",
|
||||
"integrity": "sha512-Pwqw31tJ663cJur6ypqyJU33nPckvTepmz96erdQZoHsfOyLmFj4nXT7afC30DJ48lp0nfNsw+028mlf7f/h4g==",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.10.2",
|
||||
"@types/d3-selection": "^3.0.3",
|
||||
"@types/d3-zoom": "^3.0.1",
|
||||
"classcat": "^5.0.3",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/node-resizer": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmmirror.com/@reactflow/node-resizer/-/node-resizer-2.2.7.tgz",
|
||||
"integrity": "sha512-BMBstmWNiklHnnAjHu8irkiPQ8/k8nnjzqlTql4acbVhD6Tsdxx/t/saOkELmfQODqGZNiPw9+pHcAHgtE6oNQ==",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.10.2",
|
||||
"classcat": "^5.0.4",
|
||||
"d3-drag": "^3.0.0",
|
||||
"d3-selection": "^3.0.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/node-toolbar": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/@reactflow/node-toolbar/-/node-toolbar-1.3.7.tgz",
|
||||
"integrity": "sha512-75moEQKg23YKA3A2DNSFhq719ZPmby5mpwOD+NO7ZffJ88oMS/2eY8l8qpA3hvb1PTBHDxyKazhJirW+f4t0Wg==",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.10.2",
|
||||
"classcat": "^5.0.3",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz",
|
||||
@@ -2446,11 +2543,227 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/chroma-js/-/chroma-js-2.4.2.tgz",
|
||||
"integrity": "sha512-gbiHvCuBS9aXkE3OEDfS69bscNLTYtbbx2TQf6WyOu+4eCH1AH1gPSiDGF2UzwkRFAbqKNsC5F0mY0xcaEHCbg=="
|
||||
},
|
||||
"node_modules/@types/d3": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3/-/d3-7.4.3.tgz",
|
||||
"integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "*",
|
||||
"@types/d3-axis": "*",
|
||||
"@types/d3-brush": "*",
|
||||
"@types/d3-chord": "*",
|
||||
"@types/d3-color": "*",
|
||||
"@types/d3-contour": "*",
|
||||
"@types/d3-delaunay": "*",
|
||||
"@types/d3-dispatch": "*",
|
||||
"@types/d3-drag": "*",
|
||||
"@types/d3-dsv": "*",
|
||||
"@types/d3-ease": "*",
|
||||
"@types/d3-fetch": "*",
|
||||
"@types/d3-force": "*",
|
||||
"@types/d3-format": "*",
|
||||
"@types/d3-geo": "*",
|
||||
"@types/d3-hierarchy": "*",
|
||||
"@types/d3-interpolate": "*",
|
||||
"@types/d3-path": "*",
|
||||
"@types/d3-polygon": "*",
|
||||
"@types/d3-quadtree": "*",
|
||||
"@types/d3-random": "*",
|
||||
"@types/d3-scale": "*",
|
||||
"@types/d3-scale-chromatic": "*",
|
||||
"@types/d3-selection": "*",
|
||||
"@types/d3-shape": "*",
|
||||
"@types/d3-time": "*",
|
||||
"@types/d3-time-format": "*",
|
||||
"@types/d3-timer": "*",
|
||||
"@types/d3-transition": "*",
|
||||
"@types/d3-zoom": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
|
||||
},
|
||||
"node_modules/@types/d3-axis": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-axis/-/d3-axis-3.0.6.tgz",
|
||||
"integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-brush": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-brush/-/d3-brush-3.0.6.tgz",
|
||||
"integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-chord": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-chord/-/d3-chord-3.0.6.tgz",
|
||||
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
|
||||
},
|
||||
"node_modules/@types/d3-contour": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-contour/-/d3-contour-3.0.6.tgz",
|
||||
"integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "*",
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-delaunay": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="
|
||||
},
|
||||
"node_modules/@types/d3-dispatch": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
|
||||
"integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ=="
|
||||
},
|
||||
"node_modules/@types/d3-drag": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-drag/-/d3-drag-3.0.7.tgz",
|
||||
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-dsv": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
|
||||
"integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
|
||||
},
|
||||
"node_modules/@types/d3-fetch": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
|
||||
"integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
|
||||
"dependencies": {
|
||||
"@types/d3-dsv": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-force": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-force/-/d3-force-3.0.9.tgz",
|
||||
"integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA=="
|
||||
},
|
||||
"node_modules/@types/d3-format": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-format/-/d3-format-3.0.4.tgz",
|
||||
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="
|
||||
},
|
||||
"node_modules/@types/d3-geo": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-geo/-/d3-geo-3.1.0.tgz",
|
||||
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-hierarchy": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz",
|
||||
"integrity": "sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw=="
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.0.2.tgz",
|
||||
"integrity": "sha512-WAIEVlOCdd/NKRYTsqCpOMHQHemKBEINf8YXMYOtXH0GA7SY0dqMB78P3Uhgfy+4X+/Mlw2wDtlETkN6kQUCMA=="
|
||||
},
|
||||
"node_modules/@types/d3-polygon": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
|
||||
"integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="
|
||||
},
|
||||
"node_modules/@types/d3-quadtree": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
|
||||
"integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="
|
||||
},
|
||||
"node_modules/@types/d3-random": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-random/-/d3-random-3.0.3.tgz",
|
||||
"integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.8.tgz",
|
||||
"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-scale-chromatic": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
|
||||
"integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw=="
|
||||
},
|
||||
"node_modules/@types/d3-selection": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-selection/-/d3-selection-3.0.10.tgz",
|
||||
"integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg=="
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.6.tgz",
|
||||
"integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.3.tgz",
|
||||
"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
|
||||
},
|
||||
"node_modules/@types/d3-time-format": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
|
||||
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-2.0.1.tgz",
|
||||
"integrity": "sha512-TF8aoF5cHcLO7W7403blM7L1T+6NF3XMyN3fxyUolq2uOcFeicG/khQg/dGxiCJWoAcmYulYN7LYSRKO54IXaA==",
|
||||
"peer": true
|
||||
"integrity": "sha512-TF8aoF5cHcLO7W7403blM7L1T+6NF3XMyN3fxyUolq2uOcFeicG/khQg/dGxiCJWoAcmYulYN7LYSRKO54IXaA=="
|
||||
},
|
||||
"node_modules/@types/d3-transition": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-transition/-/d3-transition-3.0.8.tgz",
|
||||
"integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-zoom": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
|
||||
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
|
||||
"dependencies": {
|
||||
"@types/d3-interpolate": "*",
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.8",
|
||||
@@ -2460,6 +2773,11 @@
|
||||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.13",
|
||||
"resolved": "https://registry.npmmirror.com/@types/geojson/-/geojson-7946.0.13.tgz",
|
||||
"integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ=="
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.5.tgz",
|
||||
@@ -3420,6 +3738,11 @@
|
||||
"resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz",
|
||||
"integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A=="
|
||||
},
|
||||
"node_modules/classcat": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/classcat/-/classcat-5.0.4.tgz",
|
||||
"integrity": "sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g=="
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||
@@ -3639,6 +3962,18 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dsv": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
|
||||
@@ -3756,6 +4091,14 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
@@ -3772,11 +4115,44 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz",
|
||||
"integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw=="
|
||||
},
|
||||
"node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-voronoi": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
|
||||
"integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/dagre": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz",
|
||||
@@ -8565,6 +8941,23 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reactflow": {
|
||||
"version": "11.10.2",
|
||||
"resolved": "https://registry.npmmirror.com/reactflow/-/reactflow-11.10.2.tgz",
|
||||
"integrity": "sha512-tqQJfPEiIkXonT3piVYf+F9CvABI5e28t5I6rpaLTnO8YVCAOh1h0f+ziDKz0Bx9Y2B/mFgyz+H7LZeUp/+lhQ==",
|
||||
"dependencies": {
|
||||
"@reactflow/background": "11.3.7",
|
||||
"@reactflow/controls": "11.2.7",
|
||||
"@reactflow/core": "11.10.2",
|
||||
"@reactflow/minimap": "11.7.7",
|
||||
"@reactflow/node-resizer": "2.2.7",
|
||||
"@reactflow/node-toolbar": "1.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -9322,6 +9715,11 @@
|
||||
"resolved": "https://registry.npmjs.org/svg-path-parser/-/svg-path-parser-1.1.0.tgz",
|
||||
"integrity": "sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A=="
|
||||
},
|
||||
"node_modules/tailwind-scrollbar-hide": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmmirror.com/tailwind-scrollbar-hide/-/tailwind-scrollbar-hide-1.1.7.tgz",
|
||||
"integrity": "sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA=="
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
|
||||
@@ -9868,6 +10266,14 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -10147,6 +10553,33 @@
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/zustand/-/zustand-4.5.0.tgz",
|
||||
"integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=16.8",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/zwitch": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"react-i18next": "^13.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"reactflow": "^11.10.2",
|
||||
"rehype-raw": "6.1.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sql-formatter": "^12.2.4",
|
||||
|
||||
213
web/pages/flow/canvas/index.tsx
Normal file
213
web/pages/flow/canvas/index.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
import { addFlow, apiInterceptors, getFlowById, updateFlowById } from '@/client/api';
|
||||
import MuiLoading from '@/components/common/loading';
|
||||
import AddNodes from '@/components/flow/add-nodes';
|
||||
import ButtonEdge from '@/components/flow/button-edge';
|
||||
import CanvasNode from '@/components/flow/canvas-node';
|
||||
import { IFlowData } from '@/types/flow';
|
||||
import { getUniqueNodeId, mapHumpToUnderline } from '@/utils/flow';
|
||||
import { SaveOutlined } from '@ant-design/icons';
|
||||
import { Divider, Input, Modal, message } from 'antd';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import React, { DragEvent, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ReactFlow, { Background, Connection, Controls, ReactFlowProvider, addEdge, useEdgesState, useNodesState, useReactFlow, Node } from 'reactflow';
|
||||
import 'reactflow/dist/style.css';
|
||||
|
||||
interface Props {
|
||||
// Define your component props here
|
||||
}
|
||||
const nodeTypes = { customNode: CanvasNode };
|
||||
const edgeTypes = { buttonedge: ButtonEdge };
|
||||
|
||||
const Canvas: React.FC<Props> = () => {
|
||||
const { t } = useTranslation();
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const searchParams = useSearchParams();
|
||||
const id = searchParams?.get('id') || '';
|
||||
const reactFlow = useReactFlow();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [name, setName] = useState('');
|
||||
|
||||
async function getFlowData() {
|
||||
setLoading(true);
|
||||
const [_, data] = await apiInterceptors(getFlowById(id));
|
||||
if (data) {
|
||||
const flowData = mapHumpToUnderline(data.flow_data);
|
||||
setName(data.name);
|
||||
setNodes(flowData.nodes.map((node) => ({ ...node, type: 'customNode' })));
|
||||
setEdges(flowData.edges.map((edge) => ({ ...edge, type: 'buttonedge' })));
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
id && getFlowData();
|
||||
}, [id]);
|
||||
|
||||
function onNodesClick(event: any, clickedNode: Node) {
|
||||
reactFlow.setNodes((nds) =>
|
||||
nds.map((node) => {
|
||||
if (node.id === clickedNode.id) {
|
||||
node.data = {
|
||||
...node.data,
|
||||
selected: true,
|
||||
};
|
||||
} else {
|
||||
node.data = {
|
||||
...node.data,
|
||||
selected: false,
|
||||
};
|
||||
}
|
||||
return node;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function onConnect(connection: Connection) {
|
||||
const newEdge = {
|
||||
...connection,
|
||||
type: 'buttonedge',
|
||||
id: `${connection.source}|${connection.target}`,
|
||||
};
|
||||
setEdges((eds) => addEdge(newEdge, eds));
|
||||
}
|
||||
|
||||
const onDrop = useCallback(
|
||||
(event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
const reactFlowBounds = reactFlowWrapper.current!.getBoundingClientRect();
|
||||
let nodeStr = event.dataTransfer.getData('application/reactflow');
|
||||
if (!nodeStr || typeof nodeStr === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const nodeData = JSON.parse(nodeStr);
|
||||
const position = reactFlow.screenToFlowPosition({
|
||||
x: event.clientX - reactFlowBounds.left,
|
||||
y: event.clientY - reactFlowBounds.top,
|
||||
});
|
||||
const nodeId = getUniqueNodeId(nodeData, reactFlow.getNodes());
|
||||
const newNode = {
|
||||
id: nodeId,
|
||||
position,
|
||||
type: 'customNode',
|
||||
data: nodeData,
|
||||
};
|
||||
setNodes((nds) =>
|
||||
nds.concat(newNode).map((node) => {
|
||||
if (node.id === newNode.id) {
|
||||
node.data = {
|
||||
...node.data,
|
||||
selected: true,
|
||||
};
|
||||
} else {
|
||||
node.data = {
|
||||
...node.data,
|
||||
selected: false,
|
||||
};
|
||||
}
|
||||
return node;
|
||||
}),
|
||||
);
|
||||
},
|
||||
[reactFlow],
|
||||
);
|
||||
|
||||
const onDragOver = useCallback((event: DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
}, []);
|
||||
|
||||
function clickSave() {
|
||||
if (id) {
|
||||
handleSaveFlow();
|
||||
} else {
|
||||
setIsModalVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveFlow() {
|
||||
const reactFlowObject = mapHumpToUnderline(reactFlow.toObject() as IFlowData);
|
||||
if (id) {
|
||||
const [, , res] = await apiInterceptors(updateFlowById(id, { name, uid: id, flow_data: reactFlowObject }));
|
||||
if (res?.success) {
|
||||
messageApi.success(t('save_flow_success'));
|
||||
} else if (res?.err_msg) {
|
||||
messageApi.error(res?.err_msg);
|
||||
}
|
||||
} else {
|
||||
if (!name) {
|
||||
return messageApi.warning(t('flow_name_required'));
|
||||
}
|
||||
const [_, res] = await apiInterceptors(addFlow({ name, flow_data: reactFlowObject }));
|
||||
if (res?.uid) {
|
||||
messageApi.success(t('save_flow_success'));
|
||||
const history = window.history;
|
||||
history.pushState(null, '', `/flow/canvas?id=${res.uid}`);
|
||||
}
|
||||
setIsModalVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-2">
|
||||
<MuiLoading visible={loading} />
|
||||
<div className="my-2 flex flex-row justify-end items-center">
|
||||
<div className="w-8 h-8 rounded-md bg-stone-300 flext justify-center items-center text-center">
|
||||
<SaveOutlined className="text-xl" onClick={clickSave} />
|
||||
</div>
|
||||
</div>
|
||||
<Divider className="mt-2 mb-0" />
|
||||
<div className="min-h-screen w-full h-full" ref={reactFlowWrapper}>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onNodeClick={onNodesClick}
|
||||
onConnect={onConnect}
|
||||
onDrop={onDrop}
|
||||
onDragOver={onDragOver}
|
||||
minZoom={0.1}
|
||||
fitView
|
||||
>
|
||||
<Controls className="flex flex-row items-center" position="bottom-center" />
|
||||
<Background color="#aaa" gap={16} />
|
||||
<AddNodes />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
<Modal
|
||||
title={t('flow_modal_title')}
|
||||
open={isModalVisible}
|
||||
onOk={handleSaveFlow}
|
||||
onCancel={() => {
|
||||
setIsModalVisible(false);
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<p>{t('flow_name')}</p>
|
||||
<Input
|
||||
onChange={(e) => {
|
||||
setName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
</Modal>
|
||||
{contextHolder}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function CanvasWrapper() {
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<Canvas />
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
||||
49
web/pages/flow/index.tsx
Normal file
49
web/pages/flow/index.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { apiInterceptors, getFlows } from '@/client/api';
|
||||
import MuiLoading from '@/components/common/loading';
|
||||
import FlowCard from '@/components/flow/flow-card';
|
||||
import { IFlow } from '@/types/flow';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Empty } from 'antd';
|
||||
import Link from 'next/link';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
function Flow() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [flowList, setFlowList] = useState<Array<IFlow>>([]);
|
||||
|
||||
async function getFlowList() {
|
||||
setLoading(true);
|
||||
const [_, data] = await apiInterceptors(getFlows());
|
||||
setLoading(false);
|
||||
setFlowList(data?.items ?? []);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getFlowList();
|
||||
}, []);
|
||||
|
||||
function updateFlowList(uid: string) {
|
||||
setFlowList((flows) => flows.filter((flow) => flow.uid !== uid));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative p-4 md:p-6 bg-[#FAFAFA] dark:bg-transparent min-h-full overflow-y-auto">
|
||||
<MuiLoading visible={loading} />
|
||||
<div className="mb-4">
|
||||
<Link href="/flow/canvas">
|
||||
<Button type="primary" className="flex items-center" icon={<PlusOutlined />}>
|
||||
New AWEL Flow
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="min-h-[600px] flex flex-wrap gap-2 md:gap-4 justify-start items-start">
|
||||
{flowList.map((flow) => (
|
||||
<FlowCard key={flow.uid} flow={flow} deleteCallback={updateFlowList} />
|
||||
))}
|
||||
{flowList.length === 0 && <Empty description="No flow found" />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Flow;
|
||||
37
web/public/icons/node/default_node_icon.svg
Normal file
37
web/public/icons/node/default_node_icon.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="88" fill="none">
|
||||
<path fill="#FFD21E" d="M47.21 76.5a34.75 34.75 0 1 0 0-69.5 34.75 34.75 0 0 0 0 69.5Z" />
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M81.96 41.75a34.75 34.75 0 1 0-69.5 0 34.75 34.75 0 0 0 69.5 0Zm-73.5 0a38.75 38.75 0 1 1 77.5 0 38.75 38.75 0 0 1-77.5 0Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
d="M58.5 32.3c1.28.44 1.78 3.06 3.07 2.38a5 5 0 1 0-6.76-2.07c.61 1.15 2.55-.72 3.7-.32ZM34.95 32.3c-1.28.44-1.79 3.06-3.07 2.38a5 5 0 1 1 6.76-2.07c-.61 1.15-2.56-.72-3.7-.32Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF323D"
|
||||
d="M46.96 56.29c9.83 0 13-8.76 13-13.26 0-2.34-1.57-1.6-4.09-.36-2.33 1.15-5.46 2.74-8.9 2.74-7.19 0-13-6.88-13-2.38s3.16 13.26 13 13.26Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
fillRule="evenodd"
|
||||
d="M39.43 54a8.7 8.7 0 0 1 5.3-4.49c.4-.12.81.57 1.24 1.28.4.68.82 1.37 1.24 1.37.45 0 .9-.68 1.33-1.35.45-.7.89-1.38 1.32-1.25a8.61 8.61 0 0 1 5 4.17c3.73-2.94 5.1-7.74 5.1-10.7 0-2.34-1.57-1.6-4.09-.36l-.14.07c-2.31 1.15-5.39 2.67-8.77 2.67s-6.45-1.52-8.77-2.67c-2.6-1.29-4.23-2.1-4.23.29 0 3.05 1.46 8.06 5.47 10.97Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M70.71 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM24.21 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM17.52 48c-1.62 0-3.06.66-4.07 1.87a5.97 5.97 0 0 0-1.33 3.76 7.1 7.1 0 0 0-1.94-.3c-1.55 0-2.95.59-3.94 1.66a5.8 5.8 0 0 0-.8 7 5.3 5.3 0 0 0-1.79 2.82c-.24.9-.48 2.8.8 4.74a5.22 5.22 0 0 0-.37 5.02c1.02 2.32 3.57 4.14 8.52 6.1 3.07 1.22 5.89 2 5.91 2.01a44.33 44.33 0 0 0 10.93 1.6c5.86 0 10.05-1.8 12.46-5.34 3.88-5.69 3.33-10.9-1.7-15.92-2.77-2.78-4.62-6.87-5-7.77-.78-2.66-2.84-5.62-6.25-5.62a5.7 5.7 0 0 0-4.6 2.46c-1-1.26-1.98-2.25-2.86-2.82A7.4 7.4 0 0 0 17.52 48Zm0 4c.51 0 1.14.22 1.82.65 2.14 1.36 6.25 8.43 7.76 11.18.5.92 1.37 1.31 2.14 1.31 1.55 0 2.75-1.53.15-3.48-3.92-2.93-2.55-7.72-.68-8.01.08-.02.17-.02.24-.02 1.7 0 2.45 2.93 2.45 2.93s2.2 5.52 5.98 9.3c3.77 3.77 3.97 6.8 1.22 10.83-1.88 2.75-5.47 3.58-9.16 3.58-3.81 0-7.73-.9-9.92-1.46-.11-.03-13.45-3.8-11.76-7 .28-.54.75-.76 1.34-.76 2.38 0 6.7 3.54 8.57 3.54.41 0 .7-.17.83-.6.79-2.85-12.06-4.05-10.98-8.17.2-.73.71-1.02 1.44-1.02 3.14 0 10.2 5.53 11.68 5.53.11 0 .2-.03.24-.1.74-1.2.33-2.04-4.9-5.2-5.21-3.16-8.88-5.06-6.8-7.33.24-.26.58-.38 1-.38 3.17 0 10.66 6.82 10.66 6.82s2.02 2.1 3.25 2.1c.28 0 .52-.1.68-.38.86-1.46-8.06-8.22-8.56-11.01-.34-1.9.24-2.85 1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M38.6 76.69c2.75-4.04 2.55-7.07-1.22-10.84-3.78-3.77-5.98-9.3-5.98-9.3s-.82-3.2-2.69-2.9c-1.87.3-3.24 5.08.68 8.01 3.91 2.93-.78 4.92-2.29 2.17-1.5-2.75-5.62-9.82-7.76-11.18-2.13-1.35-3.63-.6-3.13 2.2.5 2.79 9.43 9.55 8.56 11-.87 1.47-3.93-1.71-3.93-1.71s-9.57-8.71-11.66-6.44c-2.08 2.27 1.59 4.17 6.8 7.33 5.23 3.16 5.64 4 4.9 5.2-.75 1.2-12.28-8.53-13.36-4.4-1.08 4.11 11.77 5.3 10.98 8.15-.8 2.85-9.06-5.38-10.74-2.18-1.7 3.21 11.65 6.98 11.76 7.01 4.3 1.12 15.25 3.49 19.08-2.12Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M77.4 48c1.62 0 3.07.66 4.07 1.87a5.97 5.97 0 0 1 1.33 3.76 7.1 7.1 0 0 1 1.95-.3c1.55 0 2.95.59 3.94 1.66a5.8 5.8 0 0 1 .8 7 5.3 5.3 0 0 1 1.78 2.82c.24.9.48 2.8-.8 4.74a5.22 5.22 0 0 1 .37 5.02c-1.02 2.32-3.57 4.14-8.51 6.1-3.08 1.22-5.9 2-5.92 2.01a44.33 44.33 0 0 1-10.93 1.6c-5.86 0-10.05-1.8-12.46-5.34-3.88-5.69-3.33-10.9 1.7-15.92 2.78-2.78 4.63-6.87 5.01-7.77.78-2.66 2.83-5.62 6.24-5.62a5.7 5.7 0 0 1 4.6 2.46c1-1.26 1.98-2.25 2.87-2.82A7.4 7.4 0 0 1 77.4 48Zm0 4c-.51 0-1.13.22-1.82.65-2.13 1.36-6.25 8.43-7.76 11.18a2.43 2.43 0 0 1-2.14 1.31c-1.54 0-2.75-1.53-.14-3.48 3.91-2.93 2.54-7.72.67-8.01a1.54 1.54 0 0 0-.24-.02c-1.7 0-2.45 2.93-2.45 2.93s-2.2 5.52-5.97 9.3c-3.78 3.77-3.98 6.8-1.22 10.83 1.87 2.75 5.47 3.58 9.15 3.58 3.82 0 7.73-.9 9.93-1.46.1-.03 13.45-3.8 11.76-7-.29-.54-.75-.76-1.34-.76-2.38 0-6.71 3.54-8.57 3.54-.42 0-.71-.17-.83-.6-.8-2.85 12.05-4.05 10.97-8.17-.19-.73-.7-1.02-1.44-1.02-3.14 0-10.2 5.53-11.68 5.53-.1 0-.19-.03-.23-.1-.74-1.2-.34-2.04 4.88-5.2 5.23-3.16 8.9-5.06 6.8-7.33-.23-.26-.57-.38-.98-.38-3.18 0-10.67 6.82-10.67 6.82s-2.02 2.1-3.24 2.1a.74.74 0 0 1-.68-.38c-.87-1.46 8.05-8.22 8.55-11.01.34-1.9-.24-2.85-1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M56.33 76.69c-2.75-4.04-2.56-7.07 1.22-10.84 3.77-3.77 5.97-9.3 5.97-9.3s.82-3.2 2.7-2.9c1.86.3 3.23 5.08-.68 8.01-3.92 2.93.78 4.92 2.28 2.17 1.51-2.75 5.63-9.82 7.76-11.18 2.13-1.35 3.64-.6 3.13 2.2-.5 2.79-9.42 9.55-8.55 11 .86 1.47 3.92-1.71 3.92-1.71s9.58-8.71 11.66-6.44c2.08 2.27-1.58 4.17-6.8 7.33-5.23 3.16-5.63 4-4.9 5.2.75 1.2 12.28-8.53 13.36-4.4 1.08 4.11-11.76 5.3-10.97 8.15.8 2.85 9.05-5.38 10.74-2.18 1.69 3.21-11.65 6.98-11.76 7.01-4.31 1.12-15.26 3.49-19.08-2.12Z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
37
web/public/icons/node/http_trigger.svg
Normal file
37
web/public/icons/node/http_trigger.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="88" fill="none">
|
||||
<path fill="#FFD21E" d="M47.21 76.5a34.75 34.75 0 1 0 0-69.5 34.75 34.75 0 0 0 0 69.5Z" />
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M81.96 41.75a34.75 34.75 0 1 0-69.5 0 34.75 34.75 0 0 0 69.5 0Zm-73.5 0a38.75 38.75 0 1 1 77.5 0 38.75 38.75 0 0 1-77.5 0Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
d="M58.5 32.3c1.28.44 1.78 3.06 3.07 2.38a5 5 0 1 0-6.76-2.07c.61 1.15 2.55-.72 3.7-.32ZM34.95 32.3c-1.28.44-1.79 3.06-3.07 2.38a5 5 0 1 1 6.76-2.07c-.61 1.15-2.56-.72-3.7-.32Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF323D"
|
||||
d="M46.96 56.29c9.83 0 13-8.76 13-13.26 0-2.34-1.57-1.6-4.09-.36-2.33 1.15-5.46 2.74-8.9 2.74-7.19 0-13-6.88-13-2.38s3.16 13.26 13 13.26Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
fillRule="evenodd"
|
||||
d="M39.43 54a8.7 8.7 0 0 1 5.3-4.49c.4-.12.81.57 1.24 1.28.4.68.82 1.37 1.24 1.37.45 0 .9-.68 1.33-1.35.45-.7.89-1.38 1.32-1.25a8.61 8.61 0 0 1 5 4.17c3.73-2.94 5.1-7.74 5.1-10.7 0-2.34-1.57-1.6-4.09-.36l-.14.07c-2.31 1.15-5.39 2.67-8.77 2.67s-6.45-1.52-8.77-2.67c-2.6-1.29-4.23-2.1-4.23.29 0 3.05 1.46 8.06 5.47 10.97Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M70.71 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM24.21 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM17.52 48c-1.62 0-3.06.66-4.07 1.87a5.97 5.97 0 0 0-1.33 3.76 7.1 7.1 0 0 0-1.94-.3c-1.55 0-2.95.59-3.94 1.66a5.8 5.8 0 0 0-.8 7 5.3 5.3 0 0 0-1.79 2.82c-.24.9-.48 2.8.8 4.74a5.22 5.22 0 0 0-.37 5.02c1.02 2.32 3.57 4.14 8.52 6.1 3.07 1.22 5.89 2 5.91 2.01a44.33 44.33 0 0 0 10.93 1.6c5.86 0 10.05-1.8 12.46-5.34 3.88-5.69 3.33-10.9-1.7-15.92-2.77-2.78-4.62-6.87-5-7.77-.78-2.66-2.84-5.62-6.25-5.62a5.7 5.7 0 0 0-4.6 2.46c-1-1.26-1.98-2.25-2.86-2.82A7.4 7.4 0 0 0 17.52 48Zm0 4c.51 0 1.14.22 1.82.65 2.14 1.36 6.25 8.43 7.76 11.18.5.92 1.37 1.31 2.14 1.31 1.55 0 2.75-1.53.15-3.48-3.92-2.93-2.55-7.72-.68-8.01.08-.02.17-.02.24-.02 1.7 0 2.45 2.93 2.45 2.93s2.2 5.52 5.98 9.3c3.77 3.77 3.97 6.8 1.22 10.83-1.88 2.75-5.47 3.58-9.16 3.58-3.81 0-7.73-.9-9.92-1.46-.11-.03-13.45-3.8-11.76-7 .28-.54.75-.76 1.34-.76 2.38 0 6.7 3.54 8.57 3.54.41 0 .7-.17.83-.6.79-2.85-12.06-4.05-10.98-8.17.2-.73.71-1.02 1.44-1.02 3.14 0 10.2 5.53 11.68 5.53.11 0 .2-.03.24-.1.74-1.2.33-2.04-4.9-5.2-5.21-3.16-8.88-5.06-6.8-7.33.24-.26.58-.38 1-.38 3.17 0 10.66 6.82 10.66 6.82s2.02 2.1 3.25 2.1c.28 0 .52-.1.68-.38.86-1.46-8.06-8.22-8.56-11.01-.34-1.9.24-2.85 1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M38.6 76.69c2.75-4.04 2.55-7.07-1.22-10.84-3.78-3.77-5.98-9.3-5.98-9.3s-.82-3.2-2.69-2.9c-1.87.3-3.24 5.08.68 8.01 3.91 2.93-.78 4.92-2.29 2.17-1.5-2.75-5.62-9.82-7.76-11.18-2.13-1.35-3.63-.6-3.13 2.2.5 2.79 9.43 9.55 8.56 11-.87 1.47-3.93-1.71-3.93-1.71s-9.57-8.71-11.66-6.44c-2.08 2.27 1.59 4.17 6.8 7.33 5.23 3.16 5.64 4 4.9 5.2-.75 1.2-12.28-8.53-13.36-4.4-1.08 4.11 11.77 5.3 10.98 8.15-.8 2.85-9.06-5.38-10.74-2.18-1.7 3.21 11.65 6.98 11.76 7.01 4.3 1.12 15.25 3.49 19.08-2.12Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M77.4 48c1.62 0 3.07.66 4.07 1.87a5.97 5.97 0 0 1 1.33 3.76 7.1 7.1 0 0 1 1.95-.3c1.55 0 2.95.59 3.94 1.66a5.8 5.8 0 0 1 .8 7 5.3 5.3 0 0 1 1.78 2.82c.24.9.48 2.8-.8 4.74a5.22 5.22 0 0 1 .37 5.02c-1.02 2.32-3.57 4.14-8.51 6.1-3.08 1.22-5.9 2-5.92 2.01a44.33 44.33 0 0 1-10.93 1.6c-5.86 0-10.05-1.8-12.46-5.34-3.88-5.69-3.33-10.9 1.7-15.92 2.78-2.78 4.63-6.87 5.01-7.77.78-2.66 2.83-5.62 6.24-5.62a5.7 5.7 0 0 1 4.6 2.46c1-1.26 1.98-2.25 2.87-2.82A7.4 7.4 0 0 1 77.4 48Zm0 4c-.51 0-1.13.22-1.82.65-2.13 1.36-6.25 8.43-7.76 11.18a2.43 2.43 0 0 1-2.14 1.31c-1.54 0-2.75-1.53-.14-3.48 3.91-2.93 2.54-7.72.67-8.01a1.54 1.54 0 0 0-.24-.02c-1.7 0-2.45 2.93-2.45 2.93s-2.2 5.52-5.97 9.3c-3.78 3.77-3.98 6.8-1.22 10.83 1.87 2.75 5.47 3.58 9.15 3.58 3.82 0 7.73-.9 9.93-1.46.1-.03 13.45-3.8 11.76-7-.29-.54-.75-.76-1.34-.76-2.38 0-6.71 3.54-8.57 3.54-.42 0-.71-.17-.83-.6-.8-2.85 12.05-4.05 10.97-8.17-.19-.73-.7-1.02-1.44-1.02-3.14 0-10.2 5.53-11.68 5.53-.1 0-.19-.03-.23-.1-.74-1.2-.34-2.04 4.88-5.2 5.23-3.16 8.9-5.06 6.8-7.33-.23-.26-.57-.38-.98-.38-3.18 0-10.67 6.82-10.67 6.82s-2.02 2.1-3.24 2.1a.74.74 0 0 1-.68-.38c-.87-1.46 8.05-8.22 8.55-11.01.34-1.9-.24-2.85-1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M56.33 76.69c-2.75-4.04-2.56-7.07 1.22-10.84 3.77-3.77 5.97-9.3 5.97-9.3s.82-3.2 2.7-2.9c1.86.3 3.23 5.08-.68 8.01-3.92 2.93.78 4.92 2.28 2.17 1.51-2.75 5.63-9.82 7.76-11.18 2.13-1.35 3.64-.6 3.13 2.2-.5 2.79-9.42 9.55-8.55 11 .86 1.47 3.92-1.71 3.92-1.71s9.58-8.71 11.66-6.44c2.08 2.27-1.58 4.17-6.8 7.33-5.23 3.16-5.63 4-4.9 5.2.75 1.2 12.28-8.53 13.36-4.4 1.08 4.11-11.76 5.3-10.97 8.15.8 2.85 9.05-5.38 10.74-2.18 1.69 3.21-11.65 6.98-11.76 7.01-4.31 1.12-15.26 3.49-19.08-2.12Z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
37
web/public/icons/node/llm_operator.svg
Normal file
37
web/public/icons/node/llm_operator.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="88" fill="none">
|
||||
<path fill="#FFD21E" d="M47.21 76.5a34.75 34.75 0 1 0 0-69.5 34.75 34.75 0 0 0 0 69.5Z" />
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M81.96 41.75a34.75 34.75 0 1 0-69.5 0 34.75 34.75 0 0 0 69.5 0Zm-73.5 0a38.75 38.75 0 1 1 77.5 0 38.75 38.75 0 0 1-77.5 0Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
d="M58.5 32.3c1.28.44 1.78 3.06 3.07 2.38a5 5 0 1 0-6.76-2.07c.61 1.15 2.55-.72 3.7-.32ZM34.95 32.3c-1.28.44-1.79 3.06-3.07 2.38a5 5 0 1 1 6.76-2.07c-.61 1.15-2.56-.72-3.7-.32Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF323D"
|
||||
d="M46.96 56.29c9.83 0 13-8.76 13-13.26 0-2.34-1.57-1.6-4.09-.36-2.33 1.15-5.46 2.74-8.9 2.74-7.19 0-13-6.88-13-2.38s3.16 13.26 13 13.26Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
fillRule="evenodd"
|
||||
d="M39.43 54a8.7 8.7 0 0 1 5.3-4.49c.4-.12.81.57 1.24 1.28.4.68.82 1.37 1.24 1.37.45 0 .9-.68 1.33-1.35.45-.7.89-1.38 1.32-1.25a8.61 8.61 0 0 1 5 4.17c3.73-2.94 5.1-7.74 5.1-10.7 0-2.34-1.57-1.6-4.09-.36l-.14.07c-2.31 1.15-5.39 2.67-8.77 2.67s-6.45-1.52-8.77-2.67c-2.6-1.29-4.23-2.1-4.23.29 0 3.05 1.46 8.06 5.47 10.97Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M70.71 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM24.21 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM17.52 48c-1.62 0-3.06.66-4.07 1.87a5.97 5.97 0 0 0-1.33 3.76 7.1 7.1 0 0 0-1.94-.3c-1.55 0-2.95.59-3.94 1.66a5.8 5.8 0 0 0-.8 7 5.3 5.3 0 0 0-1.79 2.82c-.24.9-.48 2.8.8 4.74a5.22 5.22 0 0 0-.37 5.02c1.02 2.32 3.57 4.14 8.52 6.1 3.07 1.22 5.89 2 5.91 2.01a44.33 44.33 0 0 0 10.93 1.6c5.86 0 10.05-1.8 12.46-5.34 3.88-5.69 3.33-10.9-1.7-15.92-2.77-2.78-4.62-6.87-5-7.77-.78-2.66-2.84-5.62-6.25-5.62a5.7 5.7 0 0 0-4.6 2.46c-1-1.26-1.98-2.25-2.86-2.82A7.4 7.4 0 0 0 17.52 48Zm0 4c.51 0 1.14.22 1.82.65 2.14 1.36 6.25 8.43 7.76 11.18.5.92 1.37 1.31 2.14 1.31 1.55 0 2.75-1.53.15-3.48-3.92-2.93-2.55-7.72-.68-8.01.08-.02.17-.02.24-.02 1.7 0 2.45 2.93 2.45 2.93s2.2 5.52 5.98 9.3c3.77 3.77 3.97 6.8 1.22 10.83-1.88 2.75-5.47 3.58-9.16 3.58-3.81 0-7.73-.9-9.92-1.46-.11-.03-13.45-3.8-11.76-7 .28-.54.75-.76 1.34-.76 2.38 0 6.7 3.54 8.57 3.54.41 0 .7-.17.83-.6.79-2.85-12.06-4.05-10.98-8.17.2-.73.71-1.02 1.44-1.02 3.14 0 10.2 5.53 11.68 5.53.11 0 .2-.03.24-.1.74-1.2.33-2.04-4.9-5.2-5.21-3.16-8.88-5.06-6.8-7.33.24-.26.58-.38 1-.38 3.17 0 10.66 6.82 10.66 6.82s2.02 2.1 3.25 2.1c.28 0 .52-.1.68-.38.86-1.46-8.06-8.22-8.56-11.01-.34-1.9.24-2.85 1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M38.6 76.69c2.75-4.04 2.55-7.07-1.22-10.84-3.78-3.77-5.98-9.3-5.98-9.3s-.82-3.2-2.69-2.9c-1.87.3-3.24 5.08.68 8.01 3.91 2.93-.78 4.92-2.29 2.17-1.5-2.75-5.62-9.82-7.76-11.18-2.13-1.35-3.63-.6-3.13 2.2.5 2.79 9.43 9.55 8.56 11-.87 1.47-3.93-1.71-3.93-1.71s-9.57-8.71-11.66-6.44c-2.08 2.27 1.59 4.17 6.8 7.33 5.23 3.16 5.64 4 4.9 5.2-.75 1.2-12.28-8.53-13.36-4.4-1.08 4.11 11.77 5.3 10.98 8.15-.8 2.85-9.06-5.38-10.74-2.18-1.7 3.21 11.65 6.98 11.76 7.01 4.3 1.12 15.25 3.49 19.08-2.12Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M77.4 48c1.62 0 3.07.66 4.07 1.87a5.97 5.97 0 0 1 1.33 3.76 7.1 7.1 0 0 1 1.95-.3c1.55 0 2.95.59 3.94 1.66a5.8 5.8 0 0 1 .8 7 5.3 5.3 0 0 1 1.78 2.82c.24.9.48 2.8-.8 4.74a5.22 5.22 0 0 1 .37 5.02c-1.02 2.32-3.57 4.14-8.51 6.1-3.08 1.22-5.9 2-5.92 2.01a44.33 44.33 0 0 1-10.93 1.6c-5.86 0-10.05-1.8-12.46-5.34-3.88-5.69-3.33-10.9 1.7-15.92 2.78-2.78 4.63-6.87 5.01-7.77.78-2.66 2.83-5.62 6.24-5.62a5.7 5.7 0 0 1 4.6 2.46c1-1.26 1.98-2.25 2.87-2.82A7.4 7.4 0 0 1 77.4 48Zm0 4c-.51 0-1.13.22-1.82.65-2.13 1.36-6.25 8.43-7.76 11.18a2.43 2.43 0 0 1-2.14 1.31c-1.54 0-2.75-1.53-.14-3.48 3.91-2.93 2.54-7.72.67-8.01a1.54 1.54 0 0 0-.24-.02c-1.7 0-2.45 2.93-2.45 2.93s-2.2 5.52-5.97 9.3c-3.78 3.77-3.98 6.8-1.22 10.83 1.87 2.75 5.47 3.58 9.15 3.58 3.82 0 7.73-.9 9.93-1.46.1-.03 13.45-3.8 11.76-7-.29-.54-.75-.76-1.34-.76-2.38 0-6.71 3.54-8.57 3.54-.42 0-.71-.17-.83-.6-.8-2.85 12.05-4.05 10.97-8.17-.19-.73-.7-1.02-1.44-1.02-3.14 0-10.2 5.53-11.68 5.53-.1 0-.19-.03-.23-.1-.74-1.2-.34-2.04 4.88-5.2 5.23-3.16 8.9-5.06 6.8-7.33-.23-.26-.57-.38-.98-.38-3.18 0-10.67 6.82-10.67 6.82s-2.02 2.1-3.24 2.1a.74.74 0 0 1-.68-.38c-.87-1.46 8.05-8.22 8.55-11.01.34-1.9-.24-2.85-1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M56.33 76.69c-2.75-4.04-2.56-7.07 1.22-10.84 3.77-3.77 5.97-9.3 5.97-9.3s.82-3.2 2.7-2.9c1.86.3 3.23 5.08-.68 8.01-3.92 2.93.78 4.92 2.28 2.17 1.51-2.75 5.63-9.82 7.76-11.18 2.13-1.35 3.64-.6 3.13 2.2-.5 2.79-9.42 9.55-8.55 11 .86 1.47 3.92-1.71 3.92-1.71s9.58-8.71 11.66-6.44c2.08 2.27-1.58 4.17-6.8 7.33-5.23 3.16-5.63 4-4.9 5.2.75 1.2 12.28-8.53 13.36-4.4 1.08 4.11-11.76 5.3-10.97 8.15.8 2.85 9.05-5.38 10.74-2.18 1.69 3.21-11.65 6.98-11.76 7.01-4.31 1.12-15.26 3.49-19.08-2.12Z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
37
web/public/icons/node/openai_llm_client.svg
Normal file
37
web/public/icons/node/openai_llm_client.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="95" height="88" fill="none">
|
||||
<path fill="#FFD21E" d="M47.21 76.5a34.75 34.75 0 1 0 0-69.5 34.75 34.75 0 0 0 0 69.5Z" />
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M81.96 41.75a34.75 34.75 0 1 0-69.5 0 34.75 34.75 0 0 0 69.5 0Zm-73.5 0a38.75 38.75 0 1 1 77.5 0 38.75 38.75 0 0 1-77.5 0Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
d="M58.5 32.3c1.28.44 1.78 3.06 3.07 2.38a5 5 0 1 0-6.76-2.07c.61 1.15 2.55-.72 3.7-.32ZM34.95 32.3c-1.28.44-1.79 3.06-3.07 2.38a5 5 0 1 1 6.76-2.07c-.61 1.15-2.56-.72-3.7-.32Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF323D"
|
||||
d="M46.96 56.29c9.83 0 13-8.76 13-13.26 0-2.34-1.57-1.6-4.09-.36-2.33 1.15-5.46 2.74-8.9 2.74-7.19 0-13-6.88-13-2.38s3.16 13.26 13 13.26Z"
|
||||
/>
|
||||
<path
|
||||
fill="#3A3B45"
|
||||
fillRule="evenodd"
|
||||
d="M39.43 54a8.7 8.7 0 0 1 5.3-4.49c.4-.12.81.57 1.24 1.28.4.68.82 1.37 1.24 1.37.45 0 .9-.68 1.33-1.35.45-.7.89-1.38 1.32-1.25a8.61 8.61 0 0 1 5 4.17c3.73-2.94 5.1-7.74 5.1-10.7 0-2.34-1.57-1.6-4.09-.36l-.14.07c-2.31 1.15-5.39 2.67-8.77 2.67s-6.45-1.52-8.77-2.67c-2.6-1.29-4.23-2.1-4.23.29 0 3.05 1.46 8.06 5.47 10.97Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M70.71 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM24.21 37a3.25 3.25 0 1 0 0-6.5 3.25 3.25 0 0 0 0 6.5ZM17.52 48c-1.62 0-3.06.66-4.07 1.87a5.97 5.97 0 0 0-1.33 3.76 7.1 7.1 0 0 0-1.94-.3c-1.55 0-2.95.59-3.94 1.66a5.8 5.8 0 0 0-.8 7 5.3 5.3 0 0 0-1.79 2.82c-.24.9-.48 2.8.8 4.74a5.22 5.22 0 0 0-.37 5.02c1.02 2.32 3.57 4.14 8.52 6.1 3.07 1.22 5.89 2 5.91 2.01a44.33 44.33 0 0 0 10.93 1.6c5.86 0 10.05-1.8 12.46-5.34 3.88-5.69 3.33-10.9-1.7-15.92-2.77-2.78-4.62-6.87-5-7.77-.78-2.66-2.84-5.62-6.25-5.62a5.7 5.7 0 0 0-4.6 2.46c-1-1.26-1.98-2.25-2.86-2.82A7.4 7.4 0 0 0 17.52 48Zm0 4c.51 0 1.14.22 1.82.65 2.14 1.36 6.25 8.43 7.76 11.18.5.92 1.37 1.31 2.14 1.31 1.55 0 2.75-1.53.15-3.48-3.92-2.93-2.55-7.72-.68-8.01.08-.02.17-.02.24-.02 1.7 0 2.45 2.93 2.45 2.93s2.2 5.52 5.98 9.3c3.77 3.77 3.97 6.8 1.22 10.83-1.88 2.75-5.47 3.58-9.16 3.58-3.81 0-7.73-.9-9.92-1.46-.11-.03-13.45-3.8-11.76-7 .28-.54.75-.76 1.34-.76 2.38 0 6.7 3.54 8.57 3.54.41 0 .7-.17.83-.6.79-2.85-12.06-4.05-10.98-8.17.2-.73.71-1.02 1.44-1.02 3.14 0 10.2 5.53 11.68 5.53.11 0 .2-.03.24-.1.74-1.2.33-2.04-4.9-5.2-5.21-3.16-8.88-5.06-6.8-7.33.24-.26.58-.38 1-.38 3.17 0 10.66 6.82 10.66 6.82s2.02 2.1 3.25 2.1c.28 0 .52-.1.68-.38.86-1.46-8.06-8.22-8.56-11.01-.34-1.9.24-2.85 1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M38.6 76.69c2.75-4.04 2.55-7.07-1.22-10.84-3.78-3.77-5.98-9.3-5.98-9.3s-.82-3.2-2.69-2.9c-1.87.3-3.24 5.08.68 8.01 3.91 2.93-.78 4.92-2.29 2.17-1.5-2.75-5.62-9.82-7.76-11.18-2.13-1.35-3.63-.6-3.13 2.2.5 2.79 9.43 9.55 8.56 11-.87 1.47-3.93-1.71-3.93-1.71s-9.57-8.71-11.66-6.44c-2.08 2.27 1.59 4.17 6.8 7.33 5.23 3.16 5.64 4 4.9 5.2-.75 1.2-12.28-8.53-13.36-4.4-1.08 4.11 11.77 5.3 10.98 8.15-.8 2.85-9.06-5.38-10.74-2.18-1.7 3.21 11.65 6.98 11.76 7.01 4.3 1.12 15.25 3.49 19.08-2.12Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FF9D0B"
|
||||
d="M77.4 48c1.62 0 3.07.66 4.07 1.87a5.97 5.97 0 0 1 1.33 3.76 7.1 7.1 0 0 1 1.95-.3c1.55 0 2.95.59 3.94 1.66a5.8 5.8 0 0 1 .8 7 5.3 5.3 0 0 1 1.78 2.82c.24.9.48 2.8-.8 4.74a5.22 5.22 0 0 1 .37 5.02c-1.02 2.32-3.57 4.14-8.51 6.1-3.08 1.22-5.9 2-5.92 2.01a44.33 44.33 0 0 1-10.93 1.6c-5.86 0-10.05-1.8-12.46-5.34-3.88-5.69-3.33-10.9 1.7-15.92 2.78-2.78 4.63-6.87 5.01-7.77.78-2.66 2.83-5.62 6.24-5.62a5.7 5.7 0 0 1 4.6 2.46c1-1.26 1.98-2.25 2.87-2.82A7.4 7.4 0 0 1 77.4 48Zm0 4c-.51 0-1.13.22-1.82.65-2.13 1.36-6.25 8.43-7.76 11.18a2.43 2.43 0 0 1-2.14 1.31c-1.54 0-2.75-1.53-.14-3.48 3.91-2.93 2.54-7.72.67-8.01a1.54 1.54 0 0 0-.24-.02c-1.7 0-2.45 2.93-2.45 2.93s-2.2 5.52-5.97 9.3c-3.78 3.77-3.98 6.8-1.22 10.83 1.87 2.75 5.47 3.58 9.15 3.58 3.82 0 7.73-.9 9.93-1.46.1-.03 13.45-3.8 11.76-7-.29-.54-.75-.76-1.34-.76-2.38 0-6.71 3.54-8.57 3.54-.42 0-.71-.17-.83-.6-.8-2.85 12.05-4.05 10.97-8.17-.19-.73-.7-1.02-1.44-1.02-3.14 0-10.2 5.53-11.68 5.53-.1 0-.19-.03-.23-.1-.74-1.2-.34-2.04 4.88-5.2 5.23-3.16 8.9-5.06 6.8-7.33-.23-.26-.57-.38-.98-.38-3.18 0-10.67 6.82-10.67 6.82s-2.02 2.1-3.24 2.1a.74.74 0 0 1-.68-.38c-.87-1.46 8.05-8.22 8.55-11.01.34-1.9-.24-2.85-1.31-2.85Z"
|
||||
/>
|
||||
<path
|
||||
fill="#FFD21E"
|
||||
d="M56.33 76.69c-2.75-4.04-2.56-7.07 1.22-10.84 3.77-3.77 5.97-9.3 5.97-9.3s.82-3.2 2.7-2.9c1.86.3 3.23 5.08-.68 8.01-3.92 2.93.78 4.92 2.28 2.17 1.51-2.75 5.63-9.82 7.76-11.18 2.13-1.35 3.64-.6 3.13 2.2-.5 2.79-9.42 9.55-8.55 11 .86 1.47 3.92-1.71 3.92-1.71s9.58-8.71 11.66-6.44c2.08 2.27-1.58 4.17-6.8 7.33-5.23 3.16-5.63 4-4.9 5.2.75 1.2 12.28-8.53 13.36-4.4 1.08 4.11-11.76 5.3-10.97 8.15.8 2.85 9.05-5.38 10.74-2.18 1.69 3.21-11.65 6.98-11.76 7.01-4.31 1.12-15.26 3.49-19.08-2.12Z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -18,6 +18,9 @@ body {
|
||||
color: #f7f7f7;
|
||||
background-color: #151622;
|
||||
}
|
||||
.dark-sub-bg {
|
||||
background-color: rgb(35, 38, 44);
|
||||
}
|
||||
|
||||
.ant-btn-primary {
|
||||
background-color: var(--antd-primary-color);
|
||||
@@ -48,7 +51,10 @@ table tr td {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.scrollbar-default::-webkit-scrollbar {
|
||||
display: block;
|
||||
width: 6px;
|
||||
}
|
||||
/* 自定义滚动条样式 */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
@@ -70,7 +76,7 @@ table tr td {
|
||||
color: white;
|
||||
}
|
||||
|
||||
:where(.css-dev-only-do-not-override-18iikkb).ant-form-item .ant-form-item-label>label {
|
||||
:where(.css-dev-only-do-not-override-18iikkb).ant-form-item .ant-form-item-label > label {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
@@ -78,4 +84,4 @@ table tr td {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
116
web/types/flow.ts
Normal file
116
web/types/flow.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Node } from 'reactflow';
|
||||
|
||||
export type IFlowUpdateParam = {
|
||||
name: string;
|
||||
uid?: string;
|
||||
flow_data: IFlowData;
|
||||
};
|
||||
|
||||
export type IFlow = {
|
||||
uid: string;
|
||||
name: string;
|
||||
flow_data: IFlowData;
|
||||
};
|
||||
|
||||
export type IFlowResponse = {
|
||||
items: Array<IFlow>;
|
||||
total_count: number;
|
||||
total_pages: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
};
|
||||
|
||||
export type IFlowNodeParameter = {
|
||||
id: string;
|
||||
type_name: string;
|
||||
type_cls: string;
|
||||
label: string;
|
||||
name: string;
|
||||
category: string;
|
||||
optional: boolean;
|
||||
default?: any;
|
||||
placeholder?: any;
|
||||
description: string;
|
||||
options?: any;
|
||||
value: any;
|
||||
};
|
||||
|
||||
export type IFlowNodeInput = {
|
||||
type_name: string;
|
||||
type_cls: string;
|
||||
label: string;
|
||||
name: string;
|
||||
description: string;
|
||||
id: string;
|
||||
optional?: boolean | undefined;
|
||||
value: any;
|
||||
};
|
||||
|
||||
export type IFlowNodeOutput = {
|
||||
type_name: string;
|
||||
type_cls: string;
|
||||
label: string;
|
||||
name: string;
|
||||
description: string;
|
||||
id: string;
|
||||
optional?: boolean | undefined;
|
||||
};
|
||||
|
||||
export type IFlowNode = Node & {
|
||||
type_name: string;
|
||||
type_cls: string;
|
||||
type: string;
|
||||
label: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
flow_type: 'resource' | 'operator';
|
||||
icon?: string;
|
||||
documentation_url?: null;
|
||||
id: string;
|
||||
tags?: any;
|
||||
parameters: Array<IFlowNodeParameter>;
|
||||
inputs: Array<IFlowNodeInput>;
|
||||
outputs: Array<IFlowNodeOutput>;
|
||||
version: string;
|
||||
};
|
||||
|
||||
interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
zoom: number;
|
||||
}
|
||||
|
||||
// flodata, the data of the flow
|
||||
export type IFlowDataNode = {
|
||||
width: number;
|
||||
height: number;
|
||||
id: string;
|
||||
position: Position;
|
||||
position_absolute?: Position;
|
||||
positionAbsolute?: Position;
|
||||
data: IFlowNode;
|
||||
};
|
||||
|
||||
export type IFlowDataEdge = {
|
||||
source: string;
|
||||
target: string;
|
||||
source_handle?: string;
|
||||
sourceHandle?: string;
|
||||
target_handle?: string;
|
||||
targetHandle?: string;
|
||||
id: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type IFlowDataViewport = {
|
||||
x: number;
|
||||
y: number;
|
||||
zoom: number;
|
||||
};
|
||||
|
||||
export type IFlowData = {
|
||||
nodes: Array<IFlowDataNode>;
|
||||
edges: Array<IFlowDataEdge>;
|
||||
viewport: IFlowDataViewport;
|
||||
};
|
||||
71
web/utils/flow.ts
Normal file
71
web/utils/flow.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { IFlowData, IFlowNode } from '@/types/flow';
|
||||
import { Node } from 'reactflow';
|
||||
|
||||
export const getUniqueNodeId = (nodeData: IFlowNode, nodes: Node[]) => {
|
||||
let count = 0;
|
||||
nodes.forEach((node) => {
|
||||
if (node.data.name === nodeData.name) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
return `${nodeData.id}_${count}`;
|
||||
};
|
||||
|
||||
// 驼峰转下划线,接口协议字段命名规范
|
||||
export const mapHumpToUnderline = (flowData: IFlowData) => {
|
||||
/**
|
||||
* sourceHandle -> source_handle,
|
||||
* targetHandle -> target_handle,
|
||||
* positionAbsolute -> position_absolute
|
||||
*/
|
||||
const { nodes, edges, ...rest } = flowData;
|
||||
const newNodes = nodes.map((node) => {
|
||||
const { positionAbsolute, ...rest } = node;
|
||||
return {
|
||||
position_absolute: positionAbsolute,
|
||||
...rest,
|
||||
};
|
||||
});
|
||||
const newEdges = edges.map((edge) => {
|
||||
const { sourceHandle, targetHandle, ...rest } = edge;
|
||||
return {
|
||||
source_handle: sourceHandle,
|
||||
target_handle: targetHandle,
|
||||
...rest,
|
||||
};
|
||||
});
|
||||
return {
|
||||
nodes: newNodes,
|
||||
edges: newEdges,
|
||||
...rest,
|
||||
};
|
||||
};
|
||||
|
||||
export const mapUnderlineToHump = (flowData: IFlowData) => {
|
||||
/**
|
||||
* source_handle -> sourceHandle,
|
||||
* target_handle -> targetHandle,
|
||||
* position_absolute -> positionAbsolute
|
||||
*/
|
||||
const { nodes, edges, ...rest } = flowData;
|
||||
const newNodes = nodes.map((node) => {
|
||||
const { position_absolute, ...rest } = node;
|
||||
return {
|
||||
positionAbsolute: position_absolute,
|
||||
...rest,
|
||||
};
|
||||
});
|
||||
const newEdges = edges.map((edge) => {
|
||||
const { source_handle, target_handle, ...rest } = edge;
|
||||
return {
|
||||
sourceHandle: source_handle,
|
||||
targetHandle: target_handle,
|
||||
...rest,
|
||||
};
|
||||
});
|
||||
return {
|
||||
nodes: newNodes,
|
||||
edges: newEdges,
|
||||
...rest,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user