diff --git a/src/components/nodes/ImageInputNode.tsx b/src/components/nodes/ImageInputNode.tsx index fb75f778..99d7b250 100644 --- a/src/components/nodes/ImageInputNode.tsx +++ b/src/components/nodes/ImageInputNode.tsx @@ -84,13 +84,8 @@ export function ImageInputNode({ id, data, selected }: NodeProps updateNodeData(id, { customTitle: title || undefined })} - onCommentChange={(comment) => updateNodeData(id, { comment: comment || undefined })} selected={selected} - commentNavigation={commentNavigation ?? undefined} + contentClassName="flex-1 min-h-0 overflow-clip" > {/* Reference input handle for visual links from Split Grid node */} {nodeData.image ? ( -
+
{nodeData.filename -
- - {nodeData.filename} - - {nodeData.dimensions && ( - - {nodeData.dimensions.width}x{nodeData.dimensions.height} - - )} -
) : (
fileInputRef.current?.click()} onDrop={handleDrop} onDragOver={handleDragOver} - className="w-full flex-1 min-h-[112px] border border-dashed border-neutral-600 rounded flex flex-col items-center justify-center cursor-pointer hover:border-neutral-500 hover:bg-neutral-700/50 transition-colors" + className="w-full h-full bg-neutral-900/40 flex flex-col items-center justify-center cursor-pointer hover:bg-neutral-900/60 transition-colors" > - - + + - - Drop or click - + Drop image
)} diff --git a/src/components/nodes/OutputNode.tsx b/src/components/nodes/OutputNode.tsx index 83bc1c97..021c909b 100644 --- a/src/components/nodes/OutputNode.tsx +++ b/src/components/nodes/OutputNode.tsx @@ -110,16 +110,10 @@ export function OutputNode({ id, data, selected }: NodeProps) { <> updateNodeData(id, { customTitle: title || undefined })} - onCommentChange={(comment) => updateNodeData(id, { comment: comment || undefined })} - onRun={handleRun} - isExecuting={isRunning} selected={selected} + isExecuting={isRunning} + contentClassName="flex-1 min-h-0 overflow-clip relative" className="min-w-[200px]" - commentNavigation={commentNavigation ?? undefined} > ) { /> {contentSrc ? ( -
+ <> {isAudio ? ( -
+
) : (
setShowLightbox(true)} > {isVideo ? ( @@ -158,17 +152,17 @@ export function OutputNode({ id, data, selected }: NodeProps) { muted autoPlay playsInline - className="w-full h-full object-contain rounded" + className="w-full h-full object-cover" onClick={(e) => e.stopPropagation()} /> ) : ( Output )} -
+
View full size @@ -177,27 +171,22 @@ export function OutputNode({ id, data, selected }: NodeProps) { )} -
+ ) : ( -
- Waiting for image, video, or audio +
+ + + + Connect input
)} - - {/* Filename input */} -
- updateNodeData(id, { outputFilename: e.target.value })} - placeholder="Output filename (optional)" - className="nodrag nopan w-full px-2 py-1.5 text-[10px] bg-neutral-900/50 border border-neutral-700 rounded text-neutral-200 placeholder:text-neutral-500 focus:outline-none focus:ring-1 focus:ring-neutral-600" - /> -
{/* Lightbox Modal (skip for audio) */} diff --git a/src/utils/nodeDimensions.ts b/src/utils/nodeDimensions.ts index 89f6b3aa..1521dc9d 100644 --- a/src/utils/nodeDimensions.ts +++ b/src/utils/nodeDimensions.ts @@ -166,11 +166,13 @@ export function calculateNodeSize( * * @param aspectRatio - Width divided by height of the content * @param currentHeight - The node's current height (if manually set) + * @param skipChromeOffset - If true, skip subtracting NODE_CHROME_HEIGHT (for full-bleed nodes with floating headers) * @returns {width, height} dimensions that preserve height when possible */ export function calculateNodeSizePreservingHeight( aspectRatio: number, - currentHeight?: number + currentHeight?: number, + skipChromeOffset: boolean = false ): { width: number; height: number } { // Handle invalid aspect ratios if (!aspectRatio || aspectRatio <= 0 || !isFinite(aspectRatio)) { @@ -179,11 +181,12 @@ export function calculateNodeSizePreservingHeight( // No current height or below minimum = use default behavior if (!currentHeight || currentHeight < MIN_HEIGHT) { - return calculateNodeSize(aspectRatio); + return skipChromeOffset ? calculateNodeSizeForFullBleed(aspectRatio) : calculateNodeSize(aspectRatio); } // Preserve height, calculate width to maintain aspect ratio - const contentHeight = currentHeight - NODE_CHROME_HEIGHT; + const chromeHeight = skipChromeOffset ? 0 : NODE_CHROME_HEIGHT; + const contentHeight = currentHeight - chromeHeight; let newWidth = contentHeight * aspectRatio; // Clamp width to constraints @@ -194,3 +197,67 @@ export function calculateNodeSizePreservingHeight( height: Math.round(currentHeight), }; } + +/** + * Calculate node dimensions for full-bleed content (no header chrome). + * Used by nodes with floating headers where content fills the entire node area. + * + * @param aspectRatio - Width divided by height (e.g., 16/9 for landscape, 9/16 for portrait) + * @param currentHeight - Optional current height to preserve (if manually resized) + * @returns {width, height} dimensions that fit within constraints + */ +export function calculateNodeSizeForFullBleed( + aspectRatio: number, + currentHeight?: number +): { width: number; height: number } { + // Handle invalid aspect ratios + if (!aspectRatio || aspectRatio <= 0 || !isFinite(aspectRatio)) { + return { width: 300, height: 300 }; // Return default square + } + + // If preserving height, calculate width + if (currentHeight && currentHeight >= MIN_HEIGHT) { + let width = currentHeight * aspectRatio; + width = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, width)); + return { + width: Math.round(width), + height: Math.round(currentHeight), + }; + } + + // Start with base width and calculate height + let width = 300; // Default starting width + let height = width / aspectRatio; + + // Check if height exceeds max - if so, scale down width to fit + if (height > MAX_HEIGHT) { + height = MAX_HEIGHT; + width = height * aspectRatio; + } + + // Check if height is below min - if so, scale up width to fit + if (height < MIN_HEIGHT) { + height = MIN_HEIGHT; + width = height * aspectRatio; + } + + // Clamp width to constraints + if (width > MAX_WIDTH) { + width = MAX_WIDTH; + height = width / aspectRatio; + // Re-clamp height + height = Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, height)); + } + + if (width < MIN_WIDTH) { + width = MIN_WIDTH; + height = width / aspectRatio; + // Re-clamp height + height = Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, height)); + } + + return { + width: Math.round(width), + height: Math.round(height), + }; +}