diff --git a/src/components/ConnectionDropMenu.tsx b/src/components/ConnectionDropMenu.tsx
index 48b74ee7..afba791f 100644
--- a/src/components/ConnectionDropMenu.tsx
+++ b/src/components/ConnectionDropMenu.tsx
@@ -210,6 +210,15 @@ const VIDEO_TARGET_OPTIONS: MenuOption[] = [
),
},
+ {
+ type: "easeCurve",
+ label: "Ease Curve",
+ icon: (
+
+ ),
+ },
{
type: "generateVideo",
label: "Generate Video",
@@ -250,6 +259,15 @@ const VIDEO_SOURCE_OPTIONS: MenuOption[] = [
),
},
+ {
+ type: "easeCurve",
+ label: "Ease Curve",
+ icon: (
+
+ ),
+ },
];
// Audio target options (nodes that accept audio input)
diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx
index 0f0d7ac1..3b80e1a8 100644
--- a/src/components/WorkflowCanvas.tsx
+++ b/src/components/WorkflowCanvas.tsx
@@ -33,6 +33,7 @@ import {
OutputGalleryNode,
ImageCompareNode,
VideoStitchNode,
+ EaseCurveNode,
} from "./nodes";
import { EditableEdge, ReferenceEdge } from "./edges";
import { ConnectionDropMenu, MenuAction } from "./ConnectionDropMenu";
@@ -63,6 +64,7 @@ const nodeTypes: NodeTypes = {
outputGallery: OutputGalleryNode,
imageCompare: ImageCompareNode,
videoStitch: VideoStitchNode,
+ easeCurve: EaseCurveNode,
};
const edgeTypes: EdgeTypes = {
@@ -118,6 +120,8 @@ const getNodeHandles = (nodeType: string): { inputs: string[]; outputs: string[]
return { inputs: ["image"], outputs: [] };
case "videoStitch":
return { inputs: ["video", "audio"], outputs: ["video"] };
+ case "easeCurve":
+ return { inputs: ["video"], outputs: ["video"] };
default:
return { inputs: [], outputs: [] };
}
@@ -300,7 +304,7 @@ export function WorkflowCanvas() {
if (!targetNode) return false;
const targetNodeType = targetNode.type;
- if (targetNodeType === "generateVideo" || targetNodeType === "videoStitch" || targetNodeType === "output") {
+ if (targetNodeType === "generateVideo" || targetNodeType === "videoStitch" || targetNodeType === "easeCurve" || targetNodeType === "output") {
// For output node, we allow video even though its handle is typed as "image"
// because output node can display both images and videos
return true;
@@ -758,6 +762,10 @@ export function WorkflowCanvas() {
// VideoStitch has dynamic video-N inputs and a video output
targetHandleId = "video-0";
sourceHandleIdForNewNode = "video";
+ } else if (nodeType === "easeCurve") {
+ // EaseCurve accepts video input and outputs video
+ targetHandleId = "video";
+ sourceHandleIdForNewNode = "video";
} else if (nodeType === "generateVideo") {
// GenerateVideo outputs video
sourceHandleIdForNewNode = "video";
@@ -1535,6 +1543,8 @@ export function WorkflowCanvas() {
return "#14b8a6";
case "videoStitch":
return "#f97316";
+ case "easeCurve":
+ return "#f59e0b"; // amber-500
default:
return "#94a3b8";
}