diff --git a/src/components/ConnectionDropMenu.tsx b/src/components/ConnectionDropMenu.tsx
index 3b5bf4b9..c464751d 100644
--- a/src/components/ConnectionDropMenu.tsx
+++ b/src/components/ConnectionDropMenu.tsx
@@ -237,6 +237,15 @@ const VIDEO_TARGET_OPTIONS: MenuOption[] = [
),
},
+ {
+ type: "videoTrim",
+ label: "Video Trim",
+ icon: (
+
+ ),
+ },
{
type: "generateVideo",
label: "Generate Video",
@@ -286,6 +295,15 @@ const VIDEO_SOURCE_OPTIONS: MenuOption[] = [
),
},
+ {
+ type: "videoTrim",
+ label: "Video Trim",
+ icon: (
+
+ ),
+ },
];
// Audio target options (nodes that accept audio input)
diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx
index 132acdb4..9327e1ad 100644
--- a/src/components/WorkflowCanvas.tsx
+++ b/src/components/WorkflowCanvas.tsx
@@ -37,6 +37,7 @@ import {
ImageCompareNode,
VideoStitchNode,
EaseCurveNode,
+ VideoTrimNode,
} from "./nodes";
// Lazy-load GLBViewerNode to avoid bundling three.js for users who don't use 3D nodes
@@ -74,6 +75,7 @@ const nodeTypes: NodeTypes = {
imageCompare: ImageCompareNode,
videoStitch: VideoStitchNode,
easeCurve: EaseCurveNode,
+ videoTrim: VideoTrimNode,
glbViewer: GLBViewerNode,
};
@@ -140,6 +142,8 @@ const getNodeHandles = (nodeType: string): { inputs: string[]; outputs: string[]
return { inputs: ["video", "audio"], outputs: ["video"] };
case "easeCurve":
return { inputs: ["video", "easeCurve"], outputs: ["video", "easeCurve"] };
+ case "videoTrim":
+ return { inputs: ["video"], outputs: ["video"] };
case "glbViewer":
return { inputs: ["3d"], outputs: ["image"] };
default:
@@ -332,7 +336,7 @@ export function WorkflowCanvas() {
if (!targetNode) return false;
const targetNodeType = targetNode.type;
- if (targetNodeType === "generateVideo" || targetNodeType === "videoStitch" || targetNodeType === "easeCurve" || targetNodeType === "output") {
+ if (targetNodeType === "generateVideo" || targetNodeType === "videoStitch" || targetNodeType === "easeCurve" || targetNodeType === "videoTrim" || 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;
@@ -841,6 +845,10 @@ export function WorkflowCanvas() {
// EaseCurve accepts video input and outputs video
targetHandleId = "video";
sourceHandleIdForNewNode = "video";
+ } else if (nodeType === "videoTrim") {
+ // VideoTrim accepts video input and outputs video
+ targetHandleId = "video";
+ sourceHandleIdForNewNode = "video";
} else if (nodeType === "generateVideo") {
// GenerateVideo outputs video
sourceHandleIdForNewNode = "video";
@@ -1714,6 +1722,8 @@ export function WorkflowCanvas() {
return "#f97316";
case "easeCurve":
return "#bef264"; // lime-300 (easy-peasy-ease)
+ case "videoTrim":
+ return "#60a5fa"; // blue-400 (trim/cut)
case "glbViewer":
return "#38bdf8"; // sky-400 (3D viewport)
default:
diff --git a/src/components/nodes/index.ts b/src/components/nodes/index.ts
index 32462834..8d580db5 100644
--- a/src/components/nodes/index.ts
+++ b/src/components/nodes/index.ts
@@ -14,4 +14,5 @@ export { OutputGalleryNode } from "./OutputGalleryNode";
export { ImageCompareNode } from "./ImageCompareNode";
export { VideoStitchNode } from "./VideoStitchNode";
export { EaseCurveNode } from "./EaseCurveNode";
+export { VideoTrimNode } from "./VideoTrimNode";
export { GroupNode } from "./GroupNode";