- os module imported properly instead of __import__("os")
- browse_folder distinguishes a real COM/dialog failure from a user cancel
- fix Marshal.PtrToStringUni + leak-safe cleanup in the folder-picker COM call
- lock browse_folder so two concurrent clicks can't open two dialogs
- drop redundant int(round()) in SmartResolutionMultiplier
- unify "CraftKit —" docstring prefix across nodes
- comment OUTPUT_NODE=True on the status-label nodes so it isn't "cleaned up" later
- add .jfif/.avif/.heic to SmartBatchResize's supported extensions, document non-recursive scanning
- add a progress bar to SmartBatchResize (degrades gracefully outside a live ComfyUI)
- add readme/requires-python and a dependency-rationale comment to pyproject.toml
99 lines
3.4 KiB
Python
99 lines
3.4 KiB
Python
class SmartPromptController:
|
|
"""
|
|
CraftKit — Smart Prompt Controller 📋
|
|
Cycles through up to 4 prompt lists using a single incrementing counter.
|
|
Counts lines automatically and outputs the selected prompt + list number.
|
|
"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"index": ("INT", {
|
|
"default": 1,
|
|
"min": 1,
|
|
"max": 99999,
|
|
"step": 1,
|
|
"display": "number",
|
|
"control_after_generate": "increment",
|
|
"tooltip": "Current position across all lists.",
|
|
}),
|
|
},
|
|
"optional": {
|
|
"prompt_list_1": ("STRING", {"forceInput": True, "tooltip": "Prompt list, one prompt per line."}),
|
|
"prompt_list_2": ("STRING", {"forceInput": True, "tooltip": "Optional second prompt list."}),
|
|
"prompt_list_3": ("STRING", {"forceInput": True, "tooltip": "Optional third prompt list."}),
|
|
"prompt_list_4": ("STRING", {"forceInput": True, "tooltip": "Optional fourth prompt list."}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING", "INT")
|
|
RETURN_NAMES = ("prompt", "active_list")
|
|
FUNCTION = "control"
|
|
CATEGORY = "CraftKit"
|
|
OUTPUT_NODE = True # renders a status label on the node; must always execute, don't prune this
|
|
|
|
def _get_lines(self, text):
|
|
"""Return list of non-empty lines from a text block."""
|
|
if not text:
|
|
return []
|
|
if isinstance(text, list):
|
|
text = text[0] if text else ""
|
|
return [l for l in str(text).splitlines() if l.strip()]
|
|
|
|
def control(self, index, prompt_list_1="", prompt_list_2="",
|
|
prompt_list_3="", prompt_list_4=""):
|
|
raw = [prompt_list_1, prompt_list_2, prompt_list_3, prompt_list_4]
|
|
|
|
# Collect active lists: (list_index, [lines])
|
|
active = []
|
|
for i, text in enumerate(raw):
|
|
lines = self._get_lines(text)
|
|
if lines:
|
|
active.append((i, lines))
|
|
|
|
total_all = sum(len(lines) for _, lines in active)
|
|
|
|
if total_all == 0:
|
|
return {"ui": {"text": ["no prompts connected"]},
|
|
"result": ("", 0)}
|
|
|
|
# Map counter position to list + line
|
|
idx = (index - 1) % total_all
|
|
|
|
cumulative = 0
|
|
selected_list_index = 0
|
|
selected_line_index = 0
|
|
selected_prompt = ""
|
|
|
|
for list_idx, lines in active:
|
|
if idx < cumulative + len(lines):
|
|
selected_list_index = list_idx
|
|
selected_line_index = idx - cumulative
|
|
selected_prompt = lines[selected_line_index]
|
|
break
|
|
cumulative += len(lines)
|
|
|
|
# Label: "List 1 — 4/5 | total: 8"
|
|
list_number = selected_list_index + 1
|
|
list_total = len(self._get_lines(raw[selected_list_index]))
|
|
prompt_display = selected_line_index + 1
|
|
label = f"List {list_number} \u2014 {prompt_display}/{list_total} | total: {total_all}"
|
|
|
|
return {
|
|
"ui": {"text": [label]},
|
|
"result": (
|
|
selected_prompt,
|
|
list_number,
|
|
)
|
|
}
|
|
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"SmartPromptController": SmartPromptController,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"SmartPromptController": "Smart Prompt Controller \ud83d\udccb",
|
|
}
|