Text Builder
Prompt fragments as a stack you can toggle and reorder — without the comma hell
- text
Prompt A/B testing in ComfyUI means editing one giant string and hoping you remember what changed. "Masterpiece" block, lighting block, style block - they all live in a single text field, so toggling one fragment means surgically deleting it from the middle of a sentence and praying you don't break the commas. Text Builder gives each fragment its own multiline block on the node, so you can flip a block off without deleting it, reorder blocks with ▲/▼, and let the node handle the joining.
It's the version of Dynamic String Concat that owns its text. Dynamic String Concat joins strings that arrive over links; Text Builder holds the text itself - each block is a real multiline textarea with its own control strip.
How it works
The frontend serializes your blocks top-to-bottom into widget pairs, and the backend joins the enabled ones with a separator. The clever bit is that order is taken from display position, not from block number - move a block with the arrow buttons and the output follows, even though the internal widget names never change. Blocks are stripped of whitespace, empty ones are skipped, and the survivors get joined with the separator verbatim.
The inputs that matter
separator- inserted between every part, verbatim. Default is,, which givesa,b- swap to", "if you want a space. This is the single most common "why is it like this" gotcha.skip_empty- drops parts that end up empty after stripping, so you don't get stray,,. Default on.strip_whitespace- trims each block before joining. Default on.newline_between_inputs- join with a linefeed instead of the separator.newline_wrapped_separator- the advanced one. If non-empty, it joins parts aspart + LF + text + LF + partand overrides both the separator and the newline toggle. Useful for wrapping a style token around content.text- optional input. When connected, it's always the first part and can't be reordered. By design.
Output: text - one joined STRING, ready for your prompt encoder.
The workflow it's good for
Build a prompt from stable fragments you keep coming back to: a quality prefix, a subject line you swap, a lighting block you A/B against another lighting block. Disabled blocks keep their text but contribute nothing - so you can keep both lighting variants around and flip between them, or Toggle All to silence the whole stack. You can even name a block (click its #1 caption) for tidiness - names are cosmetic and never reach the output.
Installing it
Text Builder ships in MoonPack (comfyui-moonpack). Install via ComfyUI Manager (search MoonPack) or:
cd ComfyUI/custom_nodes
git clone https://github.com/moonwhaler/comfyui-moonpack.git
Restart ComfyUI. No models, no extra dependencies - though note Text Builder (and MoonLoRA Loader) have frontend JavaScript, so a full restart matters more than a refresh after installing.
The two things to remember
The connected text input is glued to the front - you can't reorder it, which is annoying until you realize it's deliberate: it's meant to be your "always-first" anchor. And that default comma separator: if you're seeing cat, in a hat missing spaces, it's not a bug - it's "," working as configured. Change it once, save the workflow, move on.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| separator | STRING | , | Inserted verbatim between the parts. ',' gives 'a,b'; use ', ' for a space. |
| skip_empty | BOOLEAN | true | Drop parts that are empty after stripping, so no stray separators appear. |
| strip_whitespace | BOOLEAN | true | Strip leading/trailing whitespace from each part before joining. |
| newline_between_inputs | BOOLEAN | false | Join parts with a linefeed (\n) instead of 'separator'. |
| newline_wrapped_separator | STRING | Advanced. If non-empty, joins parts as LF + this text + LF, e.g. '(part) LF (text) LF (part)'. Takes precedence over 'newline_between_inputs' and 'separator'. | |
| textopt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| text | STRING | — |