Browse Source

feat(quick-007): wire VideoTrimNode into canvas, connection menu, and exports

- Export VideoTrimNode from src/components/nodes/index.ts
- Register videoTrim in nodeTypes map in WorkflowCanvas.tsx
- Add videoTrim to getNodeHandles (video in, video out)
- Add videoTrim to isValidConnection video target list
- Add videoTrim connection drop handling (video input and output)
- Add videoTrim minimap color: blue-400 (#60a5fa)
- Add VideoTrim to VIDEO_TARGET_OPTIONS in ConnectionDropMenu (scissors icon)
- Add VideoTrim to VIDEO_SOURCE_OPTIONS in ConnectionDropMenu (scissors icon)
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
640e8dccb9
  1. 18
      src/components/ConnectionDropMenu.tsx
  2. 12
      src/components/WorkflowCanvas.tsx
  3. 1
      src/components/nodes/index.ts

18
src/components/ConnectionDropMenu.tsx

@ -237,6 +237,15 @@ const VIDEO_TARGET_OPTIONS: MenuOption[] = [
</svg> </svg>
), ),
}, },
{
type: "videoTrim",
label: "Video Trim",
icon: (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M7.848 8.25l1.536.887M7.848 8.25a3 3 0 11-5.196-3 3 3 0 015.196 3zm1.536.887a2.165 2.165 0 011.083 1.839c.005.351.054.695.14 1.024M9.384 9.137l2.077 1.199M7.848 15.75l1.536-.887m-1.536.887a3 3 0 11-5.196 3 3 3 0 015.196-3zm1.536-.887a2.165 2.165 0 001.083-1.838c.005-.352.054-.695.14-1.025m-1.223 2.863l2.077-1.199m0-3.328a4.323 4.323 0 012.068-1.379l5.325-1.628a4.5 4.5 0 012.48-.044l.803.215-7.794 4.5m-2.882-1.664A4.331 4.331 0 0010.607 12m3.736 0l7.794 4.5-.802.215a4.5 4.5 0 01-2.48-.043l-5.326-1.629a4.324 4.324 0 01-2.068-1.379M14.343 12l-2.882 1.664" />
</svg>
),
},
{ {
type: "generateVideo", type: "generateVideo",
label: "Generate Video", label: "Generate Video",
@ -286,6 +295,15 @@ const VIDEO_SOURCE_OPTIONS: MenuOption[] = [
</svg> </svg>
), ),
}, },
{
type: "videoTrim",
label: "Video Trim",
icon: (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M7.848 8.25l1.536.887M7.848 8.25a3 3 0 11-5.196-3 3 3 0 015.196 3zm1.536.887a2.165 2.165 0 011.083 1.839c.005.351.054.695.14 1.024M9.384 9.137l2.077 1.199M7.848 15.75l1.536-.887m-1.536.887a3 3 0 11-5.196 3 3 3 0 015.196-3zm1.536-.887a2.165 2.165 0 001.083-1.838c.005-.352.054-.695.14-1.025m-1.223 2.863l2.077-1.199m0-3.328a4.323 4.323 0 012.068-1.379l5.325-1.628a4.5 4.5 0 012.48-.044l.803.215-7.794 4.5m-2.882-1.664A4.331 4.331 0 0010.607 12m3.736 0l7.794 4.5-.802.215a4.5 4.5 0 01-2.48-.043l-5.326-1.629a4.324 4.324 0 01-2.068-1.379M14.343 12l-2.882 1.664" />
</svg>
),
},
]; ];
// Audio target options (nodes that accept audio input) // Audio target options (nodes that accept audio input)

12
src/components/WorkflowCanvas.tsx

@ -37,6 +37,7 @@ import {
ImageCompareNode, ImageCompareNode,
VideoStitchNode, VideoStitchNode,
EaseCurveNode, EaseCurveNode,
VideoTrimNode,
} from "./nodes"; } from "./nodes";
// Lazy-load GLBViewerNode to avoid bundling three.js for users who don't use 3D 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, imageCompare: ImageCompareNode,
videoStitch: VideoStitchNode, videoStitch: VideoStitchNode,
easeCurve: EaseCurveNode, easeCurve: EaseCurveNode,
videoTrim: VideoTrimNode,
glbViewer: GLBViewerNode, glbViewer: GLBViewerNode,
}; };
@ -140,6 +142,8 @@ const getNodeHandles = (nodeType: string): { inputs: string[]; outputs: string[]
return { inputs: ["video", "audio"], outputs: ["video"] }; return { inputs: ["video", "audio"], outputs: ["video"] };
case "easeCurve": case "easeCurve":
return { inputs: ["video", "easeCurve"], outputs: ["video", "easeCurve"] }; return { inputs: ["video", "easeCurve"], outputs: ["video", "easeCurve"] };
case "videoTrim":
return { inputs: ["video"], outputs: ["video"] };
case "glbViewer": case "glbViewer":
return { inputs: ["3d"], outputs: ["image"] }; return { inputs: ["3d"], outputs: ["image"] };
default: default:
@ -332,7 +336,7 @@ export function WorkflowCanvas() {
if (!targetNode) return false; if (!targetNode) return false;
const targetNodeType = targetNode.type; 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" // For output node, we allow video even though its handle is typed as "image"
// because output node can display both images and videos // because output node can display both images and videos
return true; return true;
@ -841,6 +845,10 @@ export function WorkflowCanvas() {
// EaseCurve accepts video input and outputs video // EaseCurve accepts video input and outputs video
targetHandleId = "video"; targetHandleId = "video";
sourceHandleIdForNewNode = "video"; sourceHandleIdForNewNode = "video";
} else if (nodeType === "videoTrim") {
// VideoTrim accepts video input and outputs video
targetHandleId = "video";
sourceHandleIdForNewNode = "video";
} else if (nodeType === "generateVideo") { } else if (nodeType === "generateVideo") {
// GenerateVideo outputs video // GenerateVideo outputs video
sourceHandleIdForNewNode = "video"; sourceHandleIdForNewNode = "video";
@ -1714,6 +1722,8 @@ export function WorkflowCanvas() {
return "#f97316"; return "#f97316";
case "easeCurve": case "easeCurve":
return "#bef264"; // lime-300 (easy-peasy-ease) return "#bef264"; // lime-300 (easy-peasy-ease)
case "videoTrim":
return "#60a5fa"; // blue-400 (trim/cut)
case "glbViewer": case "glbViewer":
return "#38bdf8"; // sky-400 (3D viewport) return "#38bdf8"; // sky-400 (3D viewport)
default: default:

1
src/components/nodes/index.ts

@ -14,4 +14,5 @@ export { OutputGalleryNode } from "./OutputGalleryNode";
export { ImageCompareNode } from "./ImageCompareNode"; export { ImageCompareNode } from "./ImageCompareNode";
export { VideoStitchNode } from "./VideoStitchNode"; export { VideoStitchNode } from "./VideoStitchNode";
export { EaseCurveNode } from "./EaseCurveNode"; export { EaseCurveNode } from "./EaseCurveNode";
export { VideoTrimNode } from "./VideoTrimNode";
export { GroupNode } from "./GroupNode"; export { GroupNode } from "./GroupNode";

Loading…
Cancel
Save