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"; }