diff --git a/prompt_manager.py b/prompt_manager.py index 6fb19a1..f54848c 100644 --- a/prompt_manager.py +++ b/prompt_manager.py @@ -92,6 +92,12 @@ class PromptManager(ComfyNodeABC): "search_text": (IO.STRING, { "default": "", "tooltip": "Search for past prompts containing this text" + }), + "prepend_text": (IO.STRING, { + "tooltip": "Text to prepend to the main prompt (connected STRING nodes will be added before the main text)" + }), + "append_text": (IO.STRING, { + "tooltip": "Text to append to the main prompt (connected STRING nodes will be added after the main text)" }) } } @@ -114,7 +120,9 @@ class PromptManager(ComfyNodeABC): text: str, category: str = "", tags: str = "", - search_text: str = "" + search_text: str = "", + prepend_text: str = "", + append_text: str = "" ) -> Tuple[Any]: """ Encode the text prompt and save it to the database. @@ -125,6 +133,8 @@ class PromptManager(ComfyNodeABC): category: Optional category for organization tags: Comma-separated tags search_text: Text to search for in past prompts + prepend_text: Text to prepend to the main prompt + append_text: Text to append to the main prompt Returns: Tuple containing the conditioning for the diffusion model @@ -132,6 +142,20 @@ class PromptManager(ComfyNodeABC): Raises: RuntimeError: If clip input is invalid """ + # Combine prepend, main text, and append text + final_text = "" + if prepend_text and prepend_text.strip(): + final_text += prepend_text.strip() + " " + final_text += text + if append_text and append_text.strip(): + final_text += " " + append_text.strip() + + # Use the combined text for encoding + encoding_text = final_text + + # For database storage, save the original main text with metadata about prepend/append + storage_text = text + # Search functionality is now handled by the JavaScript UI # The search parameters are still available for backend processing if needed @@ -147,23 +171,33 @@ class PromptManager(ComfyNodeABC): # Save prompt to database and set execution context for gallery tracking prompt_id = None - if text and text.strip(): - self.logger.debug(f"Processing prompt text: {text[:100]}...") + if storage_text and storage_text.strip(): + self.logger.debug(f"Processing prompt text: {storage_text[:100]}...") + + # Add prepend/append info to tags if they exist + extended_tags = self._parse_tags(tags) or [] + if prepend_text and prepend_text.strip(): + extended_tags.append(f"prepend:{prepend_text.strip()[:50]}") + if append_text and append_text.strip(): + extended_tags.append(f"append:{append_text.strip()[:50]}") + try: prompt_id = self._save_prompt_to_database( - text=text.strip(), # Always strip whitespace + text=storage_text.strip(), # Always strip whitespace category=category.strip() if category else None, - tags=self._parse_tags(tags) + tags=extended_tags if extended_tags else None ) # Set current prompt for image tracking if prompt_id: execution_id = self.prompt_tracker.set_current_prompt( - prompt_text=text.strip(), + prompt_text=encoding_text.strip(), # Use final combined text for tracking additional_data={ 'category': category.strip() if category else None, - 'tags': self._parse_tags(tags), - 'prompt_id': prompt_id + 'tags': extended_tags, + 'prompt_id': prompt_id, + 'prepend_text': prepend_text.strip() if prepend_text else None, + 'append_text': append_text.strip() if append_text else None } ) self.logger.info(f"Set execution context: {execution_id} for prompt ID: {prompt_id}") @@ -173,9 +207,9 @@ class PromptManager(ComfyNodeABC): self.logger.warning(f"Failed to save prompt to database: {e}") # Already logged above, no need for additional print - # Perform standard CLIP text encoding - self.logger.debug("Performing CLIP text encoding") - tokens = clip.tokenize(text) + # Perform standard CLIP text encoding using the combined text + self.logger.debug(f"Performing CLIP text encoding on combined text: {encoding_text[:100]}...") + tokens = clip.tokenize(encoding_text) conditioning = clip.encode_from_tokens_scheduled(tokens) self.logger.debug("CLIP encoding completed successfully") diff --git a/web/admin.html b/web/admin.html index 7c22d44..744fef1 100644 --- a/web/admin.html +++ b/web/admin.html @@ -2843,7 +2843,9 @@ this.updateMetadataPanel(comfyData, imageSrc); } catch (error) { console.error('Error extracting metadata:', error); - this.showMetadataError(); + + // Try fallback: get prompt from database using current image + await this.tryFallbackMetadata(imageSrc); } } @@ -3129,12 +3131,82 @@ } async copyPrompt(type) { - if (!this.currentMetadata) return; + console.log('copyPrompt called with type:', type); + console.log('currentMetadata:', this.currentMetadata); + + if (!this.currentMetadata) { + console.error('No metadata available for copying'); + this.showNotification('❌ No metadata available for copying', 'error'); + return; + } const text = type === 'positive' ? this.currentMetadata.positivePrompt : this.currentMetadata.negativePrompt; + console.log('Copying text:', text?.substring(0, 100)); + + if (!text || text === 'No prompt found' || text === 'No negative prompt found') { + this.showNotification(`❌ No ${type} prompt available`, 'error'); + return; + } + await this.copyToClipboard(text); } + async tryFallbackMetadata(imageSrc) { + console.log('Trying fallback metadata extraction for:', imageSrc); + + try { + // Try to get prompt from current gallery image data + let fallbackPrompt = 'No prompt found'; + + if (this.currentGalleryImages && this.currentImageIndex !== null && this.currentGalleryImages[this.currentImageIndex]) { + const currentImage = this.currentGalleryImages[this.currentImageIndex]; + console.log('Current image data:', currentImage); + + // If we have prompt_id, try to get the prompt from our local prompts array + if (currentImage.prompt_id && this.prompts) { + const prompt = this.prompts.find(p => p.id === currentImage.prompt_id); + if (prompt) { + fallbackPrompt = prompt.text; + console.log('Found prompt from database:', fallbackPrompt.substring(0, 100)); + } + } + } + + // Set fallback metadata + this.currentMetadata = { + positivePrompt: fallbackPrompt, + negativePrompt: 'No negative prompt found', + checkpoint: 'Unknown', + steps: 'Unknown', + cfgScale: 'Unknown', + sampler: 'Unknown', + seed: 'Unknown', + workflow: null, + prompt: null + }; + + // Update metadata panel with fallback data + this.updateMetadataPanel({}, imageSrc); + + } catch (error) { + console.error('Fallback metadata extraction failed:', error); + this.showMetadataError(); + + // Set empty metadata as last resort + this.currentMetadata = { + positivePrompt: 'No prompt found', + negativePrompt: 'No negative prompt found', + checkpoint: 'Unknown', + steps: 'Unknown', + cfgScale: 'Unknown', + sampler: 'Unknown', + seed: 'Unknown', + workflow: null, + prompt: null + }; + } + } + async copyAllMetadata() { if (!this.currentMetadata) return; diff --git a/web/metadata.html b/web/metadata.html new file mode 100644 index 0000000..c8479b0 --- /dev/null +++ b/web/metadata.html @@ -0,0 +1,558 @@ + + +
+ + +Drag & Drop PNG with ComfyUI Workflow
+Or click to browse files
+No Image Loaded
+Upload a PNG with embedded ComfyUI workflow to view generation data
+