/** * Node Types * * Types for workflow nodes including all node data interfaces, * handle types, and workflow node definitions. */ import { Node } from "@xyflow/react"; import type { AnnotationNodeData, AnnotationShape, BaseNodeData, } from "./annotation"; // Re-export types from annotation for convenience export type { AnnotationNodeData, BaseNodeData }; // Import from domain files to avoid circular dependencies import type { AspectRatio, Resolution, ModelType } from "./models"; import type { LLMProvider, LLMModelType, SelectedModel, ProviderType } from "./providers"; import type { MediaStorageStatus } from "@/utils/mediaResolver"; /** * All available node types in the workflow editor */ export type NodeType = | "imageInput" | "audioInput" | "videoInput" | "annotation" | "prompt" | "array" | "promptConstructor" | "nanoBanana" | "generateVideo" | "generateAudio" | "llmGenerate" | "splitGrid" | "output" | "outputGallery" | "imageCompare" | "videoStitch" | "easeCurve" | "videoTrim" | "videoFrameGrab" | "router" | "switch" | "conditionalSwitch" | "generate3d" | "glbViewer"; /** * Node execution status */ export type NodeStatus = "idle" | "loading" | "complete" | "error" | "skipped"; /** * Image input node - loads/uploads images into the workflow */ export interface ImageInputNodeData extends BaseNodeData { image: string | null; imageRef?: string; // External image reference for storage optimization filename: string | null; dimensions: { width: number; height: number } | null; isOptional?: boolean; } /** * Audio input node - loads/uploads audio files into the workflow */ export interface AudioInputNodeData extends BaseNodeData { audioFile: string | null; // Base64 data URL of the audio file audioFileRef?: string; // External audio reference for storage optimization filename: string | null; // Original filename for display duration: number | null; // Duration in seconds format: string | null; // MIME type (audio/mp3, audio/wav, etc.) isOptional?: boolean; } /** * Video input node - loads/uploads video files into the workflow */ export interface VideoInputNodeData extends BaseNodeData { video: string | null; // Base64 data URL or blob URL videoRef?: string; // External video reference for storage optimization filename: string | null; duration: number | null; // Duration in seconds dimensions: { width: number; height: number } | null; format: string | null; // MIME type (video/mp4, video/webm, etc.) isOptional?: boolean; } /** * Prompt node - text input for AI generation */ export interface PromptNodeData extends BaseNodeData { prompt: string; variableName?: string; // Optional variable name for use in PromptConstructor templates isOptional?: boolean; } export type ArraySplitMode = "delimiter" | "newline" | "regex"; /** * Array node - converts one text input into ordered text items. */ export interface ArrayNodeData extends BaseNodeData { inputText: string | null; splitMode: ArraySplitMode; delimiter: string; regexPattern: string; trimItems: boolean; removeEmpty: boolean; batchMode: boolean; // When true, all items are sent as a batch to downstream generate nodes selectedOutputIndex: number | null; outputItems: string[]; outputText: string | null; // JSON array string for the primary text output error: string | null; } /** * Prompt Constructor node - template-based prompt builder with @variable interpolation */ export interface PromptConstructorNodeData extends BaseNodeData { template: string; outputText: string | null; unresolvedVars: string[]; } /** * Available variable from connected Prompt nodes (for PromptConstructor autocomplete) */ export interface AvailableVariable { name: string; value: string; nodeId: string; } /** * Image history item for tracking generated images */ export interface ImageHistoryItem { id: string; image: string; // Base64 data URL timestamp: number; // For display & sorting prompt: string; // The prompt used aspectRatio: AspectRatio; model: string; modelDisplayName?: string; modelProvider?: ProviderType; } /** * Carousel image item for per-node history. */ export interface CarouselImageItem { id: string; image?: string; // Current-session fallback; stripped when externalizing workflow media timestamp: number; prompt: string; aspectRatio: AspectRatio; model: string; modelDisplayName?: string; modelProvider?: ProviderType; } /** * Carousel video item for per-node video history */ export interface CarouselVideoItem { id: string; video?: string; // Current-session fallback; stripped when externalizing workflow media timestamp: number; prompt: string; model: string; // Model ID for video (not ModelType since external providers) modelDisplayName?: string; modelProvider?: ProviderType; } /** * Model input definition for dynamic handles */ export interface ModelInputDef { name: string; type: "image" | "text" | "video" | "audio"; required: boolean; label: string; description?: string; } /** * Nano Banana node - AI image generation */ export type ImageGenerationCount = 1 | 2 | 4; export interface NanoBananaNodeData extends BaseNodeData { inputImages: string[]; // Now supports multiple images inputImageRefs?: string[]; // External image references for storage optimization inputPrompt: string | null; outputImage: string | null; outputImageRef?: string; // External image reference for storage optimization outputImageStorageStatus?: "remote-only" | "localizing" | "localized" | "missing"; aspectRatio: AspectRatio; resolution: Resolution; // Only used by Nano Banana Pro model: ModelType; selectedModel?: SelectedModel; // Multi-provider model selection (optional for backward compat) lastUsedModel?: SelectedModel; // The model that produced the current output imageCount?: ImageGenerationCount; // Number of candidate images to generate per run useGoogleSearch: boolean; // Only available for Nano Banana Pro and Nano Banana 2 useImageSearch: boolean; // Only available for Nano Banana 2 parameters?: Record; // Model-specific parameters for external providers fallbackParameters?: Record; // Parameters for fallback model inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles parametersExpanded?: boolean; // Collapse state for inline parameter display _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; imageHistory: CarouselImageItem[]; // Carousel history with refs plus current-session fallback selectedHistoryIndex: number; // Currently selected image in carousel fallbackModel?: SelectedModel; // JSON-compatible with Node Banana Pro __usedFallback?: boolean; // Set by runWithFallback on successful fallback __fallbackModelUsed?: string; // Display name of fallback model that succeeded __primaryError?: string; // Error message from the primary attempt } /** * Generate Video node - AI video generation */ export interface GenerateVideoNodeData extends BaseNodeData { inputImages: string[]; inputImageRefs?: string[]; // External image references for storage optimization inputPrompt: string | null; outputVideo: string | null; // Video data URL or URL outputVideoRef?: string; // External video reference for storage optimization outputVideoRemoteUrl?: string; // Original provider URL retained for fallback/re-download outputVideoRemoteExpiresAt?: string; // Best-effort expiry timestamp for signed provider URLs outputVideoStorageStatus?: MediaStorageStatus; // Local persistence state for provider URLs aspectRatio?: AspectRatio; resolution?: string; durationSeconds?: number; fps?: number; soundEnabled?: boolean; selectedModel?: SelectedModel; // Required for video generation (no legacy fallback) lastUsedModel?: SelectedModel; // The model that produced the current output parameters?: Record; // Model-specific parameters fallbackParameters?: Record; // Parameters for fallback model inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles parametersExpanded?: boolean; // Collapse state for inline parameter display _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; videoHistory: CarouselVideoItem[]; // Carousel history with refs plus current-session fallback selectedVideoHistoryIndex: number; // Currently selected video in carousel fallbackModel?: SelectedModel; // JSON-compatible with Node Banana Pro __usedFallback?: boolean; // Set by runWithFallback on successful fallback __fallbackModelUsed?: string; // Display name of fallback model that succeeded __primaryError?: string; // Error message from the primary attempt } /** * Generate 3D node - AI 3D model generation */ export interface Generate3DNodeData extends BaseNodeData { inputImages: string[]; inputImageRefs?: string[]; inputPrompt: string | null; output3dUrl: string | null; savedFilename: string | null; savedFilePath: string | null; selectedModel?: SelectedModel; parameters?: Record; fallbackParameters?: Record; // Parameters for fallback model inputSchema?: ModelInputDef[]; parametersExpanded?: boolean; // Collapse state for inline parameter display _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; fallbackModel?: SelectedModel; // JSON-compatible with Node Banana Pro __usedFallback?: boolean; // Set by runWithFallback on successful fallback __fallbackModelUsed?: string; // Display name of fallback model that succeeded __primaryError?: string; // Error message from the primary attempt } /** * Carousel audio item for per-node audio history */ export interface CarouselAudioItem { id: string; timestamp: number; prompt: string; model: string; // Model ID for audio (not ModelType since external providers) } /** * Generate Audio node - AI audio/TTS generation */ export interface GenerateAudioNodeData extends BaseNodeData { inputPrompt: string | null; outputAudio: string | null; // Audio data URL outputAudioRef?: string; // External audio reference for storage optimization selectedModel?: SelectedModel; // Required for audio generation parameters?: Record; // Model-specific parameters (voice, speed, etc.) fallbackParameters?: Record; // Parameters for fallback model inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles parametersExpanded?: boolean; // Collapse state for inline parameter display _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; audioHistory: CarouselAudioItem[]; // Carousel history (IDs only) selectedAudioHistoryIndex: number; // Currently selected audio in carousel duration: number | null; // Duration in seconds format: string | null; // MIME type (audio/mp3, audio/wav, etc.) fallbackModel?: SelectedModel; // JSON-compatible with Node Banana Pro __usedFallback?: boolean; // Set by runWithFallback on successful fallback __fallbackModelUsed?: string; // Display name of fallback model that succeeded __primaryError?: string; // Error message from the primary attempt } /** * LLM Generate node - AI text generation */ export interface LLMGenerateNodeData extends BaseNodeData { inputPrompt: string | null; inputImages: string[]; inputImageRefs?: string[]; // External image references for storage optimization inputVideos?: string[]; inputVideoRefs?: string[]; // External video references for storage optimization outputText: string | null; provider: LLMProvider; model: LLMModelType; temperature: number; maxTokens: number; fallbackParameters?: Record; // Parameters for fallback model (temperature, maxTokens) parametersExpanded?: boolean; // Collapse state for inline parameter display _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; fallbackModel?: SelectedModel; // JSON-compatible with Node Banana Pro __usedFallback?: boolean; // Set by runWithFallback on successful fallback __fallbackModelUsed?: string; // Display name of fallback model that succeeded __primaryError?: string; // Error message from the primary attempt } /** * Output node - displays final workflow results */ export interface OutputNodeData extends BaseNodeData { image: string | null; imageRef?: string; // External image reference for storage optimization video?: string | null; // Video data URL or HTTP URL audio?: string | null; // Audio data URL or HTTP URL model3d?: string | null; // 3D model URL/ref, usually GLB contentType?: "image" | "video" | "audio" | "3d"; // Explicit content type hint outputFilename?: string; // Custom filename for saved outputs (without extension) } /** * Output Gallery node - displays scrollable thumbnail grid of images with lightbox */ export interface OutputGalleryNodeData extends BaseNodeData { images: string[]; // Array of base64 data URLs from connected nodes imageRefs?: string[]; // External storage refs for images videos?: string[]; // Array of video URLs from connected nodes videoRefs?: string[]; // External storage refs for videos } /** * Image Compare node - side-by-side image comparison with draggable slider */ export interface ImageCompareNodeData extends BaseNodeData { imageA: string | null; imageARef?: string; // External image reference for storage optimization imageB: string | null; imageBRef?: string; // External image reference for storage optimization } /** * Video stitch clip - represents a single video clip in the filmstrip */ export interface VideoStitchClip { edgeId: string; // Edge ID for disconnect capability sourceNodeId: string; // Source node producing this video thumbnail: string | null; // Base64 JPEG thumbnail duration: number | null; // Clip duration in seconds handleId: string; // Which input handle (video-0, video-1, etc.) } /** * Video Stitch node - concatenates multiple videos into a single output */ export interface VideoStitchNodeData extends BaseNodeData { clips: VideoStitchClip[]; // Ordered clip sequence for filmstrip clipOrder: string[]; // Edge IDs in user-defined order (drag reorder) outputVideo: string | null; // Stitched video blob URL or data URL loopCount: 1 | 2 | 3; // How many times to repeat the clip sequence (1 = no loop) status: NodeStatus; error: string | null; progress: number; // 0-100 processing progress encoderSupported: boolean | null; // null = not checked yet, true/false after check } /** * Ease Curve node - applies speed curve to video using easing functions */ export interface EaseCurveNodeData extends BaseNodeData { bezierHandles: [number, number, number, number]; easingPreset: string | null; inheritedFrom: string | null; outputDuration: number; outputVideo: string | null; status: NodeStatus; error: string | null; progress: number; encoderSupported: boolean | null; } /** * Video Trim node - trims a video clip to a user-defined start/end time range */ export interface VideoTrimNodeData extends BaseNodeData { startTime: number; // Trim start in seconds (default 0) endTime: number; // Trim end in seconds (default 0 = full duration, set on video load) duration: number | null; // Source video duration (populated when video loads metadata) outputVideo: string | null; // Trimmed video blob URL or data URL status: NodeStatus; error: string | null; progress: number; // 0-100 processing progress 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; } /** * Router node - pure passthrough routing node with dynamic multi-type handles */ export interface RouterNodeData extends BaseNodeData { // No internal state - all routing is derived from edge connections } /** * Switch node - toggle-controlled routing with named outputs */ export interface SwitchNodeData extends BaseNodeData { inputType: HandleType | null; // Derived from connected input edge, null when disconnected switches: Array<{ id: string; // Unique identifier for handle mapping name: string; // User-editable label enabled: boolean; // Toggle state }>; } /** * Match mode for conditional switch rules */ export type MatchMode = "exact" | "contains" | "starts-with" | "ends-with"; /** * Conditional switch rule for text-based routing */ export interface ConditionalSwitchRule { id: string; // Unique handle ID, prefixed with "rule-" to avoid collision with reserved "default" keyword value: string; // Comma-separated match values mode: MatchMode; // Match strategy label: string; // User-editable display name isMatched: boolean; // Computed match state } /** * Conditional Switch node - text-based routing with multi-mode matching */ export interface ConditionalSwitchNodeData extends BaseNodeData { incomingText: string | null; // Upstream text for evaluation and display rules: ConditionalSwitchRule[]; // User-defined rules evaluationPaused?: boolean; // When true, skips rule evaluation and downstream dimming } /** * Split Grid node - splits image into grid cells for parallel processing */ export interface SplitGridNodeData extends BaseNodeData { sourceImage: string | null; sourceImageRef?: string; // External image reference for storage optimization targetCount: number; // 4, 6, 8, 9, or 10 defaultPrompt: string; generateSettings: { aspectRatio: AspectRatio; resolution: Resolution; model: ModelType; useGoogleSearch: boolean; useImageSearch: boolean; }; childNodeIds: Array<{ imageInput: string; prompt: string; nanoBanana: string; }>; gridRows: number; gridCols: number; isConfigured: boolean; status: NodeStatus; error: string | null; } /** * GLB 3D Viewer node - loads and displays 3D models, captures viewport as image */ export interface GLBViewerNodeData extends BaseNodeData { glbUrl: string | null; // Object URL for the loaded GLB file filename: string | null; // Original filename for display capturedImage: string | null; // Base64 PNG snapshot of the 3D viewport capturedImageRef?: string; // External image reference for storage optimization } /** * Union of all node data types */ export type WorkflowNodeData = | ImageInputNodeData | AudioInputNodeData | VideoInputNodeData | AnnotationNodeData | PromptNodeData | ArrayNodeData | PromptConstructorNodeData | NanoBananaNodeData | GenerateVideoNodeData | Generate3DNodeData | GenerateAudioNodeData | LLMGenerateNodeData | SplitGridNodeData | OutputNodeData | OutputGalleryNodeData | ImageCompareNodeData | VideoStitchNodeData | EaseCurveNodeData | VideoTrimNodeData | VideoFrameGrabNodeData | RouterNodeData | SwitchNodeData | ConditionalSwitchNodeData | GLBViewerNodeData; /** * Workflow node with typed data (extended with optional groupId) */ export type WorkflowNode = Node & { groupId?: string; }; /** * Handle types for node connections */ export type HandleType = "image" | "text" | "audio" | "video" | "3d" | "easeCurve"; /** * Default settings for node types - stored in localStorage */ export interface GenerateImageNodeDefaults { selectedModel?: { provider: ProviderType; modelId: string; displayName: string; }; aspectRatio?: string; resolution?: string; useGoogleSearch?: boolean; useImageSearch?: boolean; } export interface GenerateVideoNodeDefaults { selectedModel?: { provider: ProviderType; modelId: string; displayName: string; }; resolution?: string; durationSeconds?: number; soundEnabled?: boolean; } export interface Generate3DNodeDefaults { selectedModel?: { provider: ProviderType; modelId: string; displayName: string; }; } export interface GenerateAudioNodeDefaults { selectedModel?: { provider: ProviderType; modelId: string; displayName: string; }; } export interface LLMNodeDefaults { provider?: LLMProvider; model?: LLMModelType; temperature?: number; maxTokens?: number; } export interface NodeDefaultsConfig { generateImage?: GenerateImageNodeDefaults; generateVideo?: GenerateVideoNodeDefaults; generate3d?: Generate3DNodeDefaults; generateAudio?: GenerateAudioNodeDefaults; llm?: LLMNodeDefaults; }