Added an updated version of Prompt_Library again

The positive negative seperator can now be used inside a text block, so you don't have to use it in it's own line anymore.
The seperator is --- or ---- or -----
This commit is contained in:
hexxacubic
2026-05-16 20:19:30 +02:00
committed by GitHub
parent 15013463ce
commit 2e5f484cd3
2 changed files with 187 additions and 13 deletions
+173
View File
@@ -0,0 +1,173 @@
import os
import re
import time
import random
import sys
import subprocess
import folder_paths
class Prompt_Library:
"""
Prompt Library - File-based prompt management
• project: Select prompt file from dropdown
• index: Choose prompt project within file (1-999)
• randomize_index: When enabled, index is randomly selected
File Syntax:
- Empty lines separate projects
- A separator made of 3 or more hyphens splits positive/negative prompts
- The separator may appear anywhere in the text, not only on its own line
- ### at line start marks comments/notes (will be ignored)
Outputs:
- double_prompt
- positive_prompt
- negative_prompt
"""
def __init__(self):
self.base = os.path.join(folder_paths.models_dir, "prompts")
self.refresh_projects()
def refresh_projects(self):
base = self.base
if not os.path.exists(base):
os.makedirs(base)
cats = [d for d in os.listdir(base) if os.path.isdir(os.path.join(base, d))]
proj_choices = []
for cat in sorted(cats):
folder = os.path.join(base, cat)
for fn in sorted(os.listdir(folder)):
if fn.lower().endswith(".txt"):
name = fn[:-4]
proj_choices.append(f"{cat}/{name}")
self._proj_choices = proj_choices if proj_choices else ["No projects found"]
def load_file_content(self, project):
try:
category, name = project.split("/", 1)
path = os.path.join(self.base, category, name + ".txt")
if os.path.isfile(path):
with open(path, encoding="utf-8") as f:
return f.read()
except Exception:
pass
return ""
@staticmethod
def split_positive_negative(section_text):
match = re.search(r"-{3,}", section_text)
if not match:
pos = section_text.strip()
neg = ""
return pos, neg
pos = section_text[:match.start()].strip()
neg = section_text[match.end():].strip()
return pos, neg
@classmethod
def INPUT_TYPES(cls):
instance = cls()
instance.refresh_projects()
return {
"required": {
"project": (instance._proj_choices,),
"index": ("INT", {"default": 1, "min": 1, "max": 999}),
"randomize_index": ("BOOLEAN", {"default": False}),
"random_seed": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}),
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING")
RETURN_NAMES = ("double_prompt", "positive_prompt", "negative_prompt")
FUNCTION = "get_prompt"
OUTPUT_NODE = True
CATEGORY = "hexxacubic"
@staticmethod
def ui_open_prompts_folder():
base = os.path.join(folder_paths.models_dir, "prompts")
if sys.platform == "win32":
os.startfile(base)
elif sys.platform == "darwin":
subprocess.Popen(["open", base])
else:
subprocess.Popen(["xdg-open", base])
return "Prompts folder opened."
def ui_refresh_projects(self):
self.refresh_projects()
return "Project list refreshed."
@classmethod
def UI_BUTTONS(cls):
return [
{"label": "📁 Prompts Folder", "method": "ui_open_prompts_folder"},
{"label": "🔄 Refresh List", "method": "ui_refresh_projects"},
]
def get_prompt(self, project, index, randomize_index, random_seed):
self.refresh_projects()
if project == "No projects found":
return ("", "", "")
file_content = self.load_file_content(project)
if not file_content.strip():
return ("", "", "")
sections = []
current_lines = []
for line in file_content.splitlines():
if line.strip().startswith("###"):
continue
if not line.strip():
if current_lines:
sections.append("\n".join(current_lines).strip())
current_lines = []
else:
current_lines.append(line)
if current_lines:
sections.append("\n".join(current_lines).strip())
if not sections:
return ("", "", "")
available_indices = list(range(1, len(sections) + 1))
if randomize_index:
actual_seed = int(time.time() * 1000000) % 0xffffffffffffffff if random_seed == -1 else random_seed
gen = random.Random(actual_seed)
used_idx = gen.choice(available_indices)
else:
used_idx = available_indices[(index - 1) % len(available_indices)]
section_text = sections[used_idx - 1]
pos, neg = self.split_positive_negative(section_text)
if pos and neg:
double_prompt = f"{pos}\n---\n{neg}"
elif pos:
double_prompt = pos
elif neg:
double_prompt = f"\n---\n{neg}"
else:
double_prompt = ""
return (double_prompt, pos, neg)
@classmethod
def IS_CHANGED(cls, project, index, randomize_index, random_seed):
if randomize_index:
return float("nan")
return hash((project, index, random_seed))
+14 -13
View File
@@ -3,23 +3,24 @@ from .Prompt_Extender import Prompt_Extender
from .Double_Prompt_Encode import Double_Prompt_Encode
from .Five_Random_Lines import FiveRandomLinesNode
from .Random_Line import RandomLineNode
from .Prompt_Library import Prompt_Library
NODE_CLASS_MAPPINGS = {
"Simple_Prompt_Library": Simple_Prompt_Library,
"Prompt_Extender": Prompt_Extender,
"Double_Prompt_Encode": Double_Prompt_Encode,
"FiveRandomLines": FiveRandomLinesNode,
"RandomLine": RandomLineNode,
"Simple_Prompt_Library": Simple_Prompt_Library,
"Prompt_Extender": Prompt_Extender,
"Double_Prompt_Encode": Double_Prompt_Encode,
"FiveRandomLines": FiveRandomLinesNode,
"RandomLine": RandomLineNode,
"Prompt_Library": Prompt_Library,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Simple_Prompt_Library": "Simple Prompt Library",
"Prompt_Extender": "Prompt Extender",
"Double_Prompt_Encode": "Double Prompt Encode",
"FiveRandomLines": "Five Random Lines",
"RandomLine": "Random Line",
"Simple_Prompt_Library": "Simple Prompt Library",
"Prompt_Extender": "Prompt Extender",
"Double_Prompt_Encode": "Double Prompt Encode",
"FiveRandomLines": "Five Random Lines",
"RandomLine": "Random Line",
"Prompt_Library": "Prompt Library",
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]