diff --git a/js/floating_window.css b/js/floating_window.css index 5d5183e..c59bfb2 100644 --- a/js/floating_window.css +++ b/js/floating_window.css @@ -55,11 +55,20 @@ /* Image Grid */ .nf-image-grid { display: grid; + grid-template-columns: repeat(auto-fit, minmax(var(--min-column-width, 150px), 1fr)); gap: 8px; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); flex: 1; /* Take up remaining space */ overflow-y: auto; min-height: 0; /* Allow shrinking */ + align-items: start; +} + +/* Smart grid layout that adapts to content and window size */ +.nf-image-grid.smart-grid-layout { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(var(--min-column-width, 150px), 1fr)); + gap: 8px; + align-items: start; } .nf-image-container { @@ -70,6 +79,12 @@ transition: border-color 0.2s; } +/* Responsive image containers */ +.nf-image-grid.smart-grid-layout .nf-image-container { + width: 100%; + max-width: 100%; +} + .nf-image-container:hover { border-color: #70a0d0; } diff --git a/js/preview_dialog.js b/js/preview_dialog.js index 7e2160c..429f04c 100644 --- a/js/preview_dialog.js +++ b/js/preview_dialog.js @@ -22,6 +22,9 @@ export class NFPreviewDialog { // Apply max width setting this.applyMaxWidthSetting(); + + // Setup Queue Prompt keybinding integration + this.setupQueuePromptIntegration(); } loadCSS() { @@ -57,6 +60,55 @@ export class NFPreviewDialog { document.head.appendChild(style); } + setupQueuePromptIntegration() { + // Listen for Queue Prompt keybinding events + document.addEventListener('keydown', (event) => { + // Check if it's Ctrl+Enter (Queue Prompt) or Ctrl+Shift+Enter (Queue Prompt to Front) + if (event.ctrlKey && event.key === 'Enter') { + // Only handle if preview dialog is visible + if (this.isVisible()) { + console.log('NF Preview Dialog: Queue Prompt keybinding detected - stopping dialog and allowing queue to proceed'); + + // Stop the dialog processing immediately + this.stopDialogForQueue(); + + // Allow the event to propagate to ComfyUI's queue system + // Do not prevent default - let ComfyUI handle the queue + } + } + }); + + // Also listen for programmatic queue events via API + api.addEventListener("status", (event) => { + const status = event.detail; + + // If queue is starting and dialog is visible, stop the dialog + if (status && status.exec_info && status.exec_info.queue_remaining > 0) { + if (this.isVisible()) { + console.log('NF Preview Dialog: Queue started programmatically - stopping dialog'); + this.stopDialogForQueue(); + } + } + }); + } + + stopDialogForQueue() { + // Stop countdown immediately + this.stopCountdown(); + + console.log('NF Preview Dialog: Stopping dialog processing for queue'); + + // Send cancellation response to backend (empty selection, cancelled=true) + this.sendSelection([], true); + + // Hide the dialog + this.hide(); + } + + isVisible() { + return this.window && !this.window.classList.contains('hidden'); + } + show(data) { console.log("NF Preview Dialog: Showing review dialog", data); @@ -177,6 +229,11 @@ export class NFPreviewDialog { renderImages() { this.imageGrid.innerHTML = ''; + // Counter for loaded images to determine layout + let loadedImages = 0; + let landscapeCount = 0; + let portraitCount = 0; + this.images.forEach((imageInfo, index) => { const container = document.createElement('div'); container.className = 'nf-image-container'; @@ -188,6 +245,23 @@ export class NFPreviewDialog { img.src = this.getImageUrl(imageInfo); img.alt = `Image ${index + 1}`; + // Check image aspect ratio when loaded + img.onload = () => { + loadedImages++; + + const aspectRatio = img.naturalWidth / img.naturalHeight; + if (aspectRatio > 1.1) { // Landscape (wider than tall) + landscapeCount++; + } else if (aspectRatio < 0.9) { // Portrait (taller than wide) + portraitCount++; + } + + // When all images are loaded, determine layout + if (loadedImages === this.images.length) { + this.applyImageLayout(landscapeCount, portraitCount); + } + }; + const overlay = document.createElement('div'); overlay.className = 'nf-image-overlay'; overlay.textContent = index + 1; @@ -198,6 +272,27 @@ export class NFPreviewDialog { }); } + applyImageLayout(landscapeCount, portraitCount) { + // Always use smart grid layout that adapts to window size + this.imageGrid.className = 'nf-image-grid smart-grid-layout'; + + // Set CSS custom properties for dynamic grid sizing + const dialogWidth = this.window.offsetWidth - 48; // Account for padding + const isLandscapeDominant = landscapeCount > portraitCount; + + if (isLandscapeDominant) { + // For landscape images, use slightly wider columns but still allow multiple columns + const minColumnWidth = Math.max(200, Math.min(300, dialogWidth / 2)); // 200-300px, max 2 columns typically + this.imageGrid.style.setProperty('--min-column-width', `${minColumnWidth}px`); + console.log('NF Preview Dialog: Using smart grid layout optimized for landscape images, min width:', minColumnWidth); + } else { + // For portrait/square images, use standard grid sizing + const minColumnWidth = Math.max(120, Math.min(200, dialogWidth / 3)); // 120-200px, allow up to 3-4 columns + this.imageGrid.style.setProperty('--min-column-width', `${minColumnWidth}px`); + console.log('NF Preview Dialog: Using smart grid layout optimized for portrait/square images, min width:', minColumnWidth); + } + } + getImageUrl(imageInfo) { return `/view?filename=${encodeURIComponent(imageInfo.filename)}&type=${imageInfo.type}&subfolder=${encodeURIComponent(imageInfo.subfolder || '')}`; } @@ -397,6 +492,28 @@ export class NFPreviewDialog { if (rememberPosition) { this.saveSize(width, height); } + + // Recalculate grid layout for new window size + if (this.imageGrid && this.imageGrid.children.length > 0) { + // Get current aspect ratio distribution + let landscapeCount = 0; + let portraitCount = 0; + + for (let container of this.imageGrid.children) { + const img = container.querySelector('img'); + if (img && img.naturalWidth && img.naturalHeight) { + const aspectRatio = img.naturalWidth / img.naturalHeight; + if (aspectRatio > 1.1) { + landscapeCount++; + } else if (aspectRatio < 0.9) { + portraitCount++; + } + } + } + + this.applyImageLayout(landscapeCount, portraitCount); + } + console.log("NF Preview Dialog: Window resized to", width, height); } }