const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; const HEX_HASH_RE = /^[0-9a-f]{20,}$/i; interface WorkflowLabelNode { id?: unknown; type?: unknown; class_type?: unknown; comfyClass?: unknown; classType?: unknown; title?: unknown; name?: unknown; properties?: { subgraph_name?: unknown; title?: unknown; name?: unknown; label?: unknown; }; subgraph?: { name?: unknown }; subgraph_instance?: { name?: unknown }; } function _firstText(...values: unknown[]): string { for (const value of values) { const text = String(value || "").trim(); if (text) return text; } return ""; } export function isOpaqueWorkflowNodeType(type: any): boolean { const text = String(type || "").trim(); return Boolean(text) && (UUID_RE.test(text) || HEX_HASH_RE.test(text)); } export function getWorkflowNodeRawType(node: WorkflowLabelNode | null | undefined): string { return String(node?.type || node?.class_type || node?.comfyClass || node?.classType || "").trim(); } export function getWorkflowNodeTitle(node: WorkflowLabelNode | null | undefined): string { return _firstText( node?.properties?.subgraph_name, node?.title, node?.properties?.title, node?.properties?.name, node?.properties?.label, node?.name, node?.subgraph?.name, node?.subgraph_instance?.name, ); } export function getWorkflowNodeDisplayName(node: WorkflowLabelNode | null | undefined): string { const type = getWorkflowNodeRawType(node); const title = getWorkflowNodeTitle(node); if (title && (!type || isOpaqueWorkflowNodeType(type) || title !== type)) return title; if (type && !isOpaqueWorkflowNodeType(type)) return type; if (title) return title; if (type) return "Subgraph"; return String(node?.id || "Node").trim() || "Node"; } export function getWorkflowNodeTypeLabel(node: WorkflowLabelNode | null | undefined): string { const type = getWorkflowNodeRawType(node); if (type && !isOpaqueWorkflowNodeType(type)) return type; return type ? "Subgraph" : "Node"; }