From 28febe623b905c380899b7f9713582b7c96c7873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=8B=E3=81=BF=E3=81=AA=E3=83=BC=E5=85=88=E7=94=9F?= Date: Fri, 15 May 2026 01:52:57 +0900 Subject: [PATCH] =?UTF-8?q?=E9=81=B8=E6=8A=9E=E7=B5=90=E6=9E=9C=E3=81=AE?= =?UTF-8?q?=E5=87=BA=E5=8A=9B=E5=BD=A2=E5=BC=8F=E3=82=92=E6=95=B4=E7=90=86?= =?UTF-8?q?=E3=80=82=E6=98=8E=E7=A4=BA=E7=9A=84=E3=81=AA=E7=A9=BA=E5=80=99?= =?UTF-8?q?=E8=A3=9C=E3=81=AB=E5=AF=BE=E5=BF=9C=E3=81=97=E3=80=81=E3=83=8E?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=81=AE=E3=82=BF=E3=82=A4=E3=83=88=E3=83=AB?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=82=92=E6=94=B9=E5=96=84=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 43 ++++++++++++++++++++++----------- RELEASE_NOTES.md | 13 +++++++--- nodes.py | 47 +++++++++++++++++++++++++++++-------- web/prompt_random_choice.js | 10 ++++---- 4 files changed, 80 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index c2bf6ba..fbcd954 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ - **区切りは `|` または実際の改行** - `|` と改行の両方を区切りとして扱うので、混在していても動くぞ。 + - 区切り文字が連続して出現した場合は、候補から取り除かれることに注意じゃ。 + - 明示的に空白文字列を候補として返却させたい場合は、明示的に `()` と指定するのじゃ。 - **候補の前後の `,` と空白を自動で整理** - `town,` @@ -23,10 +25,6 @@ - `castle, fortress,` のような書き方でも大丈夫じゃ。 -- **プロンプト結合向けの返り値** - - 選ばれた候補の前後に `,` を付けて返すぞ。 - - 例: `coffee shop` → `,coffee shop,` - - **現在の選択結果をタイトル表示** - 実行時にノードタイトルが `Choice: coffee shop` のように更新されるぞ。 - KSamplerのプレビューと見比べやすいのじゃ。 @@ -34,6 +32,12 @@ - **複数設置に対応** - 背景用、時間帯用、天気用など、複数ノードを置いてそれぞれ別々に使えるぞ。 +- **ファイル名向けの返り値** + - ファイル名向けに安全化した文字列 `selected_text_safe` を出力するのじゃ。 + +- **明示的な空候補 `()` に対応** + - `()` が選ばれた場合、プロンプト向け出力は空文字に、ファイル名向け出力は `empty` になるのじゃ。 + ## 導入方法 ComfyUIの `custom_nodes` ディレクトリで、以下のコマンドを打ち込むのじゃ! @@ -47,7 +51,7 @@ git clone https://github.com/ruminar/ComfyUI-PromptRandomChoice.git 1. `Prompt Random Choice` ノードを置く 2. `options_text` に候補を入れる (入力例を参照) 3. 必要に応じて `change_every` を選ぶ (NEW) -4. `prompt_text` を文字列結合ノードなどでポジティブプロンプトへ足す +4. `selected_text` を `Join String Multi` などの文字列結合ノードへ繋ぎ、ポジティブプロンプトへ足す 5. キューを好きなだけ積む
@@ -95,6 +99,15 @@ flower field| starry sky| coffee shop| ``` +### 何も追加しない候補 + +`()` は明示的な空候補として扱われます。 + +```text +()|(full body:0.9) +``` + +この例では、何も追加しない場合と、`(full body:0.9)` を追加する場合をランダムに切り替えられます。 ### 選ばれやすさを調整したい場合 @@ -112,13 +125,20 @@ day|day|day|sunset|night - `\n` という文字列は区切りとして扱わない - 各候補の前後の空白と `,` を trim - 空候補は無視 -- 有効な候補が1件も無い場合は空文字 `""` を返す -- 候補があれば `,selected,` を返す +- `()` は明示的な空候補として扱う +- `change_every` が 1 なら毎回選び直す +- `change_every` が 2 以上なら、その回数ぶん同じ候補を維持する +- 実行時にタイトルへ `Choice: lake` や `Choice: (empty) (2/3)` のように表示する ## 出力 -- `prompt_text` - - 例: `,coffee shop,` (結合しやすいように、前後をtrimしてから`,`を付与します) +- `selected_text` + trim後の選択文字列です。 + `()` が選ばれた場合は空文字 `""` になります。 + +- `selected_text_safe` + ファイル名向けに安全化した出力です。 + `selected_text` が空なら `empty` を返します。
@@ -154,8 +174,3 @@ GPL-3.0(ComfyUI本体の掟に従っておるぞ!) ## 宣伝画像 -PromptRandomChoice説明画像v0 2 0 -※ 区切り文字は 改行 または `|` です。カンマは候補の前後に付いた場合だけ自動で取り除きます。

- -(最初の版) -PromptRandomChoice説明画像 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 95784f0..9537bed 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,10 @@ -# v0.2.0 +## v0.3.0 -change_everyを追加したのじゃ! +- 出力を整理 + - `selected_text` : プロンプト向け出力 + - `selected_text_safe` : ファイル名向け出力 +- 明示的な空候補 `()` に対応 + - `()` が選ばれた場合、プロンプト向け出力は空文字になり、ファイル名向け出力は `empty` になります `ComfyUI-PromptRandomChoice` は、プロンプト候補リストから実行のたびにランダムで1項目を選び、ポジティブプロンプトなどへ追加しやすい形で出力する ComfyUI カスタムノードです。 @@ -9,7 +13,8 @@ change_everyを追加したのじゃ! - プロンプト候補から実行ごとにランダムで1項目を選択 - 候補は `|` または実際の改行で区切り - 候補の前後にある空白や `,` を自動で trim -- 選ばれた候補を `,coffee shop,` のように前後カンマ付きで出力 +- 選ばれた候補を `selected_text` として通常の文字列で出力 +- ファイル名向けに安全化した `selected_text_safe` を出力 - `change_every` に対応 - `1` なら毎回選び直し - `3` なら同じ候補を3回返してから次を選択 @@ -20,6 +25,8 @@ change_everyを追加したのじゃ! - 背景用、時間帯用、天気用などを別々にランダム化可能 - 同じ候補を複数回書くことで、選ばれやすさを調整可能 - 例: `day|day|day|sunset|night` +- 明示的な空候補 `()` に対応 + - `()` が選ばれた場合、プロンプト向け出力は空文字になり、ファイル名向け出力は `empty` になります ## 注意 diff --git a/nodes.py b/nodes.py index 7bcb734..1e8df1f 100644 --- a/nodes.py +++ b/nodes.py @@ -26,12 +26,38 @@ def _split_options(options_text: str): options = [] for part in parts: item = str(part).strip(" \t\r\n,") - if item: - options.append(item) + + # Empty parts caused by repeated delimiters are ignored. + if not item: + continue + + # Explicit empty choice. + # Example: ()|(full body:0.9) + if item == "()": + options.append("") + continue + + options.append(item) return options +def _safe_text(value: str): + text = str(value or "") + + if not text: + return "empty" + + text = text.replace("\\", "/").replace("/", "_") + text = re.sub(r'[<>:"|?*\x00-\x1f]', "_", text) + text = re.sub(r"[\s,]+", "_", text) + text = text.replace("(", "_").replace(")", "_") + text = re.sub(r"_+", "_", text) + text = text.strip().strip("._-") + + return text or "empty" + + class PromptRandomChoice: def __init__(self): self._current_choice = None @@ -62,8 +88,8 @@ class PromptRandomChoice: }, } - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("prompt_text",) + RETURN_TYPES = ("STRING", "STRING") + RETURN_NAMES = ("selected_text", "selected_text_safe") FUNCTION = "pick" CATEGORY = "utils/prompt" @@ -90,11 +116,11 @@ class PromptRandomChoice: self._reset_state() return { "ui": { - "selected_text": [""], + "selected_text_title": ["(empty)"], "repeat_index": [0], "change_every": [change_every], }, - "result": ("",), + "result": ("", "empty"), } state_changed = ( @@ -113,16 +139,17 @@ class PromptRandomChoice: self._last_options_key = options_key self._last_change_every = change_every - selected = self._current_choice - prompt_text = f",{selected}," + selected_text = str(self._current_choice or "") + selected_text_safe = _safe_text(selected_text) + selected_text_title = selected_text if selected_text else "(empty)" return { "ui": { - "selected_text": [selected], + "selected_text_title": [selected_text_title], "repeat_index": [self._repeat_index], "change_every": [change_every], }, - "result": (prompt_text,), + "result": (selected_text, selected_text_safe), } diff --git a/web/prompt_random_choice.js b/web/prompt_random_choice.js index 2723619..e44f2ec 100644 --- a/web/prompt_random_choice.js +++ b/web/prompt_random_choice.js @@ -24,16 +24,14 @@ app.registerExtension({ nodeType.prototype.onExecuted = function (message) { originalOnExecuted?.apply(this, arguments); - const selected = message?.selected_text?.[0] ?? ""; + const selectedTitle = message?.selected_text_title?.[0] ?? "(empty)"; const repeatIndex = Number(message?.repeat_index?.[0] ?? 0); const changeEvery = Number(message?.change_every?.[0] ?? 1); - if (!selected) { - this.title = "Prompt Random Choice"; - } else if (changeEvery <= 1) { - this.title = `Choice: ${shorten(selected)}`; + if (changeEvery <= 1) { + this.title = `Choice: ${shorten(selectedTitle)}`; } else { - this.title = `Choice: ${shorten(selected)} (${repeatIndex}/${changeEvery})`; + this.title = `Choice: ${shorten(selectedTitle)} (${repeatIndex}/${changeEvery})`; } this.setDirtyCanvas?.(true, true);