From 53f4f3df1df254d009a413425aee1c6a10bb78df Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 5 Jun 2025 13:13:33 -0700 Subject: [PATCH 1/7] feat: Add PromptManagerText class for text management --- prompt_manager_text.py | 355 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 prompt_manager_text.py diff --git a/prompt_manager_text.py b/prompt_manager_text.py new file mode 100644 index 0000000..c317aa2 --- /dev/null +++ b/prompt_manager_text.py @@ -0,0 +1,355 @@ +""" +PromptManagerText: A text-only version of PromptManager that outputs STRING +without CLIP encoding, while maintaining all database and search features. +""" + +import hashlib +import datetime +import json +import webbrowser +import os +from typing import Optional, Dict, Any, Tuple, List + +# Import logging system +try: + from .utils.logging_config import get_logger +except ImportError: + import sys + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + from utils.logging_config import get_logger + +try: + from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict +except ImportError: + # Fallback for older ComfyUI versions + class ComfyNodeABC: + pass + + class IO: + STRING = "STRING" + CLIP = "CLIP" + CONDITIONING = "CONDITIONING" + + InputTypeDict = dict + +try: + from .database.operations import PromptDatabase + from .utils.prompt_tracker import PromptTracker, PromptExecutionContext + from .utils.image_monitor import ImageMonitor +except ImportError: + # For direct imports when not in a package + import sys + import os + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + from database.operations import PromptDatabase + from utils.prompt_tracker import PromptTracker, PromptExecutionContext + from utils.image_monitor import ImageMonitor + + +class PromptManagerText(ComfyNodeABC): + """ + A ComfyUI custom node that provides all PromptManager features but outputs + only a STRING without CLIP encoding. Includes: + - Persistent storage of all prompts in SQLite database + - Search and retrieval capabilities + - Metadata management (categories, tags, ratings, notes) + - Duplicate detection via SHA256 hashing + - Text concatenation with prepend/append functionality + """ + + def __init__(self): + self.logger = get_logger('prompt_manager_text.node') + self.logger.info("Initializing PromptManagerText node") + + self.db = PromptDatabase() + self.prompt_tracker = PromptTracker(self.db) + self.image_monitor = ImageMonitor(self.db, self.prompt_tracker) + + # Start image monitoring automatically + self._start_gallery_system() + self.logger.info("PromptManagerText node initialization completed") + + @classmethod + def INPUT_TYPES(cls) -> InputTypeDict: + return { + "required": { + "text": (IO.STRING, { + "multiline": True, + "dynamicPrompts": True, + "tooltip": "The text prompt to be processed and saved to database." + }) + }, + "optional": { + "category": (IO.STRING, { + "default": "", + "tooltip": "Optional category for organizing prompts (e.g., 'landscapes', 'portraits')" + }), + "tags": (IO.STRING, { + "default": "", + "tooltip": "Comma-separated tags for the prompt (e.g., 'anime, detailed, sunset')" + }), + "search_text": (IO.STRING, { + "default": "", + "tooltip": "Search for past prompts containing this text" + }), + "prepend_text": (IO.STRING, { + "default": "", + "tooltip": "Text to prepend to the main prompt (connected STRING nodes will be added before the main text)" + }), + "append_text": (IO.STRING, { + "default": "", + "tooltip": "Text to append to the main prompt (connected STRING nodes will be added after the main text)" + }) + } + } + + RETURN_TYPES = (IO.STRING,) + OUTPUT_TOOLTIPS = ( + "The final combined text string (with prepend/append applied) ready for use in other nodes.", + ) + FUNCTION = "process_text" + CATEGORY = "PromptManager/Text" + DESCRIPTION = ( + "Processes and manages text prompts with database storage and search capabilities. " + "Outputs a plain STRING that can be used with any node that accepts text input. " + "Includes all PromptManager features: categorization, tagging, search, and prepend/append functionality." + ) + + def process_text( + self, + text: str, + category: str = "", + tags: str = "", + search_text: str = "", + prepend_text: str = "", + append_text: str = "" + ) -> Tuple[str]: + """ + Process the text prompt and save it to the database. + + Args: + text: The text prompt to process + 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 final processed text string + """ + # 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() + + # 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 + + # Save prompt to database and set execution context for gallery tracking + prompt_id = None + 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=storage_text.strip(), # Always strip whitespace + category=category.strip() if category else None, + 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=final_text.strip(), # Use final combined text for tracking + additional_data={ + 'category': category.strip() if category else None, + '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}") + + except Exception as e: + # Log error but don't fail the processing + self.logger.warning(f"Failed to save prompt to database: {e}") + + self.logger.debug(f"Text processing completed: {final_text[:100]}...") + return (final_text,) + + def _save_prompt_to_database( + self, + text: str, + category: Optional[str] = None, + tags: Optional[list] = None + ) -> Optional[int]: + """ + Save the prompt to the SQLite database. + + Args: + text: The prompt text + category: Optional category + tags: List of tags + + Returns: + The prompt ID if saved successfully, None otherwise + """ + try: + # Generate hash for duplicate detection + prompt_hash = self._generate_hash(text) + self.logger.debug(f"Generated hash for prompt: {prompt_hash[:16]}...") + + # Check if prompt already exists + existing = self.db.get_prompt_by_hash(prompt_hash) + if existing: + self.logger.info(f"Found existing prompt with ID {existing['id']}, updating metadata") + # Update metadata if this is a duplicate with new info + if any([category, tags]): + self.db.update_prompt_metadata( + prompt_id=existing['id'], + category=category, + tags=tags + ) + self.logger.debug("Updated metadata for existing prompt") + return existing['id'] + + # Save new prompt + self.logger.debug(f"Saving new prompt with category: {category}, tags: {tags}") + prompt_id = self.db.save_prompt( + text=text, + category=category, + tags=tags, + prompt_hash=prompt_hash + ) + + if prompt_id: + self.logger.info(f"Successfully saved new prompt with ID: {prompt_id}") + else: + self.logger.warning("Failed to save prompt - no ID returned") + + return prompt_id + + except Exception as e: + self.logger.error(f"Error saving prompt to database: {e}") + return None + + def _generate_hash(self, text: str) -> str: + """Generate SHA256 hash for the prompt text.""" + # Normalize text for consistent hashing (strip whitespace, normalize case) + normalized_text = text.strip().lower() + return hashlib.sha256(normalized_text.encode('utf-8')).hexdigest() + + def _parse_tags(self, tags_string: str) -> Optional[list]: + """Parse comma-separated tags string into a list.""" + if not tags_string or not tags_string.strip(): + return None + + tags = [tag.strip() for tag in tags_string.split(',') if tag.strip()] + return tags if tags else None + + def _search_prompts(self, search_text: str = "") -> List[Dict[str, Any]]: + """Search for past prompts by text content.""" + try: + if not search_text or not search_text.strip(): + return [] + + results = self.db.search_prompts( + text=search_text.strip(), + category=None, + tags=None, + rating_min=None, + limit=50 + ) + + return results + + except Exception as e: + self.logger.error(f"Error searching prompts: {e}") + return [] + + def _open_web_interface(self): + """Open the web interface in the default browser.""" + try: + # Look for a web interface directory + current_dir = os.path.dirname(os.path.abspath(__file__)) + web_dir = os.path.join(current_dir, "web_interface") + + if os.path.exists(web_dir): + # If web interface exists, try to start it + index_path = os.path.join(web_dir, "index.html") + if os.path.exists(index_path): + webbrowser.open(f"file://{index_path}") + self.logger.info("Web interface opened in browser") + else: + self.logger.warning(f"Web interface directory found but no index.html. Please check {web_dir} for setup instructions") + else: + self.logger.info("Web interface not yet implemented. This feature will open a web-based prompt management interface when the web_interface directory is created.") + + except Exception as e: + self.logger.error(f"Error opening web interface: {e}") + + def search_prompts_api(self, search_text: str = "") -> List[Dict[str, Any]]: + """API method for JavaScript UI to search prompts.""" + return self._search_prompts(search_text=search_text) + + def get_recent_prompts_api(self, limit: int = 20) -> List[Dict[str, Any]]: + """API method for JavaScript UI to get recent prompts.""" + try: + return self.db.get_recent_prompts(limit=limit) + except Exception as e: + self.logger.error(f"Error getting recent prompts: {e}") + return [] + + def _start_gallery_system(self): + """Initialize and start the gallery monitoring system.""" + try: + self.logger.info("Starting gallery system...") + + # Start image monitoring + self.image_monitor.start_monitoring() + + self.logger.info("Gallery system started successfully") + + except Exception as e: + self.logger.error(f"Failed to start gallery system: {e}") + self.logger.warning("Gallery features will be disabled") + + def get_gallery_status(self) -> Dict[str, Any]: + """Get status of the gallery system.""" + return { + 'image_monitor': self.image_monitor.get_status(), + 'prompt_tracker': self.prompt_tracker.get_status() + } + + def cleanup_gallery_system(self): + """Clean up gallery system resources.""" + try: + if hasattr(self, 'image_monitor'): + self.image_monitor.stop_monitoring() + self.logger.info("Gallery system cleaned up") + except Exception as e: + self.logger.error(f"Error cleaning up gallery system: {e}") + + def __del__(self): + """Cleanup when object is destroyed.""" + self.cleanup_gallery_system() + + @classmethod + def IS_CHANGED(cls, **kwargs): + """Always process to ensure database saving.""" + return float("NaN") # Always execute \ No newline at end of file From 6c7353205539637aedc59a49ac5f0e8ff5a2491c Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 5 Jun 2025 13:13:53 -0700 Subject: [PATCH 2/7] feat(prompt_manager): support PromptManagerText nodes --- web/prompt_manager.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/prompt_manager.js b/web/prompt_manager.js index c8d422e..3c8cb09 100644 --- a/web/prompt_manager.js +++ b/web/prompt_manager.js @@ -6,8 +6,8 @@ app.registerExtension({ name: "PromptManager.UI", async beforeRegisterNodeDef(nodeType, nodeData, app) { - if (nodeData.name === "PromptManager") { - console.log("[PromptManager] Patching node type for custom UI"); + if (nodeData.name === "PromptManager" || nodeData.name === "PromptManagerText") { + console.log(`[PromptManager] Patching node type for custom UI: ${nodeData.name}`); // Store original methods const onNodeCreated = nodeType.prototype.onNodeCreated; @@ -28,6 +28,9 @@ app.registerExtension({ this._searchResults = []; this._selectedPromptIndex = -1; this.resultHideTimer = null; + + // Store node type for conditional behavior + this.nodeTypeName = nodeData.name; // Load settings from API this.loadSettings(); From 38c206c59bffcd235566d61a2992659f61a2e84d Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 5 Jun 2025 13:14:32 -0700 Subject: [PATCH 3/7] feat(prompt_manager): add string return type and output --- prompt_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/prompt_manager.py b/prompt_manager.py index f54848c..192a76d 100644 --- a/prompt_manager.py +++ b/prompt_manager.py @@ -102,9 +102,10 @@ class PromptManager(ComfyNodeABC): } } - RETURN_TYPES = (IO.CONDITIONING,) + RETURN_TYPES = (IO.CONDITIONING, IO.STRING) OUTPUT_TOOLTIPS = ( "A conditioning containing the embedded text used to guide the diffusion model.", + "The final combined text string (with prepend/append applied) that was encoded." ) FUNCTION = "encode" CATEGORY = "PromptManager/Text" @@ -137,7 +138,7 @@ class PromptManager(ComfyNodeABC): append_text: Text to append to the main prompt Returns: - Tuple containing the conditioning for the diffusion model + Tuple containing the conditioning for the diffusion model and the final text string Raises: RuntimeError: If clip input is invalid @@ -213,7 +214,7 @@ class PromptManager(ComfyNodeABC): conditioning = clip.encode_from_tokens_scheduled(tokens) self.logger.debug("CLIP encoding completed successfully") - return (conditioning,) + return (conditioning, encoding_text) def _save_prompt_to_database( self, From cde8212f32a10af9215a9cd4c6120c66b4a8c0dd Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 5 Jun 2025 13:14:43 -0700 Subject: [PATCH 4/7] feat: Add PromptManagerText class import and mappings --- __init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/__init__.py b/__init__.py index 740be16..04d815e 100644 --- a/__init__.py +++ b/__init__.py @@ -29,12 +29,23 @@ except Exception as e: log_message(f"Traceback: {traceback.format_exc()}", 'error') raise +try: + from .prompt_manager_text import PromptManagerText + log_message("Successfully imported PromptManagerText class") +except Exception as e: + log_message(f"ERROR: Failed to import PromptManagerText class: {e}", 'error') + import traceback + log_message(f"Traceback: {traceback.format_exc()}", 'error') + raise + NODE_CLASS_MAPPINGS = { "PromptManager": PromptManager, + "PromptManagerText": PromptManagerText, } NODE_DISPLAY_NAME_MAPPINGS = { "PromptManager": "Prompt Manager", + "PromptManagerText": "Prompt Manager Text", } log_message(f"NODE_CLASS_MAPPINGS: {NODE_CLASS_MAPPINGS}") From c5d33da96ba99b3b5dbabdc1dddc91c8bd5e3f87 Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 5 Jun 2025 13:18:34 -0700 Subject: [PATCH 5/7] docs: Update README with new node types and features --- README.md | 120 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index a51c6eb..4ac5efe 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,21 @@ A comprehensive ComfyUI custom node that extends the standard text encoder with ![Prompt Manager Interface](images/pm-01.png) -ComfyUI Prompt Manager functions as a drop-in replacement for ComfyUI's standard `CLIPTextEncode` node while adding comprehensive prompt management and automatic image tracking features: +ComfyUI Prompt Manager provides two powerful node types for comprehensive prompt management: +### **PromptManager** (CLIP Encoding Node) +A drop-in replacement for ComfyUI's standard `CLIPTextEncode` node with database features: - **🔄 Drop-in Replacement**: Works exactly like the standard text encoder +- **🤖 CLIP Encoding**: Outputs CONDITIONING for use with samplers and other CLIP-dependent nodes + +### **PromptManagerText** (Text-Only Node) +A text-focused variant that outputs raw STRING for maximum flexibility: +- **📝 Pure Text Output**: Outputs STRING type for use with any text-accepting node +- **🔗 Text Concatenation**: Built-in prepend/append functionality for dynamic text assembly +- **⚡ Lightweight Processing**: No CLIP encoding overhead for text-only workflows + +### **Shared Features** +Both nodes include the complete PromptManager feature set: - **💾 Persistent Storage**: Automatically saves all prompts to a local SQLite database - **🔍 Advanced Search**: Query past prompts with text search, category filtering, and metadata - **🖼️ Automatic Image Gallery**: Automatically links generated images to their prompts @@ -26,7 +38,18 @@ ComfyUI Prompt Manager functions as a drop-in replacement for ComfyUI's standard ### Core Functionality +#### PromptManager (CLIP Encoding) - **Text Encoding**: Standard CLIP text encoding for ComfyUI workflows +- **Conditioning Output**: Produces CONDITIONING for samplers and generation nodes +- **Full Compatibility**: Drop-in replacement for CLIPTextEncode nodes + +#### PromptManagerText (Text-Only) +- **String Output**: Produces STRING for use with any text-accepting node +- **Text Concatenation**: Prepend and append fields for dynamic text assembly +- **Lightweight**: No CLIP encoding overhead for pure text workflows +- **Flexible Integration**: Works with custom nodes, text processors, and other STRING inputs + +#### Shared Features - **Auto-Save**: Every prompt is automatically saved to the database - **Metadata Support**: Optional categories, tags, ratings (1-5), notes, and workflow names - **Hash-based Deduplication**: Prevents storing identical prompts multiple times @@ -133,10 +156,10 @@ The image gallery has been completely redesigned with professional image viewing 3. **Restart ComfyUI** to load the new node -4. **Add the node** to your workflow: +4. **Add the nodes** to your workflow: - - Look for "Prompt Manager" in the conditioning category - - Use it exactly like the standard "CLIP Text Encode" node + - **PromptManager**: Look for "Prompt Manager" in the conditioning category - use exactly like "CLIP Text Encode" + - **PromptManagerText**: Look for "Prompt Manager Text" in the PromptManager/Text category - outputs STRING for any text node 5. **Access the web interface**: - **Admin Dashboard**: `http://localhost:8188/prompt_manager/admin` - Full management interface @@ -154,19 +177,33 @@ The image gallery has been completely redesigned with professional image viewing ### Basic Usage +#### Using PromptManager (CLIP Encoding) Replace any `CLIPTextEncode` node with `PromptManager`: -1. **Add the node** to your workflow +1. **Add the PromptManager node** to your workflow 2. **Connect CLIP model** (same as standard text encoder) 3. **Enter your prompt** in the text field -4. **Optionally add metadata**: - - Category: "portraits", "landscapes", "abstract", etc. - - Tags: "detailed, anime, masterpiece" (comma-separated) - - Rating: 1-5 stars for prompt quality - - Notes: Any additional information - - Workflow Name: Name of your workflow for organization +4. **Connect the CONDITIONING output** to your sampler node -The node will encode your text, automatically save it to the database, and link any generated images to the prompt. +#### Using PromptManagerText (Text-Only) +For text-only workflows or when you need STRING output: + +1. **Add the PromptManagerText node** to your workflow +2. **Enter your prompt** in the text field +3. **Optionally use prepend/append** for dynamic text assembly: + - **Prepend Text**: Text added before your main prompt + - **Append Text**: Text added after your main prompt +4. **Connect the STRING output** to any node that accepts text input + +#### Shared Metadata Options +Both nodes support the same metadata fields: +- **Category**: "portraits", "landscapes", "abstract", etc. +- **Tags**: "detailed, anime, masterpiece" (comma-separated) +- **Rating**: 1-5 stars for prompt quality (web interface only) +- **Notes**: Any additional information (web interface only) +- **Workflow Name**: Name of your workflow for organization (web interface only) + +Both nodes will automatically save prompts to the database and link any generated images to the prompt. ### 🖼️ Using the Image Gallery @@ -274,34 +311,57 @@ images = db.get_prompt_images(prompt_id="123") ## Examples -### Example 1: Basic Prompt Storage +### PromptManager (CLIP Encoding) Examples +#### Example 1: Basic Prompt Storage ``` Input: "A beautiful sunset over a mountain lake" Category: "landscapes" Tags: "nature, sunset, mountains, water" -Rating: 5 -Notes: "Perfect for peaceful scenes" +→ Outputs: CONDITIONING for sampler nodes ``` -### Example 2: Character Prompt - +#### Example 2: Character Prompt ``` Input: "Portrait of a cyberpunk hacker with neon implants" Category: "characters" Tags: "cyberpunk, portrait, sci-fi, neon" -Rating: 4 -Workflow: "character_generator_v2" +→ Outputs: CONDITIONING for sampler nodes ``` -### Example 3: Abstract Art +### PromptManagerText (Text-Only) Examples +#### Example 3: Dynamic Text Assembly +``` +Main Text: "beautiful landscape" +Prepend Text: "masterpiece, ultra detailed," +Append Text: ", 8k resolution, trending on artstation" +→ Final Output: "masterpiece, ultra detailed, beautiful landscape, 8k resolution, trending on artstation" +``` + +#### Example 4: Text Processing Pipeline +``` +Input: "A serene mountain lake at sunset" +Category: "landscapes" +Tags: "nature, peaceful, golden hour" +→ Outputs: STRING for use with text processors, style nodes, or other custom nodes +``` + +#### Example 5: Multi-Node Text Workflow +``` +PromptManagerText → Text Processor → Style Applicator → Final Text Node +"cyberpunk city" → style processing → "neon-lit cyberpunk metropolis at night" +``` + +### Traditional Examples + +#### Example 6: Abstract Art ``` Input: "Swirling colors in an abstract geometric pattern" Category: "abstract" Tags: "geometric, colorful, pattern, modern" -Rating: 3 -Notes: "Good for experimental art" +Rating: 3 (set via web interface) +Notes: "Good for experimental art" (set via web interface) ``` ## Database Schema @@ -343,7 +403,8 @@ CREATE TABLE generated_images ( ### Core Components -- **`prompt_manager.py`** - Main ComfyUI node implementation +- **`prompt_manager.py`** - Main ComfyUI node implementation (CLIP encoding) +- **`prompt_manager_text.py`** - Text-only node implementation (STRING output) - **`database/models.py`** - Database schema and connection management - **`database/operations.py`** - CRUD operations and search functionality - **`py/api.py`** - Web API endpoints for the interface @@ -365,7 +426,8 @@ CREATE TABLE generated_images ( ``` ComfyUI_PromptManager/ ├── __init__.py # Node registration -├── prompt_manager.py # Main node implementation +├── prompt_manager.py # Main node implementation (CLIP encoding) +├── prompt_manager_text.py # Text-only node implementation (STRING output) ├── database/ │ ├── __init__.py │ ├── models.py # Database schema @@ -575,6 +637,16 @@ MIT License - see LICENSE file for details. ## Changelog +### v3.0.5 (Text-Only Node Release) + +- **📝 PromptManagerText Node**: New text-only variant that outputs STRING instead of CONDITIONING +- **🔗 Text Concatenation**: Built-in prepend/append functionality for dynamic text assembly +- **⚡ Lightweight Processing**: No CLIP encoding overhead for pure text workflows +- **🔄 Dual Node Architecture**: Choose between CLIP encoding (PromptManager) or text-only (PromptManagerText) +- **🏷️ Shared Metadata System**: Both nodes use the same database and metadata features +- **🖼️ Unified Gallery**: Images from both node types appear in the same gallery system +- **📊 Enhanced Tracking**: Improved prompt tracking with prepend/append context preservation + ### v3.0.3 (ViewerJS Integration & Enhanced Gallery) - **🖼️ Professional Image Viewer**: Complete gallery redesign with [ViewerJS](https://github.com/fengyuanchen/viewerjs) integration From 5c34ce35614d5c5a0ee92c2ecfeb394ff2e2a4ed Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Sun, 8 Jun 2025 15:45:01 -0700 Subject: [PATCH 6/7] style(docs): improve markdown formatting consistency --- README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ac5efe..536c761 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,24 @@ A comprehensive ComfyUI custom node that extends the standard text encoder with ComfyUI Prompt Manager provides two powerful node types for comprehensive prompt management: ### **PromptManager** (CLIP Encoding Node) + A drop-in replacement for ComfyUI's standard `CLIPTextEncode` node with database features: + - **🔄 Drop-in Replacement**: Works exactly like the standard text encoder - **🤖 CLIP Encoding**: Outputs CONDITIONING for use with samplers and other CLIP-dependent nodes ### **PromptManagerText** (Text-Only Node) + A text-focused variant that outputs raw STRING for maximum flexibility: + - **📝 Pure Text Output**: Outputs STRING type for use with any text-accepting node - **🔗 Text Concatenation**: Built-in prepend/append functionality for dynamic text assembly - **⚡ Lightweight Processing**: No CLIP encoding overhead for text-only workflows ### **Shared Features** + Both nodes include the complete PromptManager feature set: + - **💾 Persistent Storage**: Automatically saves all prompts to a local SQLite database - **🔍 Advanced Search**: Query past prompts with text search, category filtering, and metadata - **🖼️ Automatic Image Gallery**: Automatically links generated images to their prompts @@ -39,17 +45,20 @@ Both nodes include the complete PromptManager feature set: ### Core Functionality #### PromptManager (CLIP Encoding) + - **Text Encoding**: Standard CLIP text encoding for ComfyUI workflows - **Conditioning Output**: Produces CONDITIONING for samplers and generation nodes - **Full Compatibility**: Drop-in replacement for CLIPTextEncode nodes #### PromptManagerText (Text-Only) -- **String Output**: Produces STRING for use with any text-accepting node + +- **String Output**: Produces STRING for use with any text-accepting node - **Text Concatenation**: Prepend and append fields for dynamic text assembly - **Lightweight**: No CLIP encoding overhead for pure text workflows - **Flexible Integration**: Works with custom nodes, text processors, and other STRING inputs #### Shared Features + - **Auto-Save**: Every prompt is automatically saved to the database - **Metadata Support**: Optional categories, tags, ratings (1-5), notes, and workflow names - **Hash-based Deduplication**: Prevents storing identical prompts multiple times @@ -74,7 +83,7 @@ The image gallery has been completely redesigned with professional image viewing - **File Path Integration**: Direct access to image files in your output directory - **Responsive Design**: Works seamlessly across desktop, tablet, and mobile devices -*Image viewing powered by [ViewerJS](https://github.com/fengyuanchen/viewerjs) - A powerful JavaScript image viewer with zoom, rotation, and slideshow features.* +_Image viewing powered by [ViewerJS](https://github.com/fengyuanchen/viewerjs) - A powerful JavaScript image viewer with zoom, rotation, and slideshow features._ ### 🔬 ComfyUI Workflow Analysis @@ -178,6 +187,7 @@ The image gallery has been completely redesigned with professional image viewing ### Basic Usage #### Using PromptManager (CLIP Encoding) + Replace any `CLIPTextEncode` node with `PromptManager`: 1. **Add the PromptManager node** to your workflow @@ -186,6 +196,7 @@ Replace any `CLIPTextEncode` node with `PromptManager`: 4. **Connect the CONDITIONING output** to your sampler node #### Using PromptManagerText (Text-Only) + For text-only workflows or when you need STRING output: 1. **Add the PromptManagerText node** to your workflow @@ -196,7 +207,9 @@ For text-only workflows or when you need STRING output: 4. **Connect the STRING output** to any node that accepts text input #### Shared Metadata Options + Both nodes support the same metadata fields: + - **Category**: "portraits", "landscapes", "abstract", etc. - **Tags**: "detailed, anime, masterpiece" (comma-separated) - **Rating**: 1-5 stars for prompt quality (web interface only) @@ -314,6 +327,7 @@ images = db.get_prompt_images(prompt_id="123") ### PromptManager (CLIP Encoding) Examples #### Example 1: Basic Prompt Storage + ``` Input: "A beautiful sunset over a mountain lake" Category: "landscapes" @@ -322,6 +336,7 @@ Tags: "nature, sunset, mountains, water" ``` #### Example 2: Character Prompt + ``` Input: "Portrait of a cyberpunk hacker with neon implants" Category: "characters" @@ -332,6 +347,7 @@ Tags: "cyberpunk, portrait, sci-fi, neon" ### PromptManagerText (Text-Only) Examples #### Example 3: Dynamic Text Assembly + ``` Main Text: "beautiful landscape" Prepend Text: "masterpiece, ultra detailed," @@ -340,6 +356,7 @@ Append Text: ", 8k resolution, trending on artstation" ``` #### Example 4: Text Processing Pipeline + ``` Input: "A serene mountain lake at sunset" Category: "landscapes" @@ -348,6 +365,7 @@ Tags: "nature, peaceful, golden hour" ``` #### Example 5: Multi-Node Text Workflow + ``` PromptManagerText → Text Processor → Style Applicator → Final Text Node "cyberpunk city" → style processing → "neon-lit cyberpunk metropolis at night" @@ -356,6 +374,7 @@ PromptManagerText → Text Processor → Style Applicator → Final Text Node ### Traditional Examples #### Example 6: Abstract Art + ``` Input: "Swirling colors in an abstract geometric pattern" Category: "abstract" @@ -646,6 +665,7 @@ MIT License - see LICENSE file for details. - **🏷️ Shared Metadata System**: Both nodes use the same database and metadata features - **🖼️ Unified Gallery**: Images from both node types appear in the same gallery system - **📊 Enhanced Tracking**: Improved prompt tracking with prepend/append context preservation +- **📊 Enhanced Tracking**: Improved prompt tracking with prepend/append context preservation ### v3.0.3 (ViewerJS Integration & Enhanced Gallery) From feb8248fdd6c5ea43857d66521ed139f371b9057 Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Sun, 8 Jun 2025 15:45:13 -0700 Subject: [PATCH 7/7] chore: bump version to 3.0.7 in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 225b41d..cf350cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "promptmanager" description = "A powerful ComfyUI custom node that extends the standard text encoder with persistent prompt storage, advanced search capabilities, and an automatic image gallery system using SQLite." -version = "3.0.6" +version = "3.0.7" license = {file = "LICENSE"} dependencies = ["# Core dependencies for PromptManager", "# Note: Most dependencies are already included with ComfyUI", "# Already included with Python standard library:", "# - sqlite3", "# - hashlib", "# - json", "# - datetime", "# - os", "# - typing", "# - threading", "# - uuid", "# Required for gallery functionality:", "watchdog>=2.1.0 # For file system monitoring", "Pillow>=8.0.0 # For image metadata extraction (usually included with ComfyUI)", "# Optional dependencies for enhanced search functionality:", "# fuzzywuzzy[speedup]>=0.18.0 # For fuzzy string matching (optional)", "# sqlalchemy>=1.4.0 # For advanced ORM features (optional)", "# Development dependencies (optional):", "# pytest>=6.0.0 # For running tests", "# black>=22.0.0 # For code formatting", "# flake8>=4.0.0 # For linting", "# mypy>=0.910 # For type checking"]