minimax-h3-workflows / index.html
Leo91fr's picture
Upload index.html with huggingface_hub
0a2ad20 verified
Raw
History Blame Contribute Delete
16.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MiniMax-H3 ComfyUI Workflows Showcase</title>
<style>
:root {
--bg: #0d1020;
--panel: #151a30;
--panel-2: #1b2137;
--border: #2a3150;
--text: #e8eaf6;
--muted: #9aa3c7;
--accent: #7c5cff;
--accent-2: #22c3ff;
--chip: #2a2447;
--good: #4ade80;
--radius: 12px;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text);
font: 15px/1.55 "Segoe UI", system-ui, -apple-system, sans-serif; }
a { color: var(--accent-2); text-decoration: none; }
a:hover { text-decoration: underline; }
#shell { max-width: 1080px; margin: 0 auto; padding: 28px 20px 80px; }
header h1 { margin: 0 0 4px; font-size: 30px; letter-spacing: .2px; }
header h1 .badge { font-size: 12px; vertical-align: middle; margin-left: 8px;
background: var(--chip); color: var(--accent-2); border: 1px solid var(--border);
padding: 3px 8px; border-radius: 999px; font-weight: 600; }
header p.sub { color: var(--muted); margin: 0 0 18px; max-width: 820px; }
.links { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.links a { border: 1px solid var(--border); background: var(--panel);
padding: 7px 12px; border-radius: 8px; font-size: 13.5px; }
.stats { display: flex; flex-wrap: wrap; gap: 14px; margin: 4px 0 26px; }
.stat { flex: 1 1 160px; background: var(--panel); border: 1px solid var(--border);
border-radius: var(--radius); padding: 12px 14px; }
.stat .num { font-size: 22px; font-weight: 700; color: var(--accent-2); }
.stat .lbl { font-size: 12.5px; color: var(--muted); }
#tabs { display: flex; flex-wrap: wrap; gap: 6px; border-bottom: 1px solid var(--border);
margin: 10px 0 22px; }
#tabs button { background: transparent; border: 1px solid transparent; color: var(--muted);
padding: 9px 14px; border-radius: 8px 8px 0 0; cursor: pointer; font-size: 14px; }
#tabs button:hover { color: var(--text); background: var(--panel); }
#tabs button.active { color: var(--text); border-color: var(--border);
border-bottom-color: transparent; background: var(--panel); font-weight: 600; }
.tabpage { display: none; }
.tabpage.active { display: block; animation: fade .18s ease; }
@keyframes fade { from { opacity: 0; transform: translateY(4px);} to { opacity: 1; } }
.wf-card { background: var(--panel); border: 1px solid var(--border);
border-radius: var(--radius); padding: 20px 22px; margin-bottom: 18px; }
.wf-card h3 { margin: 0 0 4px; font-size: 19px; }
.wf-card .sub { color: var(--muted); margin: 0 0 10px; }
.wf-card .desc { margin: 0 0 12px; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.chip { background: var(--chip); border: 1px solid var(--border);
border-radius: 999px; padding: 3px 10px; font-size: 12px; color: #c9cdf0; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 820px) { .grid { grid-template-columns: 1fr; } }
.panel { background: var(--panel-2); border: 1px solid var(--border);
border-radius: 10px; padding: 12px 14px; }
.panel h4 { margin: 0 0 8px; font-size: 13px; text-transform: uppercase;
letter-spacing: .6px; color: var(--muted); }
.panel .meta { font-size: 12.5px; color: var(--muted); margin-bottom: 8px; }
.inventory { max-height: 240px; overflow: auto; font-size: 13px; }
.inventory table { width: 100%; border-collapse: collapse; }
.inventory td { padding: 3px 6px; border-bottom: 1px solid var(--border); }
.inventory td.c { color: var(--accent-2); text-align: right; width: 48px;
font-variant-numeric: tabular-nums; }
.models ul { margin: 0; padding-left: 18px; font-size: 13px; }
.models li { margin-bottom: 4px; word-break: break-all; }
.models li button { all: unset; cursor: pointer; color: var(--accent-2); margin-left: 4px; }
.notes blockquote { margin: 0; padding: 4px 12px; border-left: 3px solid var(--accent-2);
color: #c6cbed; font-size: 13px; white-space: pre-line; }
.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
button.btn { border: 1px solid var(--accent); background: transparent; color: var(--text);
padding: 8px 14px; border-radius: 8px; cursor: pointer; font-size: 13.5px; }
button.btn:hover { background: rgba(124,92,255,.15); }
button.btn.primary { background: linear-gradient(90deg, var(--accent), var(--accent-2));
border: none; font-weight: 600; color: #fff; }
button.btn.primary:hover { filter: brightness(1.08); }
#overlay { position: fixed; inset: 0; background: rgba(5,8,18,.9); display: none;
align-items: center; justify-content: center; z-index: 50; padding: 24px; }
#overlay.open { display: flex; }
.modal { background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
width: min(980px, 100%); max-height: 90vh; display: flex; flex-direction: column; }
.modal h3 { margin: 0; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.modal pre { margin: 0; padding: 16px 20px; overflow: auto; font: 12.5px/1.5 Consolas,
"Cascadia Code", monospace; white-space: pre; color: #d5d9f5; }
.modal .mfoot { display: flex; gap: 10px; justify-content: flex-end;
padding: 12px 20px; border-top: 1px solid var(--border); }
.prompt-block { margin-bottom: 18px; }
.prompt-block pre { background: var(--panel-2); border: 1px solid var(--border);
border-radius: 10px; padding: 14px 16px; max-height: 420px; overflow: auto;
font: 13px/1.55 Consolas, monospace; white-space: pre-wrap; color: #d5d9f5;
margin: 8px 0 4px; }
.toast { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
background: var(--good); color: #052e13; font-weight: 600; padding: 9px 16px;
border-radius: 999px; opacity: 0; pointer-events: none; transition: opacity .2s; z-index: 99; }
.toast.show { opacity: 1; }
.foot { margin-top: 34px; color: var(--muted); font-size: 12.5px; }
</style>
</head>
<body>
<div id="shell">
<header>
<h1>🧩 MiniMax-H3 Workflows <span class="badge">showcase</span></h1>
<p class="sub">
An interactive reference for
<a href="https://huggingface.co/javawock7618/comfy-MiniMax-H3-workflows">javawock7618/comfy-MiniMax-H3-workflows</a> —
optimized <b>INT8</b> ComfyUI workflows for MiniMax-H3: FLF image-to-video, reference-to-video,
Bridge transitions, Face Refine, TTS/Video-to-Audio, plus Music 3 &amp; Ref2Image extras.
</p>
<div class="links">
<a href="https://huggingface.co/javawock7618/comfy-MiniMax-H3-workflows">📦 Source workflows</a>
<a href="https://huggingface.co/Comfy-Org/MiniMax-H3">🤖 Base model</a>
<a href="https://github.com/xmarre/ComfyUI-Spectrum-MiniMax-H3">⚡ Spectrum</a>
<a href="https://github.com/Carasibana/ComfyUI-H3-FaceRefine">🎭 Face Refine</a>
<a href="https://github.com/LBH-123-AI/Comfyui_Minimax_h3_latent_Upscaler">🪜 Latent Upscaler</a>
<a href="https://github.com/Oratorian/ComfyUI-Switchboard">🔀 Switchboard</a>
</div>
<div class="stats" id="stats"></div>
</header>
<div id="tabs"></div>
<section class="tabpage" data-tab="overview">
<div class="wf-card">
<h3>What is this collection?</h3>
<p class="desc">
Seven drop-in ComfyUI workflows built around the <b>INT8 quantized</b> MiniMax-H3 model
(FL2VA and Ref2VA checkpoints on <a href="https://huggingface.co/Comfy-Org/MiniMax-H3">Comfy-Org/MiniMax-H3</a>).
They keep first-frame/last-frame or reference-image conditioning, add <b>ControlNet-Union</b>
video control (OpenPose / Canny / Depth), support the <b>Latent Upscaler</b> (low-res
generation → high-res decode), and are wired through <b>ComfyUI-Switchboard</b>
<i>Group Bypass</i> groups so optional stages can be toggled without editing the graph.
</p>
<h4>Setup</h4>
<ol style="margin-left: 18px; padding-left: 0;">
<li>Update ComfyUI + ComfyUI-Manager.</li>
<li>Install the required custom nodes (Spectrum, FaceRefine, Latent Upscaler, Switchboard).</li>
<li>Download the INT8/quantized MiniMax-H3 model files into <code>ComfyUI/models</code>.</li>
<li>Grab the R2V Ref LoRA (and ControlNet-Union / Lightx2v / Turbo LoRAs) as needed.</li>
<li>Load the workflow JSON from this Space or the source repo and run it.</li>
</ol>
<p class="desc" style="margin-top:12px">
Each tab below lists the workflow's <b>node inventory</b> (incl. nested sub-graph nodes),
the <b>model files</b> it references, and its embedded notes. Use
<b>Preview JSON</b> to inspect a workflow, and <b>Download</b> to grab it directly.
</p>
</div>
</section>
<section id="workflow-tabs"></section>
<section class="tabpage" data-tab="prompts">
<div class="wf-card">
<h3>LLM prompt-engineering system prompts</h3>
<p class="desc">
The authors ship two system prompts for writing generation-ready captions. Feed them to any
capable LLM, give it your rough idea, and paste its output into the workflow prompt node.
</p>
<div class="prompt-block" id="prompt-fl2va"></div>
<div class="prompt-block" id="prompt-ref2va"></div>
</div>
</section>
<div class="foot">
Workflows &copy; <a href="https://huggingface.co/javawock7618">javawock7618</a> ·
built as a showcase Space · follow each component's own license.
</div>
</div>
<div id="overlay">
<div class="modal">
<h3 id="modal-title">Workflow JSON</h3>
<pre id="modal-pre">Loading…</pre>
<div class="mfoot">
<button class="btn" onclick="closeModal()">Close</button>
<button class="btn primary" onclick="copyModal()">Copy JSON</button>
</div>
</div>
</div>
<div class="toast" id="toast">Copied!</div>
<script src="data.js"></script>
<script>
const D = window.WORKFLOW_DATA;
const SPACE_ID = "Leo91fr/minimax-h3-workflows";
const STATIC_BASE = `https://${SPACE_ID.replace("/", "-").toLowerCase()}.static.hf.space/`;
function esc(s) {
return (s || "").replace(/&/g, "&amp;").replace(/</g, "&lt;")
.replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function toast(msg) {
const t = document.getElementById("toast");
t.textContent = msg || "Copied!";
t.classList.add("show");
setTimeout(() => t.classList.remove("show"), 1400);
}
function copyText(text, msg) {
if (navigator.clipboard) { navigator.clipboard.writeText(text).then(() => toast(msg)); }
else { const ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta);
ta.select(); document.execCommand("copy"); ta.remove(); toast(msg); }
}
function copyModel(name) { copyText(name, "Copied " + name); }
let modalFile = null;
async function openModal(file) {
modalFile = file;
document.getElementById("modal-title").textContent = file.split("/").pop();
document.getElementById("modal-pre").textContent = "Fetching…";
document.getElementById("overlay").classList.add("open");
try {
const r = await fetch(STATIC_BASE + file);
if (!r.ok) throw new Error(r.status);
const txt = await r.text();
const pretty = JSON.stringify(JSON.parse(txt), null, 1);
document.getElementById("modal-pre").textContent = pretty;
} catch (e) {
document.getElementById("modal-pre").textContent =
"Could not fetch the workflow JSON (are the workflow files uploaded?): " + e;
}
}
function closeModal() { document.getElementById("overlay").classList.remove("open"); }
function copyModal() {
const pre = document.getElementById("modal-pre");
if (pre.textContent.startsWith("Fetching")) return;
copyText(pre.textContent);
}
document.getElementById("overlay").addEventListener("click", (e) => {
if (e.target === document.getElementById("overlay")) closeModal();
});
document.addEventListener("keydown", (e) => { if (e.key === "Escape") closeModal(); });
function statHtml(label, num) {
return `<div class="stat"><div class="num">${num}</div><div class="lbl">${label}</div></div>`;
}
function buildStats() {
const wfs = D.workflows;
const totalNodes = wfs.reduce((a, w) => a + w.totalNodes, 0);
const models = new Set(wfs.flatMap(w => w.models));
document.getElementById("stats").innerHTML =
statHtml("workflows", wfs.length) +
statHtml("total node instances", totalNodes) +
statHtml("model files referenced", models.size) +
statHtml("all INT8-quantized", "7");
}
function inventoryTable(w) {
if (!w.inventory.length) return `<div class="meta">No node data.</div>`;
const rows = w.inventory.slice(0, 28).map(i =>
`<tr><td>${esc(i.type)}</td><td class="c">${i.count}</td></tr>`).join("");
return `<table>${rows}</table>`;
}
function modelsHtml(w) {
if (!w.models.length) return `<div class="meta">None referenced.</div>`;
return `<ul>` + w.models.map(m =>
`<li>${esc(m)}<button title="Copy filename" onclick="copyModel('${esc(m).replace(/'/g, "&#39;")}')">⧉</button></li>`
).join("") + `</ul>`;
}
function notesHtml(w) {
if (!w.notes.length) return `<div class="meta">None.</div>`;
return `<blockquote>${esc(w.notes.join("\n\n"))}</blockquote>`;
}
function wfCard(w) {
return `
<div class="wf-card">
<h3>${esc(w.title)}</h3>
<p class="sub">${esc(w.subtitle)}</p>
<p class="desc">${esc(w.description)}
<span class="meta" style="display:block;margin-top:6px">top-level nodes: <b>${w.topLevelNodes}</b> · total incl. sub-graphs: <b>${w.totalNodes}</b></span>
</p>
<div class="chips">${w.features.map(f => `<span class="chip">${esc(f)}</span>`).join("")}</div>
<div class="grid">
<div class="panel inventory">
<h4>Node inventory</h4>
<div class="meta">top ${Math.min(w.inventory.length, 28)} of ${w.inventory.length} node types</div>
${inventoryTable(w)}
</div>
<div class="panel models">
<h4>Referenced model files</h4>
${modelsHtml(w)}
<h4 style="margin-top:12px">Embedded notes</h4>
${notesHtml(w)}
</div>
</div>
<div class="actions">
<button class="btn primary" onclick="openModal('${w.file}')">Preview JSON</button>
<a class="btn" style="display:inline-flex;align-items:center" href="${STATIC_BASE}${w.file}">↓ Download</a>
</div>
</div>`;
}
function buildWorkflowTabs() {
const host = document.getElementById("workflow-tabs");
host.innerHTML = D.workflows.map(w =>
`<section class="tabpage" data-tab="wf-${w.file.split("/").pop().replace(/\\./g, "-")}">${wfCard(w)}</section>`).join("");
}
function buildPromptBlocks() {
for (const key of ["fl2va", "ref2va"]) {
const el = document.getElementById("prompt-" + key);
const label = key === "fl2va" ? "FL2VA (image-to-video) system prompt" : "Ref2VA / bridge system prompt";
el.innerHTML = `
<h4 style="margin:0 0 2px">${label}</h4>
<pre>${esc(D.prompts[key])}</pre>
<button class="btn" onclick="copyText(document.getElementById('prompt-${key}').querySelector('pre').textContent, 'Prompt copied!')">Copy prompt</button>`;
}
}
function buildTabs() {
const tabs = [
["overview", "Overview"],
...D.workflows.map(w => [`wf-${w.file.split("/").pop().replace(/\\./g, "-")}`, w.title]),
["prompts", "LLM prompts"],
];
const bar = document.getElementById("tabs");
bar.innerHTML = tabs.map(([id, label]) =>
`<button data-tab="${id}">${esc(label)}</button>`).join("");
bar.addEventListener("click", (e) => {
const b = e.target.closest("button");
if (!b) return;
selectTab(b.dataset.tab);
});
selectTab("overview");
}
function selectTab(id) {
document.querySelectorAll("#tabs button").forEach(b => b.classList.toggle("active", b.dataset.tab === id));
document.querySelectorAll(".tabpage").forEach(p => p.classList.toggle("active", p.dataset.tab === id));
window.scrollTo({ top: 0, behavior: "smooth" });
}
buildStats();
buildWorkflowTabs();
buildPromptBlocks();
buildTabs();
</script>
</body>
</html>