Skutils Text Preview
A Text Preview That Actually Survives Ctrl+S
You wired a wildcard or prompt-builder node into a text display node, ran it, saw the final prompt resolve into something worth keeping - and then hit Ctrl+S. Reload the workflow tomorrow and the box is empty. The text was never anywhere except the last run's output stream.
Skutils Text Preview shows text on the node and writes it into the saved workflow, so the file documents itself. It's a small thing that fills a gap you've probably already noticed and quietly worked around by pasting the text somewhere else.
The mechanism: put the text in a widget
ComfyUI's save format is not complicated. When you save a workflow it serializes each node's widget values into the JSON - that's why your seed, your prompt text and your LoRA strength come back when you reload. The workflow chunk embedded in every saved PNG is that same UI document.
The ordinary text-preview nodes don't use that channel. Their display box is a widget marked serialize: false, filled only from the ephemeral execution payload - visible while the app is open, gone the moment you save or switch workflows.
This node's trick is that the displayed text lives in a regular, serialized text widget. Two halves make that work:
- The Python node (
text_preview/text_preview.py) declares one input,text, as aSTRINGport withforceInput- so it's a port, not a field you type into - andpreview()returns{"ui": {"text": [text]}}. No outputs, no return types: it's a terminalOUTPUT_NODE. - A frontend extension (
web/text_preview.js) hooks that node specifically. OnonExecutedit creates a real multiline string widget via ComfyWidgets, sets it read-only, and drops the executed text in. On reload it rebuilds the widget from the savedwidgets_values.
The visible consequence: save the workflow and the text is in the .json; open it later and the text is back on the node with no re-run. The extension has to stash incoming widget values before the frontend configures the node, because the modern frontend strips programmatically added widgets.
Using it
Add the node and connect any string output into text - a wildcard processor, an LLM node, a text concatenate, a prompt-from-file loader. Run once. The text appears greyed out in a read-only text area inside the node body. It stays read-only by design: what you see is exactly what's stored.
The schema defines exactly one input - text - and no outputs, so there's nothing else to configure.
Being an OUTPUT_NODE has a side effect worth planning around: output nodes always execute, and ComfyUI caches backwards from them. Hanging a text preview off an expensive branch pins that branch into every run - usually what you want, and also how you'd discover it forces a re-roll of a prompt builder you thought was cached.
What gets saved, and what doesn't
| Where | Stored? |
|---|---|
| File → Save / Ctrl+S .json | Yes - it's a normal widget value |
| Workflow embedded in a saved image | Yes - same UI document, so dragging the image back brings the text |
| Save (API format) | No |
| A headless run (--prompt, no browser) | No - nothing displayed, nothing copied |
The API-format and headless gaps aren't this node's fault: API JSON stores only the executable graph, never widget values, so every node's seed and prompt vanish from it too. The practical upshot is that the text only enters the JSON after a run that executed this node - save a workflow you never ran and you've saved an empty preview.
Install
ComfyUI Manager → search skkut-utils, or:
cd ComfyUI/custom_nodes
git clone https://github.com/skkut/ComfyUI-skkut-utils.git
Restart ComfyUI, then hard-refresh the browser tab (Ctrl+Shift+R). That last step matters here: the feature is half frontend JS, and a cached bundle is the likeliest reason the node appears in the menu but stays blank.
No dependencies for this node. The pack's requirements.txt holds one optional package, piexif, used only by its Save Compressed Weppy node. Installing gets you six utilities at once, two of which self-activate with no opt-in - OS-synced theme and a floating job timer. If your theme changes when your desktop theme changes, that's this.
If the text comes back blank
Check the file before you blame the node: open the workflow .json and look at this node's widgets_values. If your text is in there, the value survived and what failed is the rebuild on load - the fragile half of the design, since it depends on the frontend's widget lifecycle. Hard-refresh; a stale frontend build is the usual culprit. If widgets_values is empty, the node never ran in a browser, or you saved an API-format export by mistake.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | Text to display. Connect any STRING output; the text shown after a run is stored in the node and saved with the workflow JSON. |
Outputs (0)
No outputs