Visual Prompt Composer (Studio Leiel)
Your prompt is a wall of text — this node gives it landmarks
- all prompt
- labeled prompt
- out_1
- out_2
- out_3
- out_4
- out_5
- out_6
- out_7
- out_8
A long prompt is a record of decisions that all look alike once it's saved. Subject, lighting, film stock, that clause you added last month and never checked again - the moment it becomes one flat string, finding the part you meant to change means rereading all fourteen thousand characters. That rereading is where your time goes, and it's the whole problem this node exists to solve.
Visual Prompt Composer holds your prompt as up to eight named sections, each in its own box, each with its own bypass toggle, colour, notes and character count. It replaces the classic chain of a text box per part, a concatenate node per join, and a preview node to see the result. Reorder a section and the wiring doesn't move with it; bypass one with a click instead of deleting and re-pasting. This matters more on 2026's LLM-encoded models than it did on SD 1.5 - block structure in a prompt is genuinely load-bearing there, and this is a way to keep that structure visible instead of hoping you remember it.
The design trick that makes it free to annotate: none of the styling reaches the model. The marks are metadata kept beside the text, and what leaves the node is plain text. Bold, colour, highlight, notes to your future self - all cost nothing at generation time.
How it works
The whole layout - titles, text, on/off, box heights, slot numbers - lives in a single hidden STRING widget (layout_json) that travels with the workflow like any other widget value. The frontend owns the editing; the Python side only assembles the final strings. It's deliberately built to never throw, because a prompt node that errors out stops a render before it starts.
The inputs a beginner actually touches are three: prompt_separator (how sections join - blank line, newline, space or comma), labeled_style (how the labelled output formats each section's name, from banner down to slash), and skip_empty (whether an empty section still contributes a separator). The rest are the wires: ext_1 through ext_8 let another node feed or override any section's text, and find_text / replace_text drive a search-and-replace from elsewhere in the graph - handy when you want one node swapping a character across every workflow.
Outputs are all prompt (everything joined), labeled prompt (the same with each section named), and out_1 through out_8, one per section. The per-section outputs are bound to a section's stable slot number, not its position, so deleting or reordering a section can't silently rewire your links. That's also how the "two sections called Positive and Negative, each wired to its own encoder" trick works.
Presets and snapshots are saved to ComfyUI's user/ directory, so updating the pack never eats them - a nicer answer than the packs that keep them in browser storage and lose them to a cache clear.
Install
This ships in Visual Suite (Studio Leiel). In ComfyUI Manager, search that name, or clone it in:
cd ComfyUI/custom_nodes
git clone https://github.com/StudioLeiel/comfyui-visual-suite
Restart ComfyUI. No extra dependencies and no model downloads - it's pure graph plumbing.
Common issues
- My notes disappeared? They live in the workflow JSON and in the preset files under
user/- a new browser session should still see them, since nothing is in localStorage. - A section shows nothing in the output. Check its bypass (
B) is off and thatskip_emptyisn't eating it - and remember a connectedext_ninput overrides whatever you typed, by design. - Translation pane: it runs in the browser, is never saved and never touches the model. It's there to be read, not to change anything - don't expect it to survive a reload.
For the one thing most prompt tools get wrong - keeping the part you're changing findable without forcing you to reread everything - this earns its place in the graph.
Inputs (14)
| Name | Type | Default | Description |
|---|---|---|---|
| layout_json | STRING | — | |
| prompt_separator | COMBO | blank_line | 4 options: blank_line, newline, space, comma |
| labeled_style | COMBO | banner | 7 options: banner, rule, bracket, title_block, upper_block, title_inline, +1 |
| skip_empty | BOOLEAN | true | — |
| ext_1opt | STRING | — | |
| ext_2opt | STRING | — | |
| ext_3opt | STRING | — | |
| ext_4opt | STRING | — | |
| ext_5opt | STRING | — | |
| ext_6opt | STRING | — | |
| ext_7opt | STRING | — | |
| ext_8opt | STRING | — | |
| find_textopt | STRING | — | |
| replace_textopt | STRING | — |
Outputs (10)
| Name | Type | Description |
|---|---|---|
| all prompt | STRING | — |
| labeled prompt | STRING | — |
| out_1 | STRING | — |
| out_2 | STRING | — |
| out_3 | STRING | — |
| out_4 | STRING | — |
| out_5 | STRING | — |
| out_6 | STRING | — |
| out_7 | STRING | — |
| out_8 | STRING | — |