XMarkdownSave
Turn a run into a readable report file
- content
- save_path
ComfyUI is a machine for generating files, but generating documentation about what you generated is usually a chore you do by hand afterward. XMarkdownSave is the pack's answer: a save node that assembles a markdown document from up to three parts - header, main content, footer - with explicit separator controls, and writes it to your output folder. It's for the people who want a run report, a prompt log, or a caption file that a human (or a script) can actually read next week instead of a pile of undifferentiated text dumps.
How it works
The node takes up to three text sections - header_text, text_content, and footer_text - and each has a connected-input override: header_input, main_text_input, footer_input. When a string input is connected, it takes priority over the fallback textbox; otherwise the textbox value is used. That gives you the useful pattern of having dynamic content (a prompt, a seed, a timestamp from XDateTimeString) on an input port while keeping static boilerplate in the textboxes.
Then the assembly logic, which is the part that distinguishes this from "just append strings": before_main_separator and after_main_separator control whether a newline is inserted between sections, and before_main_newline_count / after_main_newline_count set exactly how many. The author's guidance is pointed: use the separator controls instead of relying on trailing/leading blank lines in the text fields, because blank lines inside a textbox are easy to lose and annoying to reason about. Explicit separators make the output predictable.
The result is saved as a .md file using the pack's usual filename_prefix + subfolder with datetime placeholders (%Y% %m% %d% %H% %M% %S%), defaulting to the Markdown subfolder.
Inputs and outputs
- header_text / header_input - the top section (input wins when connected).
- text_content / main_text_input - the main body.
- footer_text / footer_input - the bottom section.
- before_main_separator / after_main_separator -
noneornewline. - before_main_newline_count / after_main_newline_count - 1–99 newlines when the separator is newline.
Outputs: content (the final assembled markdown, so you can also log or display it) and save_path (relative to the output dir).
Installing it
Part of ComfyUI-Xz3r0-Nodes. ComfyUI Manager: search ComfyUI-Xz3r0-Nodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Xz3r0-M/ComfyUI-Xz3r0-Nodes.git
cd ComfyUI-Xz3r0-Nodes
pip install -r requirements.txt
Restart ComfyUI. No extra dependencies - plain file writing.
Gotchas
The pattern worth adopting: feed the dynamic bits through the input ports and keep the static scaffolding in the fallback textboxes. It's tempting to put everything in textboxes, but then every run overwrites the same content and you lose the "report per run" benefit. And respect the separator controls - if your markdown comes out with sections glued together or triple-spaced, the newline counts are where to look. It's a plain text writer, so no markdown rendering happens in the node itself; render it in any markdown viewer later.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| filename_prefix | STRING | ComfyUI_%Y%-%m%-%d%_%H%-%M%-%S% | Filename prefix, supports datetime placeholders: %Y%, %m%, %d%, %H%, %M%, %S% |
| subfolder | STRING | Markdown | Subfolder name (no path separators allowed), supports datetime placeholders: %Y%, %m%, %d%, %H%, %M%, %S% |
| header_text | STRING | Fallback multiline header text used when header_input is not connected. Use separator controls below instead of relying on trailing blank lines | |
| before_main_separator | COMBO | none | Separator inserted between header_text and the main content: none or newline |
| before_main_newline_count | INT | 11–99 | Number of newline characters inserted before the main content when before_main_separator is newline |
| text_content | STRING | Fallback multiline text content used when text_input is not connected | |
| after_main_separator | COMBO | none | Separator inserted between the main content and footer_text: none or newline |
| after_main_newline_count | INT | 11–99 | Number of newline characters inserted after the main content when after_main_separator is newline |
| footer_text | STRING | Fallback multiline footer text used when footer_input is not connected. Use separator controls above instead of relying on leading blank lines | |
| header_inputopt | STRING | Optional header string input. When connected, this input takes priority over header_text | |
| main_text_inputopt | STRING | Optional main content string input. When connected, input takes priority over text_content | |
| footer_inputopt | STRING | Optional footer string input. When connected, this input takes priority over footer_text |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| content | STRING | Final markdown content saved to the file |
| save_path | STRING | Saved file path relative to ComfyUI output directory |