Nodes/ComfyUI-DonutNodes/Donut Text · Recursive Wildcards
ComfyUI Node

Donut Text · Recursive Wildcards

Wildcards That Reference Other Wildcards — Recursive Prompt Expansion Without the Blowup

By DonutsDelivery·Created about a year ago·Updated 2 days ago· 22
Donut Text · Recursive Wildcards
    • text
    text
    seed0
    max_depth128
    missing
    prefix
    suffix
    separator

    By the time you've used wildcards for a week you've hit the wall they all share: __pose__ files that themselves contain __pose__ tokens, or a "random prompt generator" that's really several wildcard files mashed together until they blow the stack. Donut Text is a text-expansion node that treats nesting as a feature. It resolves wildcards, then resolves whatever those resolved, and keeps going until the text is stable or it hits a safety limit - hence "recursive wildcards". Same seed, same files, same output, every time.

    Drop it into the positive prompt position of any workflow and wire its STRING output into your CLIP encoder. The syntax is the familiar wildcard dialect, plus a few toys: __filename__ pulls a random line from filename.txt in your wildcards folder, {a|b|c} is an inline choice, <random:0.5:1.2> rolls a number in a range, and %macro% is a macro. Because a wildcard file can itself contain any of these tokens, max_depth (default 128) bounds how deep the recursion digs before it gives up, and the node refuses to loop forever with a clear "cyclic expansion" error instead of freezing.

    The tokens worth knowing

    • __name__ - pick a random line from ComfyUI/wildcards/name.txt (or wherever you've pointed the wildcards folders). 3$$__name__ picks three lines; __name|word1|word2__ filters to lines containing a whole-word match.
    • Repeated tokens get special modifiers: __!name__ forces the same pick as the first time, __+name__/__-name__ step one line forward/back. That's the author's deliberate choice to keep "Mikey-style" selection semantics so existing wildcard collections behave like you expect.
    • {option A|option B} inline random choice; <random:0.5:1.2> random float to four decimals.
    • %date:yyyyMMdd% formats today's date (codes for year, month, day, hour, minute, second), and %node.widget% reads another node's widget value live - %12.positive% grabs whatever node 12 currently has in its prompt box, which is a surprisingly handy trick for reusing one prompt in several places without duplicate typing.

    Everything is seeded from the seed input, so a run is deterministic: roll a seed, find a composition you love, and the exact text that produced it is reproducible. The missing dropdown decides what happens when you reference a file that doesn't exist: error (loud, default - you'll see exactly which name failed and where it looked), keep (leave the __token__ in the text, useful for optional groups), or empty (silently drop it). For a beginner, keep is the friendly middle ground while you build your library; error is the one to switch to when things mysteriously stop changing.

    Wiring and caveats

    prefix and suffix are wire-in inputs, and separator joins them to the main text - handy for stamping the same style tag onto every prompt without editing each one. The node re-evaluates cheaply on changes (it fingerprints your wildcards folder), so editing a .txt file invalidates the cached result without re-encoding your CLIP text.

    The one honest gotcha: with dynamicPrompts explicitly disabled, this node does not queue multiple generations for {a|b} choices - one seed, one expansion. If you want a batch of variations, randomize the seed (or drive it from a Donut Seed Plan text seed) rather than expecting multi-run behavior. It ships in ComfyUI-DonutNodes; install the pack via ComfyUI Manager ("DonutNodes") or git clone into ComfyUI/custom_nodes + pip install -r requirements.txt with ComfyUI's Python. Nothing to download - it reads plain .txt files from your wildcards folder, and it'll happily work with wildcard sets you already have.

    Categorydonut/text

    Inputs (7)

    NameTypeDefaultDescription
    textSTRING
    seedINT00–9007199254740991
    max_depthINT1281–1024
    missingCOMBO3 options: error, keep, empty
    prefixoptSTRING
    suffixoptSTRING
    separatoroptSTRING

    Outputs (1)

    NameTypeDescription
    textSTRING