⬡ Polyhedron CLIP Text Encode
One prompt node that replaces the string-of-CLIP-nodes chain
- clip
- positive
- negative
- positive_text
- negative_text
- full_text
Every serious multi-LoRA workflow eventually builds the same franken-chain: two or three CLIPTextEncode nodes, a couple of string concats, maybe a trigger-words feed from your LoRA stack. The Polyhedron CLIP Text Encode collapses that whole chain into one node, and it does it without re-implementing anything - it encodes through ComfyUI's own CLIP encoder, which is the right instinct for a custom node to have.
The pitch is simple: instead of one wall of text, the positive prompt is assembled from up to six segments. Trigger block in segment one, subject in two, style in three - you edit a section without hunting through a paragraph. There's a negative box in the same node, and the composed strings come back out as text, so what the encoder actually saw is readable right on the graph.
The inputs that matter
clip is the same pin as the core node - your CLIP/text encoder. segments sets how many positive fields are visible (1–6), and separator decides how they join: comma, newline, space, or none. The genuinely clever bit: turning segments down only hides fields. The text stays saved with the node; it just drops out of the prompt. Park a style block in segment 4, drop the count to 3, and you've switched it off without deleting a word.
strip_comments is the feature you didn't know you wanted. Marked lines - default marker // - are stripped before encoding, so you can keep // SCENE & CAMERA headers and // TODO try 0.8 notes in the box. They never become tokens. That's not cosmetic: every word in the prompt competes for the encoder's attention, and a stray SUBJECT heading left in the text is read as content.
use_negative switches the negative box off entirely for distilled and Lightning setups that want an empty negative at CFG 1.0. The negative output still carries a valid conditioning (an encoding of an empty prompt), so anything wired to it keeps working.
Then there are the external inputs, pos_external and neg_external - text from any STRING source, with external_mode choosing append, prepend, or replace. This is where a captioner like Florence2 or a WD14 tagger becomes part of your prompt without you typing a word, and replace is the batch mode: the segments stay in the node untouched, but the prompt is the external text alone.
Outputs: positive and negative (CONDITIONING) wire into a sampler; positive_text, negative_text and full_text hand back exactly what was encoded - useful for logging or a Save node's metadata.
Installing it
It's in the Polyhedron Suite pack. ComfyUI Manager: search "Polyhedron Suite", or:
cd ComfyUI/custom_nodes
git clone https://github.com/PolyhedronAI/ComfyUI-PolyhedronLoRAStack.git
# restart ComfyUI
No extra Python dependencies. Unlike the LoRA Stack and Engine, this node's UI is plain widgets, so it renders fine under ComfyUI's new "Modern Node Design" renderer.
Where people get burned
The token footer along the bottom is the honest account of what will be encoded - words and characters counted live, tokens from the last run. Trust it: if your segments plus a wired trigger-words feed are pushing past WAN's 512-token limit, the truncation is silent. That's the same counting function the pack's Token Counter uses, so the two never disagree. Also worth knowing: neg_external only works while use_negative is on - with the negative switched off, the negative prompt isn't assembled at all.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| clip | CLIP | The CLIP / text encoder to use (same pin as the core node). | |
| segments | INT | 31–6 | How many positive segments are visible. Turning this DOWN only hides fields - their text stays saved. |
| pos_1 | STRING | Positive segment 1. Keep your sections apart for readability - they are joined into ONE prompt by the separator. Hidden segments keep their text. | |
| pos_2 | STRING | Positive segment 2. Keep your sections apart for readability - they are joined into ONE prompt by the separator. Hidden segments keep their text. | |
| pos_3 | STRING | Positive segment 3. Keep your sections apart for readability - they are joined into ONE prompt by the separator. Hidden segments keep their text. | |
| pos_4 | STRING | Positive segment 4. Keep your sections apart for readability - they are joined into ONE prompt by the separator. Hidden segments keep their text. | |
| pos_5 | STRING | Positive segment 5. Keep your sections apart for readability - they are joined into ONE prompt by the separator. Hidden segments keep their text. | |
| pos_6 | STRING | Positive segment 6. Keep your sections apart for readability - they are joined into ONE prompt by the separator. Hidden segments keep their text. | |
| use_negative | BOOLEAN | true | Off hides the negative box and encodes an EMPTY negative (a valid conditioning - the graph stays intact). |
| neg_1 | STRING | The negative prompt. | |
| separator | COMBO | comma | How the segments are joined into one prompt (comma = ', '). |
| strip_comments | BOOLEAN | true | Remove // comments BEFORE flattening newlines. Your section headers stay in the box but never reach the model (left in, they would cost tokens). Off = the historic behaviour. |
| strip_newlines | BOOLEAN | true | Flatten line breaks into spaces after the comment pass. |
| external_mode | COMBO | append | What to do with a wired external text (e.g. Florence2): add it behind, in front, or let it REPLACE the segments. |
| comment_markers | STRING | // | Which markers start a comment (used by strip_comments). SPACE-SEPARATED list, e.g. '// # ***'. A marker counts at the line start or after a space, and everything up to the end of the line goes - so '//Comment' and '// Comment' both work, while 'https://...' survives. Empty = nothing is stripped. |
| pos_externalopt | STRING | External positive text (Florence2, a Stack's trigger words, ...). It shows up in the node preview. | |
| neg_externalopt | STRING | External negative text. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| positive | CONDITIONING | — |
| negative | CONDITIONING | — |
| positive_text | STRING | — |
| negative_text | STRING | — |
| full_text | STRING | — |