- rename Smart Category Config → Smart Profile Switch 🧩 (code, JS, locales, demo workflow)
- add banner.svg and embed it at the top of README
- replace fragile OpenFileDialog folder-browse hack with native IFileDialog/FOS_PICKFOLDERS picker — fixes wrong-folder bug on single-click select
- flip Smart Resize's upscale_if_smaller default to true — node now resizes to target by default, as the name implies
- add width/height source tooltip to Smart Resolution Multiplier's image input
- fix stale widgets_values in smart_batch_resize_demo.json (10 fields → 13 after prefix/counter fields were added)
- full README rewrite: correct inputs/defaults/outputs for all 5 nodes, new Smart Profile Switch section, Example workflows section, expanded "Why these nodes?"
- add new example workflow (seedvr2_smartresize_demo.json), updated screenshots
- add assets/*.ai to .gitignore
- bump version to 1.0.2
99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
class SmartPromptController:
|
|
"""
|
|
Craftopia — 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
|
|
|
|
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",
|
|
}
|