flux-klein-api / index.html
paktinker's picture
Upload 3 files
3848811 verified
Raw
History Blame Contribute Delete
9.08 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flux2 Klein Generator</title>
<style>
:root {
--bg-color: #121212;
--surface-color: #1e1e1e;
--primary-color: #6200ea;
--text-color: #e0e0e0;
--border-color: #333;
}
body {
font-family: system-ui, -apple-system, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
display: flex;
justify-content: center;
padding: 2rem;
margin: 0;
}
.container {
background-color: var(--surface-color);
padding: 2rem;
border-radius: 12px;
width: 100%;
max-width: 800px;
box-shadow: 0 8px 24px rgba(0,0,0,0.5);
}
h2 {
margin-top: 0;
font-weight: 500;
}
textarea {
width: 100%;
height: 120px;
background-color: var(--bg-color);
color: var(--text-color);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
font-family: inherit;
font-size: 1rem;
box-sizing: border-box;
resize: vertical;
margin-bottom: 1rem;
}
textarea:focus {
outline: none;
border-color: var(--primary-color);
}
button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 0.75rem 1.5rem;
font-size: 1rem;
border-radius: 8px;
cursor: pointer;
transition: opacity 0.2s;
width: 100%;
font-weight: bold;
}
button:hover {
opacity: 0.9;
}
button:disabled {
background-color: var(--border-color);
cursor: not-allowed;
}
.status {
margin-top: 1rem;
font-size: 0.9rem;
color: #aaa;
text-align: center;
min-height: 1.5em;
}
.result-container {
margin-top: 1.5rem;
text-align: center;
}
img {
max-width: 100%;
border-radius: 8px;
display: none;
border: 1px solid var(--border-color);
}
</style>
</head>
<body>
<div class="container">
<h2>🎨 Flux 2 Klein Generator</h2>
<textarea id="promptInput" placeholder="Enter your image prompt here...">Photoreal studio product photo of a matte black ceramic coffee mug sitting centered on a raw, weathered oak wooden block. Clear, highly legible text printed on the mug reads: "FAST 4-STEP". Below it, a smaller line reads: "KLEIN 4B". Wisps of realistic steam rise from hot coffee inside the mug. Studio softbox lighting from camera left creates a subtle golden rim highlight, casting soft, smooth shadows on the wood. Crisp focus with razor-sharp details, shallow depth of field, minimalist clean beige background.</textarea>
<button id="generateBtn" onclick="generateImage()">Generate Image</button>
<div id="status" class="status"></div>
<div class="result-container">
<img id="resultImage" alt="Generated Output" />
</div>
</div>
<script>
const workflowTemplate = {
"76": { "inputs": { "value": "" }, "class_type": "PrimitiveStringMultiline" },
"97": { "inputs": { "filename_prefix": "ComfyUI", "images": [ "77:82", 0 ] }, "class_type": "SaveImage" },
"77:80": { "inputs": { "sampler_name": "euler" }, "class_type": "KSamplerSelect" },
"77:81": { "inputs": { "noise": [ "77:86", 0 ], "guider": [ "77:90", 0 ], "sampler": [ "77:80", 0 ], "sigmas": [ "77:93", 0 ], "latent_image": [ "77:83", 0 ] }, "class_type": "SamplerCustomAdvanced" },
"77:82": { "inputs": { "samples": [ "77:81", 0 ], "vae": [ "77:89", 0 ] }, "class_type": "VAEDecode" },
"77:83": { "inputs": { "width": [ "77:84", 0 ], "height": [ "77:85", 0 ], "batch_size": 1 }, "class_type": "EmptyFlux2LatentImage" },
"77:84": { "inputs": { "value": 1024 }, "class_type": "PrimitiveInt" },
"77:85": { "inputs": { "value": 1024 }, "class_type": "PrimitiveInt" },
"77:86": { "inputs": { "noise_seed": 0 }, "class_type": "RandomNoise" },
"77:87": { "inputs": { "unet_name": "flux-2-klein-4b-fp8.safetensors", "weight_dtype": "default" }, "class_type": "UNETLoader" },
"77:88": { "inputs": { "clip_name": "qwen_3_4b.safetensors", "type": "flux2", "device": "default" }, "class_type": "CLIPLoader" },
"77:89": { "inputs": { "vae_name": "flux2-vae.safetensors" }, "class_type": "VAELoader" },
"77:90": { "inputs": { "cfg": 1, "model": [ "77:87", 0 ], "positive": [ "77:92", 0 ], "negative": [ "77:91", 0 ] }, "class_type": "CFGGuider" },
"77:91": { "inputs": { "conditioning": [ "77:92", 0 ] }, "class_type": "ConditioningZeroOut" },
"77:92": { "inputs": { "text": [ "76", 0 ], "clip": [ "77:88", 0 ] }, "class_type": "CLIPTextEncode" },
"77:93": { "inputs": { "steps": 4, "width": [ "77:84", 0 ], "height": [ "77:85", 0 ] }, "class_type": "Flux2Scheduler" }
};
const COMFY_URL = "http://127.0.0.1:8188";
async function generateImage() {
const promptText = document.getElementById('promptInput').value.trim();
if (!promptText) return;
const btn = document.getElementById('generateBtn');
const statusEl = document.getElementById('status');
const imgEl = document.getElementById('resultImage');
btn.disabled = true;
statusEl.innerText = "Queuing workflow to ComfyUI...";
imgEl.style.display = 'none';
// 1. Prepare the JSON Payload
const workflow = JSON.parse(JSON.stringify(workflowTemplate)); // Deep copy
workflow["76"].inputs.value = promptText;
// Randomize seed so you don't get the exact same image twice
workflow["77:86"].inputs.noise_seed = Math.floor(Math.random() * 999999999999999);
try {
// 2. Submit Prompt to ComfyUI API
const response = await fetch(`${COMFY_URL}/prompt`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt: workflow })
});
if (!response.ok) throw new Error("Failed to reach ComfyUI.");
const data = await response.json();
const promptId = data.prompt_id;
statusEl.innerText = "Generating... (Pushing through the GPU)";
// 3. Poll for the result
const pollInterval = setInterval(async () => {
try {
const historyRes = await fetch(`${COMFY_URL}/history/${promptId}`);
const historyData = await historyRes.json();
// If promptId exists in history, the job is completely done
if (historyData[promptId]) {
clearInterval(pollInterval);
statusEl.innerText = "Generation complete!";
// 4. Extract the final image filename from the SaveImage node (Node 97)
const outputs = historyData[promptId].outputs;
if (outputs && outputs["97"] && outputs["97"].images) {
const filename = outputs["97"].images[0].filename;
// 5. Construct the view URL
imgEl.src = `${COMFY_URL}/view?filename=${filename}&type=output`;
imgEl.style.display = 'block';
} else {
statusEl.innerText = "Error: Couldn't find image output in Node 97.";
}
btn.disabled = false;
}
} catch (pollErr) {
console.error("Polling error:", pollErr);
}
}, 2000); // Check every 2 seconds
} catch (err) {
console.error(err);
statusEl.innerText = "Error: Make sure ComfyUI is running and CORS is enabled.";
btn.disabled = false;
}
}
</script>
</body>
</html>