Browse Source

feat(quick-009): add VideoFrameGrabNodeData type, defaults, and connected inputs wiring

- Add 'videoFrameGrab' to NodeType union and VideoFrameGrabNodeData interface
- Register default dimensions (320x320) and default data in nodeDefaults
- Wire getSourceOutput to return image type from videoFrameGrab.outputImage
- Add videoFrameGrab dimensions to local Record types in WorkflowCanvas and validation
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
0a46423717
  1. 1
      src/components/WorkflowCanvas.tsx
  2. 1
      src/lib/quickstart/validation.ts
  3. 3
      src/store/utils/connectedInputs.ts
  4. 9
      src/store/utils/nodeDefaults.ts
  5. 12
      src/types/nodes.ts

1
src/components/WorkflowCanvas.tsx

@ -1136,6 +1136,7 @@ export function WorkflowCanvas() {
videoStitch: { width: 400, height: 280 },
easeCurve: { width: 340, height: 480 },
videoTrim: { width: 360, height: 360 },
videoFrameGrab: { width: 320, height: 320 },
glbViewer: { width: 360, height: 380 },
};
const dims = defaultDimensions[nodeType];

1
src/lib/quickstart/validation.ts

@ -45,6 +45,7 @@ const DEFAULT_DIMENSIONS: Record<NodeType, { width: number; height: number }> =
videoStitch: { width: 400, height: 280 },
easeCurve: { width: 340, height: 480 },
videoTrim: { width: 360, height: 360 },
videoFrameGrab: { width: 320, height: 320 },
glbViewer: { width: 360, height: 380 },
};

3
src/store/utils/connectedInputs.ts

@ -18,6 +18,7 @@ import {
VideoStitchNodeData,
EaseCurveNodeData,
VideoTrimNodeData,
VideoFrameGrabNodeData,
PromptNodeData,
PromptConstructorNodeData,
LLMGenerateNodeData,
@ -86,6 +87,8 @@ function getSourceOutput(sourceNode: WorkflowNode): { type: "image" | "text" | "
return { type: "text", value: pcData.outputText ?? pcData.template ?? null };
} else if (sourceNode.type === "llmGenerate") {
return { type: "text", value: (sourceNode.data as LLMGenerateNodeData).outputText };
} else if (sourceNode.type === "videoFrameGrab") {
return { type: "image", value: (sourceNode.data as VideoFrameGrabNodeData).outputImage };
} else if (sourceNode.type === "glbViewer") {
return { type: "image", value: (sourceNode.data as GLBViewerNodeData).capturedImage };
}

9
src/store/utils/nodeDefaults.ts

@ -16,6 +16,7 @@ import {
ImageCompareNodeData,
EaseCurveNodeData,
VideoTrimNodeData,
VideoFrameGrabNodeData,
GLBViewerNodeData,
WorkflowNodeData,
GroupColor,
@ -45,6 +46,7 @@ export const defaultNodeDimensions: Record<NodeType, { width: number; height: nu
videoStitch: { width: 400, height: 280 },
easeCurve: { width: 340, height: 480 },
videoTrim: { width: 360, height: 360 },
videoFrameGrab: { width: 320, height: 320 },
glbViewer: { width: 360, height: 380 },
};
@ -259,6 +261,13 @@ export const createDefaultNodeData = (type: NodeType): WorkflowNodeData => {
progress: 0,
encoderSupported: null,
} as VideoTrimNodeData;
case "videoFrameGrab":
return {
framePosition: "first",
outputImage: null,
status: "idle",
error: null,
} as VideoFrameGrabNodeData;
case "glbViewer":
return {
glbUrl: null,

12
src/types/nodes.ts

@ -39,6 +39,7 @@ export type NodeType =
| "videoStitch"
| "easeCurve"
| "videoTrim"
| "videoFrameGrab"
| "generate3d"
| "glbViewer";
@ -319,6 +320,16 @@ export interface VideoTrimNodeData extends BaseNodeData {
encoderSupported: boolean | null;
}
/**
* Video Frame Grab node - extracts the first or last frame from a video as a full-resolution PNG image
*/
export interface VideoFrameGrabNodeData extends BaseNodeData {
framePosition: "first" | "last"; // Which frame to extract
outputImage: string | null; // Extracted frame as base64 PNG data URL
status: NodeStatus;
error: string | null;
}
/**
* Split Grid node - splits image into grid cells for parallel processing
*/
@ -375,6 +386,7 @@ export type WorkflowNodeData =
| VideoStitchNodeData
| EaseCurveNodeData
| VideoTrimNodeData
| VideoFrameGrabNodeData
| GLBViewerNodeData;
/**

Loading…
Cancel
Save