comfyui-mcp
Version:
Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),
86 lines • 3.3 kB
JavaScript
// A small, dependency-free HTML → Markdown converter, scoped to the kind of
// markup CivitAI model/version descriptions use (paragraphs, line breaks,
// headings, bold/italic, links, lists, code, blockquotes). It is deliberately
// forgiving: anything it doesn't recognise is stripped rather than escaped, so
// the output is always readable plain-ish Markdown for an agent to consume — it
// is NOT a faithful round-trippable transform.
const NAMED_ENTITIES = {
amp: "&",
lt: "<",
gt: ">",
quot: '"',
apos: "'",
nbsp: " ",
mdash: "—",
ndash: "–",
hellip: "…",
copy: "©",
reg: "®",
trade: "™",
deg: "°",
};
function decodeEntities(s) {
return s.replace(/&(#x?[0-9a-f]+|[a-z]+);/gi, (m, body) => {
if (body[0] === "#") {
const code = body[1] === "x" || body[1] === "X"
? parseInt(body.slice(2), 16)
: parseInt(body.slice(1), 10);
return Number.isFinite(code) ? String.fromCodePoint(code) : m;
}
const named = NAMED_ENTITIES[body.toLowerCase()];
return named ?? m;
});
}
/**
* Convert a subset of HTML to Markdown. Returns "" for empty/whitespace input.
*/
export function htmlToMarkdown(html) {
if (!html)
return "";
let s = html;
// Drop elements whose content is never useful as text.
s = s.replace(/<(script|style|head)\b[\s\S]*?<\/\1>/gi, "");
// Comments.
s = s.replace(/<!--[\s\S]*?-->/g, "");
// Inline formatting.
s = s.replace(/<\s*(strong|b)\b[^>]*>([\s\S]*?)<\/\s*\1\s*>/gi, "**$2**");
s = s.replace(/<\s*(em|i)\b[^>]*>([\s\S]*?)<\/\s*\1\s*>/gi, "_$2_");
s = s.replace(/<\s*code\b[^>]*>([\s\S]*?)<\/\s*code\s*>/gi, "`$1`");
s = s.replace(/<\s*a\b[^>]*href\s*=\s*["']([^"']*)["'][^>]*>([\s\S]*?)<\/\s*a\s*>/gi, (_m, href, text) => {
const t = text.trim() || href;
return href ? `[${t}](${href})` : t;
});
// Headings → ATX. Level from the tag number.
s = s.replace(/<\s*h([1-6])\b[^>]*>([\s\S]*?)<\/\s*h\1\s*>/gi, (_m, lvl, text) => `\n\n${"#".repeat(Number(lvl))} ${text.trim()}\n\n`);
// List items → "- " lines (ordered lists also become dashes; good enough).
s = s.replace(/<\s*li\b[^>]*>([\s\S]*?)<\/\s*li\s*>/gi, (_m, text) => {
return `\n- ${text.trim()}`;
});
s = s.replace(/<\/?\s*(ul|ol)\b[^>]*>/gi, "\n");
// Blockquotes.
s = s.replace(/<\s*blockquote\b[^>]*>([\s\S]*?)<\/\s*blockquote\s*>/gi, (_m, text) => "\n" +
text
.trim()
.split(/\n/)
.map((l) => `> ${l.trim()}`)
.join("\n") +
"\n");
// Block separators.
s = s.replace(/<\s*br\s*\/?\s*>/gi, "\n");
s = s.replace(/<\/\s*p\s*>/gi, "\n\n");
s = s.replace(/<\s*p\b[^>]*>/gi, "");
s = s.replace(/<\/?\s*div\b[^>]*>/gi, "\n");
s = s.replace(/<\s*hr\s*\/?\s*>/gi, "\n\n---\n\n");
// Strip any remaining tags.
s = s.replace(/<[^>]+>/g, "");
s = decodeEntities(s);
// Normalise whitespace: trim each line, collapse 3+ blank lines to one.
s = s
.split("\n")
.map((l) => l.replace(/[ \t]+$/g, ""))
.join("\n")
.replace(/\n{3,}/g, "\n\n")
.trim();
return s;
}
//# sourceMappingURL=html-to-markdown.js.map