From d1108867e235c9df43d3a6cb9c63d664df20490d Mon Sep 17 00:00:00 2001
From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com>
Date: Sat, 1 Nov 2025 10:00:56 +0200
Subject: [PATCH] feat(TextPreviewWidget): add minimal support for
[[label|url]] links (#6482)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
This PR adds a minimal, backward-compatible way to render **labeled
hyperlinks** in node text previews without enabling full Markdown
rendering.
The syntax is:
```
[[Label|https://example.com]]
```
Links open in a new tab and preserve the existing look and behavior of
the widget.
## Motivation
I first implemented a `Markdown`-based version that correctly rendered
`[label](url)` and other inline Markdown. After some consideration, I
have decided against shipping Markdown here because it risks breaking
existing custom nodes that already rely on
`PromptServer.instance.send_progress_text` with the current
`linkifyHtml` --> `nl2br` behavior. The token approach is **smaller**,
safer, and avoids surprises.
## Changes
* No global behavior change.
* No new dependencies.
* Token parsing is opt-in. If a node does not emit `[[label|url]]`,
behavior is unchanged.
## ComfyUI backend changes
PR to ComfyUI with these will come later(as first version of ComfyUI
with these frontend changes should be released), and it will contain:
```python
def _display_text(
node_cls: type[IO.ComfyNode],
text: Optional[str],
*,
status: Optional[Union[str, int]] = None,
price: Optional[float] = None,
results: Optional[Union[list[str], str]] = None,
) -> None:
display_lines: list[str] = []
if status:
display_lines.append(f"Status: {status.capitalize() if isinstance(status, str) else status}")
if price is not None:
display_lines.append(f"Price: ${float(price):,.4f}")
if results: # New code starts
if isinstance(results, str):
display_lines.append(f"Result link: [[1|{results}]]")
elif len(results) == 1:
display_lines.append(f"Result link: [[1|{results[0]}]]")
else:
links = ", ".join(f"[[{i}|{u}]]" for i, u in enumerate(results, start=1))
display_lines.append(f"Result links: {links}") # New code ends
if text is not None:
display_lines.append(text)
if display_lines:
PromptServer.instance.send_progress_text("\n".join(display_lines), get_node_id(node_cls))
```
## Screenshots (if applicable)
┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6482-feat-TextPreviewWidget-add-minimal-support-for-label-url-links-29d6d73d365081e9ac97dd7f41e85d8f)
by [Unito](https://www.unito.io)
---
.../graph/widgets/TextPreviewWidget.vue | 28 ++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/src/components/graph/widgets/TextPreviewWidget.vue b/src/components/graph/widgets/TextPreviewWidget.vue
index 33ab748748..68c2c09550 100644
--- a/src/components/graph/widgets/TextPreviewWidget.vue
+++ b/src/components/graph/widgets/TextPreviewWidget.vue
@@ -27,7 +27,33 @@ const props = defineProps<{
const executionStore = useExecutionStore()
const isParentNodeExecuting = ref(true)
-const formattedText = computed(() => nl2br(linkifyHtml(modelValue.value)))
+const formattedText = computed(() => {
+ const src = modelValue.value
+ // Turn [[label|url]] into placeholders to avoid interfering with linkifyHtml
+ const tokens: { label: string; url: string }[] = []
+ const holed = src.replace(
+ /\[\[([^|\]]+)\|([^\]]+)\]\]/g,
+ (_m, label, url) => {
+ tokens.push({ label: String(label), url: String(url) })
+ return `__LNK${tokens.length - 1}__`
+ }
+ )
+
+ // Keep current behavior (auto-link bare URLs + \n ->
)
+ let html = nl2br(linkifyHtml(holed))
+
+ // Restore placeholders as ... (minimal escaping + http default)
+ html = html.replace(/__LNK(\d+)__/g, (_m, i) => {
+ const { label, url } = tokens[+i]
+ const safeHref = url.replace(/"/g, '"')
+ const safeLabel = label.replace(//g, '>')
+ return /^https?:\/\//i.test(url)
+ ? `${safeLabel}`
+ : safeLabel
+ })
+
+ return html
+})
let parentNodeId: NodeId | null = null
onMounted(() => {