diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx index 6f532baa7c..29b46ddc22 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx @@ -4,9 +4,17 @@ import { memo } from 'react' import Workflow from '@/app/components/workflow' const Page = () => { + const nodes = [ + { + id: '1', + type: 'custom', + position: { x: 180, y: 180 }, + data: { type: 'start' }, + }, + ] return ( ) diff --git a/web/app/components/base/icons/assets/vender/line/layout/organize-grid.svg b/web/app/components/base/icons/assets/vender/line/layout/organize-grid.svg new file mode 100644 index 0000000000..802359d095 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/line/layout/organize-grid.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.json b/web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.json new file mode 100644 index 0000000000..cf369fd60c --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.json @@ -0,0 +1,81 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "16", + "height": "16", + "viewBox": "0 0 16 16", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "Icon" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "Vector" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "d": "M9.33366 10.667C9.33366 9.93061 9.93061 9.33366 10.667 9.33366H12.0003C12.7367 9.33366 13.3337 9.93061 13.3337 10.667V12.0003C13.3337 12.7367 12.7367 13.3337 12.0003 13.3337H10.667C9.93061 13.3337 9.33366 12.7367 9.33366 12.0003V10.667Z", + "stroke": "currentColor", + "stroke-width": "1.5", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M2.66699 10.667C2.66699 9.93059 3.26395 9.33366 4.00033 9.33366H5.33366C6.07004 9.33366 6.66699 9.93059 6.66699 10.667V12.0003C6.66699 12.7367 6.07004 13.3337 5.33366 13.3337H4.00033C3.26395 13.3337 2.66699 12.7367 2.66699 12.0003V10.667Z", + "stroke": "currentColor", + "stroke-width": "1.5", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M2.66699 4.00033C2.66699 3.26395 3.26393 2.66699 4.00033 2.66699H5.33366C6.07006 2.66699 6.66699 3.26395 6.66699 4.00033V5.33366C6.66699 6.07004 6.07006 6.66699 5.33366 6.66699H4.00033C3.26393 6.66699 2.66699 6.07004 2.66699 5.33366V4.00033Z", + "stroke": "currentColor", + "stroke-width": "1.5", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + } + ] + }, + { + "type": "element", + "name": "path", + "attributes": { + "id": "Vector_2", + "d": "M11.6409 2.1899C11.5143 1.93674 11.153 1.93674 11.0265 2.1899L10.3544 3.53389C10.3213 3.60035 10.2673 3.65425 10.2008 3.68748L8.85684 4.35948C8.60371 4.48606 8.60371 4.84732 8.85684 4.97389L10.2008 5.64589C10.2673 5.67913 10.3213 5.73303 10.3544 5.7995L11.0265 7.14348C11.153 7.39664 11.5143 7.39664 11.6409 7.14348L12.3129 5.7995C12.3461 5.73303 12.4 5.67913 12.4665 5.64589L13.8105 4.97389C14.0636 4.84732 14.0636 4.48606 13.8105 4.35948L12.4665 3.68748C12.4 3.65425 12.3461 3.60035 12.3129 3.53389L11.6409 2.1899Z", + "fill": "currentColor" + }, + "children": [] + } + ] + } + ] + }, + "name": "OrganizeGrid" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.tsx b/web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.tsx new file mode 100644 index 0000000000..f01763c61e --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/layout/OrganizeGrid.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './OrganizeGrid.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'OrganizeGrid' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/line/layout/index.ts b/web/app/components/base/icons/src/vender/line/layout/index.ts index 7c12b1f58f..88be234417 100644 --- a/web/app/components/base/icons/src/vender/line/layout/index.ts +++ b/web/app/components/base/icons/src/vender/line/layout/index.ts @@ -2,3 +2,4 @@ export { default as AlignLeft01 } from './AlignLeft01' export { default as AlignRight01 } from './AlignRight01' export { default as Grid01 } from './Grid01' export { default as LayoutGrid02 } from './LayoutGrid02' +export { default as OrganizeGrid } from './OrganizeGrid' diff --git a/web/app/components/workflow/features.tsx b/web/app/components/workflow/features.tsx index 9f1e897d84..9a870660b7 100644 --- a/web/app/components/workflow/features.tsx +++ b/web/app/components/workflow/features.tsx @@ -4,41 +4,38 @@ import { XClose } from '@/app/components/base/icons/src/vender/line/general' import { FeaturesChoose, FeaturesPanel, - FeaturesProvider, } from '@/app/components/base/features' const Features = () => { const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel) return ( - -
-
- Features -
- -
-
setShowFeaturesPanel(false)} - > - -
+
+
+ Features +
+ +
+
setShowFeaturesPanel(false)} + > +
-
- {}, - }} - annotationProps={{ - onEmbeddingChange: () => {}, - onScoreChange: () => {}, - }} - /> -
- +
+ {}, + }} + annotationProps={{ + onEmbeddingChange: () => {}, + onScoreChange: () => {}, + }} + /> +
+
) } diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 074e7458c1..81d22a7616 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -1,7 +1,6 @@ import type { FC } from 'react' import { memo, - // useEffect, } from 'react' import { useParams } from 'next/navigation' import useSWR from 'swr' @@ -10,11 +9,9 @@ import ReactFlow, { Background, ReactFlowProvider, useEdgesState, - // useNodesInitialized, useNodesState, } from 'reactflow' import 'reactflow/dist/style.css' -// import './style.css' import type { Edge, Node, @@ -22,7 +19,7 @@ import type { import { useWorkflow } from './hooks' import Header from './header' import CustomNode from './nodes' -import ZoomInOut from './zoom-in-out' +import Operator from './operator' import CustomEdge from './custom-edge' import CustomConnectionLine from './custom-connection-line' import Panel from './panel' @@ -34,6 +31,7 @@ import { syncWorkflowDraft, } from '@/service/workflow' import Loading from '@/app/components/base/loading' +import { FeaturesProvider } from '@/app/components/base/features' const nodeTypes = { custom: CustomNode, @@ -53,11 +51,8 @@ const Workflow: FC = memo(({ const showFeaturesPanel = useStore(state => state.showFeaturesPanel) const [nodes] = useNodesState(initialNodes) const [edges, _, onEdgesChange] = useEdgesState(initialEdges) - // const nodesInitialized = useNodesInitialized() const { - // handleLayout, - handleNodeDragStart, handleNodeDrag, handleNodeDragStop, @@ -71,18 +66,13 @@ const Workflow: FC = memo(({ handleEdgeDelete, } = useWorkflow() - // useEffect(() => { - // if (nodesInitialized) - // handleLayout() - // }, [nodesInitialized, handleLayout]) - useKeyPress('Backspace', handleEdgeDelete) return (
- + { showFeaturesPanel && } @@ -121,8 +111,7 @@ const WorkflowWrap: FC = ({ edges, }) => { const appId = useParams().appId - const { data, isLoading, error } = useSWR(`/apps/${appId}/workflows/draft`, fetchWorkflowDraft) - // const { data: configsData } = useSWR(`/apps/${appId}/workflows/default-workflow-block-configs`, fetchNodesDefaultConfigs) + const { isLoading, error } = useSWR(`/apps/${appId}/workflows/draft`, fetchWorkflowDraft) if (error) { syncWorkflowDraft({ @@ -152,10 +141,12 @@ const WorkflowWrap: FC = ({ return ( - + + + ) } diff --git a/web/app/components/workflow/operator/index.tsx b/web/app/components/workflow/operator/index.tsx new file mode 100644 index 0000000000..4feb08adc0 --- /dev/null +++ b/web/app/components/workflow/operator/index.tsx @@ -0,0 +1,22 @@ +import { memo } from 'react' +import ZoomInOut from './zoom-in-out' +import { OrganizeGrid } from '@/app/components/base/icons/src/vender/line/layout' +import TooltipPlus from '@/app/components/base/tooltip-plus' + +const Operator = () => { + return ( +
+ + +
+ +
+
+
+ ) +} + +export default memo(Operator) diff --git a/web/app/components/workflow/operator/zoom-in-out.tsx b/web/app/components/workflow/operator/zoom-in-out.tsx new file mode 100644 index 0000000000..4373320bd1 --- /dev/null +++ b/web/app/components/workflow/operator/zoom-in-out.tsx @@ -0,0 +1,109 @@ +import type { FC } from 'react' +import { + Fragment, + memo, + useState, +} from 'react' +import { useReactFlow } from 'reactflow' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import { SearchLg } from '@/app/components/base/icons/src/vender/line/general' +import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' + +const ZOOM_IN_OUT_OPTIONS = [ + [ + { + key: 'in', + text: 'Zoom In', + }, + { + key: 'out', + text: 'Zoom Out', + }, + ], + [ + { + key: 'to50', + text: 'Zoom to 50%', + }, + { + key: 'to100', + text: 'Zoom to 100%', + }, + ], + [ + { + key: 'fit', + text: 'Zoom to Fit', + }, + ], +] + +const ZoomInOut: FC = () => { + const reactFlow = useReactFlow() + const [open, setOpen] = useState(false) + + const handleZoom = (type: string) => { + if (type === 'in') + reactFlow.zoomIn() + + if (type === 'out') + reactFlow.zoomOut() + + if (type === 'fit') + reactFlow.fitView() + } + + return ( + + setOpen(v => !v)}> +
+ + 100% + +
+
+ +
+ { + ZOOM_IN_OUT_OPTIONS.map((options, i) => ( + + { + i !== 0 && ( +
+ ) + } +
+ { + options.map(option => ( +
handleZoom(option.key)} + > + {option.text} +
+ )) + } +
+ + )) + } +
+ + + ) +} + +export default memo(ZoomInOut)