+
{children}
diff --git a/ui/src/components/workflow/designer/index.ts b/ui/src/components/workflow/designer/index.ts
new file mode 100644
index 000000000..ea6c2b18d
--- /dev/null
+++ b/ui/src/components/workflow/designer/index.ts
@@ -0,0 +1,16 @@
+import Designer from "./Designer";
+import NodeDrawer from "./NodeDrawer";
+import Toolbar from "./Toolbar";
+
+export { type DesignerInstance as WorkflowDesignerInstance, type DesignerProps as WorkflowDesignerProps } from "./Designer";
+export const WorkflowDesigner = Designer;
+
+export { type NodeDrawerProps as WorkflowNodeDrawerProps } from "./NodeDrawer";
+export const WorkflowNodeDrawer = NodeDrawer;
+
+export { type ToolbarProps as WorkflowToolbarProps } from "./Toolbar";
+export const WorkflowToolbar = Toolbar;
+
+export * from "./_util";
+
+export type * from "./nodes/typings";
diff --git a/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx
index 4c1e0b7ba..ca46f63ff 100644
--- a/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizApplyNodeRegistry.tsx
@@ -2,12 +2,13 @@ import { getI18n } from "react-i18next";
import { FeedbackLevel, Field } from "@flowgram.ai/fixed-layout-editor";
import { IconContract } from "@tabler/icons-react";
import { Avatar } from "antd";
-import { nanoid } from "nanoid";
import { acmeDns01ProvidersMap } from "@/domain/provider";
+import { defaultNodeConfigForApply } from "@/domain/workflow";
import { BaseNode } from "./_shared";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
+import { newNodeId } from "../_util";
import BizApplyNodeConfigForm from "../forms/BizApplyNodeConfigForm";
export const BizApplyNodeRegistry: NodeRegistry = {
@@ -64,10 +65,11 @@ export const BizApplyNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.BizApply,
data: {
name: t("workflow_node.apply.default_name"),
+ config: defaultNodeConfigForApply(),
},
};
},
diff --git a/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx
index c89e721c5..dcf35c7e9 100644
--- a/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizDeployNodeRegistry.tsx
@@ -2,10 +2,11 @@ import { getI18n } from "react-i18next";
import { FeedbackLevel, Field } from "@flowgram.ai/fixed-layout-editor";
import { IconPackage } from "@tabler/icons-react";
import { Avatar } from "antd";
-import { nanoid } from "nanoid";
import { deploymentProvidersMap } from "@/domain/provider";
+import { defaultNodeConfigForDeploy } from "@/domain/workflow";
+import { getAllPreviousNodes, newNodeId } from "../_util";
import { BaseNode } from "./_shared";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
import BizDeployNodeConfigForm from "../forms/BizDeployNodeConfigForm";
@@ -36,6 +37,14 @@ export const BizDeployNodeRegistry: NodeRegistry = {
};
}
},
+ ["config.certificateOutputNodeId"]: ({ value, context: { node } }) => {
+ if (!getAllPreviousNodes(node).includes(value)) {
+ return {
+ message: "Invalid input",
+ level: FeedbackLevel.Error,
+ };
+ }
+ },
},
render: () => {
@@ -70,10 +79,11 @@ export const BizDeployNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
- type: NodeType.BizMonitor,
+ id: newNodeId(),
+ type: NodeType.BizDeploy,
data: {
name: t("workflow_node.deploy.default_name"),
+ config: defaultNodeConfigForDeploy(),
},
};
},
diff --git a/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx
index eb0d518b6..dbf705434 100644
--- a/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizMonitorNodeRegistry.tsx
@@ -1,9 +1,11 @@
import { getI18n } from "react-i18next";
import { FeedbackLevel, Field } from "@flowgram.ai/fixed-layout-editor";
import { IconDeviceDesktopSearch } from "@tabler/icons-react";
-import { nanoid } from "nanoid";
+
+import { defaultNodeConfigForMonitor } from "@/domain/workflow";
import { BaseNode } from "./_shared";
+import { newNodeId } from "../_util";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
import BizMonitorNodeConfigForm from "../forms/BizMonitorNodeConfigForm";
@@ -60,10 +62,11 @@ export const BizMonitorNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.BizMonitor,
data: {
name: t("workflow_node.monitor.default_name"),
+ config: defaultNodeConfigForMonitor(),
},
};
},
diff --git a/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx
index 86763c505..6ca34ccc6 100644
--- a/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizNotifyNodeRegistry.tsx
@@ -2,11 +2,12 @@ import { getI18n } from "react-i18next";
import { FeedbackLevel, Field } from "@flowgram.ai/fixed-layout-editor";
import { IconSend } from "@tabler/icons-react";
import { Avatar } from "antd";
-import { nanoid } from "nanoid";
import { notificationProvidersMap } from "@/domain/provider";
+import { defaultNodeConfigForNotify } from "@/domain/workflow";
import { BaseNode } from "./_shared";
+import { newNodeId } from "../_util";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
import BizNotifyNodeConfigForm from "../forms/BizNotifyNodeConfigForm";
@@ -70,10 +71,11 @@ export const BizNotifyNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.BizNotify,
data: {
name: t("workflow_node.notify.default_name"),
+ config: defaultNodeConfigForNotify(),
},
};
},
diff --git a/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx b/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx
index 270b9ac93..90629944b 100644
--- a/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx
+++ b/ui/src/components/workflow/designer/nodes/BizUploadNodeRegistry.tsx
@@ -1,9 +1,11 @@
import { getI18n } from "react-i18next";
import { FeedbackLevel, Field } from "@flowgram.ai/fixed-layout-editor";
import { IconCloudUpload } from "@tabler/icons-react";
-import { nanoid } from "nanoid";
+
+import { defaultNodeConfigForUpload } from "@/domain/workflow";
import { BaseNode } from "./_shared";
+import { newNodeId } from "../_util";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
import BizUploadNodeConfigForm from "../forms/BizUploadNodeConfigForm";
@@ -50,10 +52,11 @@ export const BizUploadNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.BizUpload,
data: {
name: t("workflow_node.upload.default_name"),
+ config: defaultNodeConfigForUpload(),
},
};
},
diff --git a/ui/src/components/workflow/designer/nodes/ConditionNode.tsx b/ui/src/components/workflow/designer/nodes/ConditionNode.tsx
index 52f788a7e..9048bff56 100644
--- a/ui/src/components/workflow/designer/nodes/ConditionNode.tsx
+++ b/ui/src/components/workflow/designer/nodes/ConditionNode.tsx
@@ -2,11 +2,11 @@ import { getI18n } from "react-i18next";
import { FeedbackLevel, Field, FlowNodeBaseType, FlowNodeSplitType } from "@flowgram.ai/fixed-layout-editor";
import { IconFilter, IconFilterFilled, IconSitemap } from "@tabler/icons-react";
import { Typography } from "antd";
-import { nanoid } from "nanoid";
import { type Expr, ExprType } from "@/domain/workflow";
import { BaseNode, BranchNode } from "./_shared";
+import { newNodeId } from "../_util";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
import BranchBlockNodeConfigForm from "../forms/BranchBlockNodeConfigForm";
@@ -40,14 +40,14 @@ export const ConditionNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.Condition,
data: {
name: t("workflow_node.condition.default_name"),
},
blocks: [
{
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.BranchBlock,
blocks: [],
data: {
@@ -55,7 +55,7 @@ export const ConditionNodeRegistry: NodeRegistry = {
},
},
{
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.BranchBlock,
data: {
name: t("workflow_node.branch_block.default_name") + " 2",
@@ -165,7 +165,7 @@ export const BranchBlockNodeRegistry: NodeRegistry = {
}
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.BranchBlock,
data: {
name: nodeName,
diff --git a/ui/src/components/workflow/designer/nodes/EndNode.tsx b/ui/src/components/workflow/designer/nodes/EndNode.tsx
index f851f8b6d..ee86e748b 100644
--- a/ui/src/components/workflow/designer/nodes/EndNode.tsx
+++ b/ui/src/components/workflow/designer/nodes/EndNode.tsx
@@ -1,9 +1,9 @@
import { getI18n } from "react-i18next";
import { FlowNodeBaseType } from "@flowgram.ai/fixed-layout-editor";
import { IconLogout } from "@tabler/icons-react";
-import { nanoid } from "nanoid";
import { BaseNode } from "./_shared";
+import { newNodeId } from "../_util";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
export const EndNodeRegistry: NodeRegistry = {
@@ -37,22 +37,11 @@ export const EndNodeRegistry: NodeRegistry = {
// You can only add to the last node of the branch
if (!from.isLast) return false;
- /**
- * condition
- * blockIcon
- * inlineBlocks
- * block1
- * blockOrderIcon
- * <---- [add end]
- * block2
- * blockOrderIcon
- * end
- */
- // originParent can determine whether it is condition , and then determine whether it is the last one
+ // `originParent` can determine whether it is condition, and then determine whether it is the last one
// https://github.com/bytedance/flowgram.ai/pull/146
if (from.parent && from.parent.parent?.flowNodeType === FlowNodeBaseType.INLINE_BLOCKS && from.parent.originParent && !from.parent.originParent.isLast) {
const allBranches = from.parent.parent!.blocks;
- // Determine whether the last node of all branch is end, All branches are not allowed to be end
+ // Determine whether the last node of all branch is end, all branches are not allowed to be end
const branchEndCount = allBranches.filter((block) => block.blocks[block.blocks.length - 1]?.getNodeMeta().isNodeEnd).length;
return branchEndCount < allBranches.length - 1;
}
@@ -68,7 +57,7 @@ export const EndNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.End,
data: {
name: t("workflow_node.end.default_name"),
diff --git a/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx b/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx
index 7149bf3bf..432cdeacf 100644
--- a/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx
+++ b/ui/src/components/workflow/designer/nodes/TryCatchNode.tsx
@@ -1,9 +1,9 @@
import { getI18n } from "react-i18next";
import { Field } from "@flowgram.ai/fixed-layout-editor";
import { IconArrowsSplit, IconCircleX } from "@tabler/icons-react";
-import { nanoid } from "nanoid";
import { BaseNode, BranchNode } from "./_shared";
+import { newNodeId } from "../_util";
import { NodeKindType, type NodeRegistry, NodeType } from "./typings";
export const TryCatchNodeRegistry: NodeRegistry = {
@@ -32,23 +32,23 @@ export const TryCatchNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.TryCatch,
data: {
name: t("workflow_node.try_catch.default_name"),
},
blocks: [
{
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.TryBlock,
blocks: [],
},
{
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.CatchBlock,
blocks: [
{
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.End,
data: {
name: t("workflow_node.end.default_name"),
@@ -106,11 +106,11 @@ export const CatchBlockNodeRegistry: NodeRegistry = {
const { t } = getI18n();
return {
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.CatchBlock,
blocks: [
{
- id: nanoid(),
+ id: newNodeId(),
type: NodeType.End,
data: {
name: t("workflow_node.end.default_name"),
diff --git a/ui/src/components/workflow/designer/nodes/_shared.tsx b/ui/src/components/workflow/designer/nodes/_shared.tsx
index d95c0d085..9099e64f1 100644
--- a/ui/src/components/workflow/designer/nodes/_shared.tsx
+++ b/ui/src/components/workflow/designer/nodes/_shared.tsx
@@ -8,14 +8,13 @@ import {
useClientContext,
useWatchFormState,
} from "@flowgram.ai/fixed-layout-editor";
-import { IconCopy, IconDotsVertical, IconExclamationCircle, IconGripVertical, IconLabel, IconX } from "@tabler/icons-react";
+import { IconCopy, IconDotsVertical, IconGripVertical, IconLabel, IconX } from "@tabler/icons-react";
import { Button, type ButtonProps, Card, Dropdown, Input, type InputRef, Popover, Tooltip, theme } from "antd";
-import { Immer } from "immer";
-import { nanoid } from "nanoid";
import { mergeCls } from "@/utils/css";
-import { type NodeJSON, type NodeRegistry, NodeType } from "./typings";
+import { type NodeJSON, type NodeRegistry } from "./typings";
+import { duplicateNodeJSON } from "../_util";
import { useNodeRenderContext } from "../NodeRenderContext";
const useInternalRenamingInput = ({ nodeRender }: { nodeRender: NodeRenderReturnType }) => {
@@ -44,7 +43,7 @@ const useInternalRenamingInput = ({ nodeRender }: { nodeRender: NodeRenderReturn
setInputVisible(false);
- nodeRender.updateData({ name: value });
+ nodeRender.updateData({ ...nodeRender.data, name: value });
};
return {
@@ -72,7 +71,7 @@ const InternalNodeCard = ({
const nodeRegistry = nodeRender.node.getNodeRegistry
();
const isActivated = useMemo(() => nodeRenderData.activated || nodeRenderData.lineActivated, [nodeRenderData.activated, nodeRenderData.lineActivated]);
- const [isHovered, setIsHovered] = useState(false);
+ const [isHovering, setIsHovering] = useState(false);
const [isInvalid, setIsInvalid] = useState(false);
const formState = useWatchFormState(nodeRender.node);
@@ -89,8 +88,8 @@ const InternalNodeCard = ({
style={style}
styles={{ body: { padding: 0 } }}
hoverable
- onMouseEnter={() => startTransition(() => setIsHovered(true))}
- onMouseLeave={() => startTransition(() => setIsHovered(false))}
+ onMouseEnter={() => startTransition(() => setIsHovering(true))}
+ onMouseLeave={() => startTransition(() => setIsHovering(false))}
>
{children}
@@ -284,11 +283,6 @@ export const BaseNode = ({ className, style, children, description }: BaseNodePr
);
};
- const [isInvalid, setIsInvalid] = useState(false);
-
- const formState = useWatchFormState(nodeRender.node);
- useEffect(() => setIsInvalid(!!formState?.invalid), [formState?.invalid]);
-
const {
inputRef,
visible: inputVisible,
@@ -349,9 +343,6 @@ export const BaseNode = ({ className, style, children, description }: BaseNodePr
)}
-