From 1fc3b8d8badb49c92ecf63cf285c6632d4457d75 Mon Sep 17 00:00:00 2001 From: slimerplanet Date: Thu, 16 Nov 2023 18:10:29 +0200 Subject: [PATCH] "Realtime" Generation With LCM and ComfyUi --- dynamic/canvas.js | 236 +++++++++++++++ dynamic/dynamic.js | 515 +++++++++++++++++++++++++++++++++ dynamic/index.html | 235 +++++++++++++++ dynamic/style.css | 20 ++ output.css | 204 +++++++++++-- tailwind.config.js | 1 + workflows/lcm_api.json | 102 +++++++ workflows/lcm_img2img_api.json | 127 ++++++++ 8 files changed, 1410 insertions(+), 30 deletions(-) create mode 100644 dynamic/canvas.js create mode 100644 dynamic/dynamic.js create mode 100644 dynamic/index.html create mode 100644 dynamic/style.css create mode 100644 workflows/lcm_api.json create mode 100644 workflows/lcm_img2img_api.json diff --git a/dynamic/canvas.js b/dynamic/canvas.js new file mode 100644 index 0000000..f202868 --- /dev/null +++ b/dynamic/canvas.js @@ -0,0 +1,236 @@ +(function () { + var $ = function (id) { return document.getElementById(id) }; + + var canvas = this.__canvas = new fabric.Canvas('canvas', { + isDrawingMode: true, + backgroundColor: 'rgb(255,255,255)' + }); + canvas.on("mouse:up", Generate); + canvas.setDimensions({ width: 512, height: 512 }); + + fabric.Object.prototype.transparentCorners = false; + + var + drawingOptionsEl = $('drawing-mode-options'), + drawingColorEl = $('drawing-color'), + drawingLineWidthEl = $('drawing-line-width'), + clearEl = $('clear-canvas'); + + clearEl.onclick = function () { + canvas.clear() + canvas.backgroundColor = 'rgb(255,255,255)'; + canvas.renderAll(); + }; + + + var currentTool = "brush"; + const tools = [ + "select", + "brush", + "spray", + "square", + "circle", + ] + + + $("select").onclick = function () { + canvas.isDrawingMode = false; + changeTool("select"); + }; + $("brush").onclick = function () { + canvas.freeDrawingBrush = new fabric['PencilBrush'](canvas); + canvas.isDrawingMode = true; + changeTool("brush"); + }; + $("spray").onclick = function () { + canvas.freeDrawingBrush = new fabric['SprayBrush'](canvas); + canvas.isDrawingMode = true; + changeTool("spray"); + }; + + function changeTool(tool) { + currentTool = tool; + tools.forEach((tool) => { + $(tool).classList.remove("bg-blue-600"); + $(tool).classList.add("bg-gray-900"); + }); + $(tool).classList.remove("bg-gray-900"); + $(tool).classList.add("bg-blue-600"); + + if (canvas.freeDrawingBrush) { + var brush = canvas.freeDrawingBrush; + brush.color = drawingColorEl.value; + if (brush.getPatternSrc) { + brush.source = brush.getPatternSrc.call(brush); + } + brush.width = parseInt(drawingLineWidthEl.value, 10) || 1; + } + } + + + if (fabric.PatternBrush) { + var vLinePatternBrush = new fabric.PatternBrush(canvas); + vLinePatternBrush.getPatternSrc = function () { + + var patternCanvas = fabric.document.createElement('canvas'); + patternCanvas.width = patternCanvas.height = 10; + var ctx = patternCanvas.getContext('2d'); + + ctx.strokeStyle = this.color; + ctx.lineWidth = 5; + ctx.beginPath(); + ctx.moveTo(0, 5); + ctx.lineTo(10, 5); + ctx.closePath(); + ctx.stroke(); + + return patternCanvas; + }; + + var hLinePatternBrush = new fabric.PatternBrush(canvas); + hLinePatternBrush.getPatternSrc = function () { + + var patternCanvas = fabric.document.createElement('canvas'); + patternCanvas.width = patternCanvas.height = 10; + var ctx = patternCanvas.getContext('2d'); + + ctx.strokeStyle = this.color; + ctx.lineWidth = 5; + ctx.beginPath(); + ctx.moveTo(5, 0); + ctx.lineTo(5, 10); + ctx.closePath(); + ctx.stroke(); + + return patternCanvas; + }; + + var squarePatternBrush = new fabric.PatternBrush(canvas); + squarePatternBrush.getPatternSrc = function () { + + var squareWidth = 10, squareDistance = 2; + + var patternCanvas = fabric.document.createElement('canvas'); + patternCanvas.width = patternCanvas.height = squareWidth + squareDistance; + var ctx = patternCanvas.getContext('2d'); + + ctx.fillStyle = this.color; + ctx.fillRect(0, 0, squareWidth, squareWidth); + + return patternCanvas; + }; + + var diamondPatternBrush = new fabric.PatternBrush(canvas); + diamondPatternBrush.getPatternSrc = function () { + + var squareWidth = 10, squareDistance = 5; + var patternCanvas = fabric.document.createElement('canvas'); + var rect = new fabric.Rect({ + width: squareWidth, + height: squareWidth, + angle: 45, + fill: this.color + }); + + var canvasWidth = rect.getBoundingRect().width; + + patternCanvas.width = patternCanvas.height = canvasWidth + squareDistance; + rect.set({ left: canvasWidth / 2, top: canvasWidth / 2 }); + + var ctx = patternCanvas.getContext('2d'); + rect.render(ctx); + + return patternCanvas; + }; + + + } + + + + + + drawingLineWidthEl.onchange = function () { + canvas.freeDrawingBrush.width = parseInt(this.value, 10) || 1; + this.previousSibling.innerHTML = this.value; + }; + + drawingColorEl.onchange = function () { + canvas.freeDrawingBrush.color = this.value; + } + + if (canvas.freeDrawingBrush) { + canvas.freeDrawingBrush.color = drawingColorEl.value; + canvas.freeDrawingBrush.width = parseInt(drawingLineWidthEl.value, 10) || 1; + + } + + + canvas.on('object:added', function () { + if (!isRedoing) { + h = []; + } + isRedoing = false; + }); + + $("undo").onclick = undo; + $("redo").onclick = redo; + + document.onkeydown = function (e) { + //ctrl+z + if (e.ctrlKey && e.keyCode == 90) { + undo(); + } + if (e.ctrlKey && e.keyCode == 89) { + redo(); + } + //Del + if (e.keyCode == 46) { + canvas.remove(canvas.getActiveObject()); + } + } + + + var isRedoing = false; + var h = []; + function undo() { + if (canvas._objects.length > 0) { + h.push(canvas._objects.pop()); + canvas.renderAll(); + Generate(); + } + } + function redo() { + + if (h.length > 0) { + isRedoing = true; + canvas.add(h.pop()); + Generate(); + } + } + + document.getElementById('upload').onchange = function handleImage(e) { + var reader = new FileReader(); + reader.onload = function (event) { + var imgObj = new Image(); + imgObj.src = event.target.result; + imgObj.onload = function () { + + + var image = new fabric.Image(imgObj); + image.set({ + angle: 0, + padding: 10, + cornersize: 10, + }); + canvas.centerObject(image); + canvas.add(image); + + + canvas.renderAll(); + } + } + reader.readAsDataURL(e.target.files[0]); + } + +})(); diff --git a/dynamic/dynamic.js b/dynamic/dynamic.js new file mode 100644 index 0000000..5c7e5c3 --- /dev/null +++ b/dynamic/dynamic.js @@ -0,0 +1,515 @@ +const prompt = document.getElementById("prompt"); +const seed = document.getElementById("seed"); +const cfg = document.getElementById("cfg"); +const steps = document.getElementById("steps"); +const lcm = document.getElementById("lcm"); +const model = document.getElementById("model"); +const negativePrompt = document.getElementById("negativePrompt"); +const url = document.getElementById("url"); +const errorMessage = document.getElementById("errorMessage"); +const lcm_lora = document.getElementById("lcm_lora"); +const resolution = document.getElementById("resolution"); +const canvas_container = document.getElementById("canvas_container"); +const canvas_checkbox = document.getElementById("canvas-checkbox"); +const denoise = document.getElementById("denoise"); + +var options = {}; + +var last_prompt = ""; + +var latest_requested_workflow = undefined; +var latest_fulfilled_workflow = undefined; + +var generation_times_ms = []; + +var object_info = undefined; + +let timeout = null; + +prompt.addEventListener("keyup", function () { + clearTimeout(timeout); + timeout = setTimeout(function () { + Generate(); + + }, 200); +}); +negativePrompt.addEventListener("keyup", function () { + clearTimeout(timeout); + timeout = setTimeout(function () { + Generate(); + + }, 1000); +}); + +seed.addEventListener("change", Generate); +cfg.addEventListener("change", Generate); +steps.addEventListener("change", Generate); +lcm.addEventListener("change", Generate); +denoise.addEventListener("change", Generate); + +function OnGenerateFinished() { + generating = false; + if(latest_requested_workflow != latest_fulfilled_workflow){ + Generate(); + } +} + +var generating = false; +async function Generate() { + if (!clientId) { + clientId = uuid.v4(); + } + + + + + + if (seed.value == "") { + seed.value = Math.floor(Math.random() * 100000); + } + + + var workflow; + + if(canvas_checkbox.checked){ + + //get base64 from canvas + var base64 = canvas.toDataURL("image/png").replace("data:image/png;base64,", ""); + var file_name = await UploadImageToComfyServer(base64, "canvas.png", true); + + workflow = await fetch("../workflows/lcm_img2img_api.json"); + }else{ + workflow = await fetch("../workflows/lcm_api.json"); + } + + workflow = await workflow.json(); + + workflow["6"].inputs.text = prompt.value; + workflow["7"].inputs.text = negativePrompt.value; + workflow["3"].inputs.seed = parseInt(seed.value); + workflow["3"].inputs.cfg = parseFloat(cfg.value); + workflow["3"].inputs.steps = parseInt(steps.value); + workflow["10"].inputs.strength_model = parseFloat(lcm.value); + + workflow["4"].inputs.ckpt_name = model.value; + workflow["10"].inputs.lora_name = lcm_lora.value; + + if(canvas_checkbox.checked){ + workflow["11"].inputs.image = file_name; + workflow["3"].inputs.denoise = denoise.value; + } + + latest_requested_workflow = workflow; + + if (generating) { + return; + } + + const saveButton = document.getElementById("save"); + saveButton.disabled = false; + saveButton.textContent = "Save"; + + + last_prompt = prompt.value; + + generating = true; + + getImages(workflow); +} +let serverAddress = "127.0.0.1:8188"; +let clientId = uuid.v4(); +//#region API +var end_time = 0; +var start_time = 0; +var message_count = 0; +async function getImages(prompt) { + message_count = 0; + start_time = performance.now(); + latest_fulfilled_workflow = prompt; + const promptId = (await queuePrompt(prompt))["prompt_id"]; + console.log("Generating: ", promptId); + const outputImages = {}; + + document.getElementById("spinner").classList.remove("hidden"); + + const ws = new WebSocket(`ws://${serverAddress}/ws?clientId=${clientId}`); + ws.onmessage = (event) => { + try { + + const message = JSON.parse(event.data); + + //handle + if(message_count == 0 && message.type == "status" && message.data.status.exec_info.queue_remaining == 0){ + generating = false; + ws.close(); + } + + if (message.type === "executing" && message.data.node === null && message.data.prompt_id === promptId) { + end_time = performance.now(); + ws.close(); // Execution is done + } + + message_count++; + } catch { } + }; + + + ws.onopen = () => { + ws.send(JSON.stringify({ type: "executing" })); + }; + + ws.onclose = async () => { + generating = false; + document.getElementById("spinner").classList.add("hidden"); + + + var time = end_time - start_time; + if(time < 0){ + time = 0; + } + generation_times_ms.push(time); + const avrg_time = generation_times_ms.reduce((a, b) => a + b, 0) / generation_times_ms.length; + console.log("Generation time: ", time, "ms", "Average time: ", avrg_time, "ms"); + + const historyResponse = await fetch(`http://${serverAddress}/history`); + let history = await historyResponse.json(); + history = history[promptId]; + + const generatedImages = []; + + for (const node_id in history.outputs) { + const nodeOutput = history.outputs[node_id]; + if (nodeOutput.images) { + for (const image of nodeOutput.images) { + const imageData = await getImage( + image.filename, + image.subfolder, + image.type + ); + const base64ImageData = await convertToBase64(imageData); + generatedImages.push(base64ImageData); + } + } + } + + document.getElementById("image").src = + "data:image/png;base64," + generatedImages[0]; + + document.getElementById("image_prompt").textContent = last_prompt; + document.getElementById("image_time").textContent = time.toFixed(0) + "ms"; + + OnGenerateFinished(); + }; + + ws.onerror = (error) => { + console.log("WebSocket error: ", error); + generating = false; + } +} +async function convertToBase64(blob) { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = (event) => { + resolve(event.target.result.split(",")[1]); // Extract base64 data + }; + reader.readAsDataURL(blob); + }); +} +function queuePrompt(prompt) { + const data = JSON.stringify({ prompt: prompt, client_id: clientId }); + return fetch(`http://${serverAddress}/prompt`, { + method: "POST", + body: data, + headers: { "Content-Type": "application/json" }, + }).then((response) => response.json()); +} +async function getImage(filename, subfolder, folderType) { + const data = { filename, subfolder, type: folderType }; + const urlValues = new URLSearchParams(data).toString(); + const response = await fetch(`http://${serverAddress}/view?${urlValues}`); + return await response.blob(); +} +//#endregion + +//#region Saving and loading + +function Save() { + options["prompt"] = prompt.value; + options["negativePrompt"] = negativePrompt.value; + options["seed"] = seed.value; + options["cfg"] = cfg.value; + options["steps"] = steps.value; + options["lcm"] = lcm.value; + options["model"] = model.value; + options["lcm_lora"] = lcm_lora.value; + options["resolution"] = resolution.value; + options["url"] = url.value; + + localStorage.setItem("lcm_options", JSON.stringify(options)); +} +function Load() { + options = JSON.parse(localStorage.getItem("lcm_options")); + + if (!options) { + //default options + options = { + prompt: "", + negativePrompt: "(worst quality:1.6, low quality:1.6),", + seed: null, + cfg: 1.0, + steps: 4, + lcm: 1, + model: null, + lcm_lora: null, + resolution: 512, + url: "http://127.0.0.1:8188", + } + } + + prompt.value = options["prompt"]; + negativePrompt.value = options["negativePrompt"]; + seed.value = options["seed"]; + if(seed.value == undefined || seed.value == ""){ + seed.value = Math.floor(Math.random() * 100000); + } + cfg.value = options["cfg"]; + steps.value = options["steps"]; + lcm.value = options["lcm"]; + model.value = options["model"]; + lcm_lora.value = options["lcm_lora"]; + resolution.value = options["resolution"]; + url.value = options["url"]; + serverAddress = url.value.replace("http://", "").replace("https://", ""); +} +//#endregion +async function FetchComfyUI(){ + model.innerHTML = ""; + lcm_lora.innerHTML = ""; + errorMessage.textContent = ""; + try{ + object_info = await fetch(`http://${serverAddress}/object_info`); + object_info = await object_info.json(); + + + var lcm_loras = object_info["LoraLoader"].input.required.lora_name[0].filter(_lcm => _lcm.includes("lcm")); + + lcm_lora.innerHTML = ""; + lcm_loras.forEach(_lcm => { + const option = document.createElement("option"); + option.value = _lcm; + option.text = _lcm; + lcm_lora.appendChild(option); + }); + //set lcm_lora + if(options["lcm_lora"]){ + lcm_loras.includes(options["lcm_lora"]) ? lcm_lora.value = options["lcm_lora"] : lcm_lora.value = lcm_loras[0]; + }else{ + lcm_loras.includes("lcm-lora-sdv1-5.safetensors") ? lcm_lora.value = "lcm-lora-sdv1-5.safetensors" : lcm_lora.value = lcm_loras[0]; + } + + if(lcm_loras.length == 0){ + errorMessage.innerHTML = "No LCM Loras found!. Please download some LCM Loras from Here and Rename them to include 'lcm' in the name"; + return; + } + + + //set model + object_info["CheckpointLoaderSimple"].input.required.ckpt_name[0].forEach(_model => { + const option = document.createElement("option"); + option.value = _model; + option.text = _model; + model.appendChild(option); + }); + if(options["model"]){ + object_info["CheckpointLoaderSimple"].input.required.ckpt_name[0].includes(options["model"]) ? model.value = options["model"] : model.value = object_info["CheckpointLoaderSimple"].input.required.ckpt_name[0][0]; + } + if(!object_info["KSampler"].input.required.sampler_name[0].includes("lcm")) { + errorMessage.innerHTML = "LCM Sampler not found! Please update ComfyUI to the latest version"; + } + + + }catch (error){ + + //if TypeError + if(error instanceof TypeError){ + errorMessage.innerHTML = "ComfyUI instance Not found/running"; + } + + console.log(error); + + } + + + +} + +async function UploadImageToComfyServer(image, filename = "Image.png", overwrite = true) { + + var imageBase64; + if (image.src) { + imageBase64 = image.src.replace("data:image/png;base64,", ""); + } else { + imageBase64 = image.replace("data:image/png;base64,", ""); + } + var imageBlob = base64ToBlob(imageBase64, "image/png"); + + const formData = new FormData(); + formData.append("image", imageBlob, filename); + formData.append("overwrite", overwrite) + formData.append("subfolder", "silverzone") + + const response = await fetch(url.value + "/upload/image", { + method: "POST", + body: formData, + }); + + if (!response.ok) { + console.error("Image upload failed:", response.status, response.statusText); + return; + } + + const responseData = await response.json(); + var filename = responseData["name"]; + var subfolder = responseData["subfolder"]; + return subfolder + "/" + filename; +} +function base64ToBlob(base64, mime) { + mime = mime || ''; + var sliceSize = 1024; + var byteChars = window.atob(base64); + var byteArrays = []; + + for (var offset = 0, len = byteChars.length; offset < len; offset += sliceSize) { + var slice = byteChars.slice(offset, offset + sliceSize); + + var byteNumbers = new Array(slice.length); + for (var i = 0, sliceLen = slice.length; i < sliceLen; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + var byteArray = new Uint8Array(byteNumbers); + byteArrays.push(byteArray); + } + + return new Blob(byteArrays, { type: mime }); + +} + + +Load(); +FetchComfyUI(); +//On submit / press enter +url.addEventListener("keyup", function (event) { + if (event.key === "Enter") { + event.preventDefault(); + serverAddress = url.value.replace("http://", "").replace("https://", ""); + FetchComfyUI(); + } +}); + +canvas_checkbox.addEventListener("change", function (event) { + if (event.target.checked) { + canvas_container.classList.remove("hidden"); + } else { + canvas_container.classList.add("hidden"); + } +}); +document.getElementById("randomize").addEventListener("click", function (event) { + document.getElementById('seed').value = Math.floor(Math.random() * 10000000); + Generate(); +}); +document.getElementById("save").addEventListener("click", function (e) { + e.target.textContent = "Saved!"; + e.target.disabled = true; + + + + addToImageHistory(document.getElementById("image").src, prompt.value); +}); + + +window.onbeforeunload = function () { + Save(); +}; +//F5 +document.addEventListener("keydown", function (event) { + if (event.key === "F5") { + event.preventDefault(); + Generate(); + } +}); + +Coloris({ + themeMode: 'dark', + swatches: [ + + '#264653', + '#2a9d8f', + '#e9c46a', + 'rgb(244,162,97)', + '#e76f51', + '#d62828', + //green + '#2b9348', + '#ffffff', + '#000000', + '#f0d9b5', + '#8d6e63', + ], +}); + + + +function createIndexedDB() { + return new Promise((resolve, reject) => { + const request = window.indexedDB.open("ImageDB", 1); + + request.onerror = (event) => { + console.error("Error opening database:", event.target.error); + reject(event.target.error); + }; + + request.onsuccess = (event) => { + const db = event.target.result; + resolve(db); + }; + + request.onupgradeneeded = (event) => { + const db = event.target.result; + if (!db.objectStoreNames.contains("images")) { + db.createObjectStore("images", { keyPath: "id", autoIncrement: true }); + } + }; + }); +} + +async function addToImageHistory(imageBase64, text) { + try { + const db = await createIndexedDB(); + const transaction = db.transaction(["images"], "readwrite"); + const store = transaction.objectStore("images"); + + const imageBlob = base64toBlob(imageBase64); // Helper function to convert base64 to Blob + const entry = { imageBlob, text, timestamp: Date.now() }; + + store.add(entry); + + console.log("Image and text added to the history successfully. with text: " + text); + } catch (error) { + console.error("Error while adding image and text to history:", error); + } +} + + +function base64toBlob(base64Data) { + + const binaryString = atob(base64Data.replace("data:image/png;base64,","")); + const arrayBuffer = new ArrayBuffer(binaryString.length); + const uint8Array = new Uint8Array(arrayBuffer); + + for (let i = 0; i < binaryString.length; i++) { + uint8Array[i] = binaryString.charCodeAt(i); + } + + return new Blob([uint8Array], { type: "image/png" }); // Change the type accordingly for different image formats +} \ No newline at end of file diff --git a/dynamic/index.html b/dynamic/index.html new file mode 100644 index 0000000..53a97a4 --- /dev/null +++ b/dynamic/index.html @@ -0,0 +1,235 @@ + + + + + + Dynamic (LCM) + + + + + + + + + + + + +
+

Realtime LCM

+ +
+ +
+ + + +
+ +
+ + +
+
+ +
+ + +
+
+
+
+ + +
+ + + + +
+ + + + + +
+ + + + + Image +
+ + + +
+ +

+

+ +
+ +
+ + + + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+ +
+
+ + +
+
+ + +
+ +
+
+
+ + +
+
+ + +
+ +
+ + + +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/dynamic/style.css b/dynamic/style.css new file mode 100644 index 0000000..f3fcb6a --- /dev/null +++ b/dynamic/style.css @@ -0,0 +1,20 @@ +.selected{ + color:green; +} + +.circle .clr-field button { + width: 22px; + height: 22px; + left: 5px; + right: auto; + border-radius: 5px; + } + + .square .clr-field input, + .circle .clr-field input { + padding-left: 36px; + } + + .circle .clr-field button { + border-radius: 50%; + } \ No newline at end of file diff --git a/output.css b/output.css index 2ab3052..d296876 100644 --- a/output.css +++ b/output.css @@ -913,10 +913,6 @@ video { margin-bottom: 0.75rem; } -.mb-32 { - margin-bottom: 8rem; -} - .mb-4 { margin-bottom: 1rem; } @@ -933,6 +929,10 @@ video { margin-bottom: 2rem; } +.me-2 { + margin-inline-end: 0.5rem; +} + .-ml-8 { margin-left: -2rem; } @@ -985,6 +985,10 @@ video { margin-right: 2rem; } +.ms-2 { + margin-inline-start: 0.5rem; +} + .mt-0 { margin-top: 0px; } @@ -1005,6 +1009,10 @@ video { margin-top: 0.5rem; } +.mt-3 { + margin-top: 0.75rem; +} + .mt-4 { margin-top: 1rem; } @@ -1140,6 +1148,10 @@ video { height: 4rem; } +.h-20 { + height: 5rem; +} + .h-3 { height: 0.75rem; } @@ -1148,6 +1160,10 @@ video { height: 0.875rem; } +.h-32 { + height: 8rem; +} + .h-4 { height: 1rem; } @@ -1192,6 +1208,10 @@ video { height: 100%-2rem; } +.h-\[100\%\] { + height: 100%; +} + .h-\[50vh\] { height: 50vh; } @@ -1357,6 +1377,11 @@ video { width: calc(100% - 4rem); } +.w-fit { + width: -moz-fit-content; + width: fit-content; +} + .w-full { width: 100%; } @@ -1951,6 +1976,10 @@ video { justify-items: stretch; } +.gap-1 { + gap: 0.25rem; +} + .gap-2 { gap: 0.5rem; } @@ -1996,18 +2025,18 @@ video { margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); } -.space-y-6 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); -} - .space-y-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(1rem * var(--tw-space-y-reverse)); } +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} + .space-y-reverse > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 1; } @@ -2636,6 +2665,11 @@ video { background-color: rgb(185 28 28 / var(--tw-bg-opacity)); } +.bg-slate-700 { + --tw-bg-opacity: 1; + background-color: rgb(51 65 85 / var(--tw-bg-opacity)); +} + .bg-transparent { background-color: transparent; } @@ -2657,6 +2691,49 @@ video { --tw-bg-opacity: 0.75; } +.bg-gradient-to-br { + background-image: linear-gradient(to bottom right, var(--tw-gradient-stops)); +} + +.bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); +} + +.from-blue-500 { + --tw-gradient-from: #3b82f6 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.from-gray-900 { + --tw-gradient-from: #111827 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(17 24 39 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.from-red-800 { + --tw-gradient-from: #991b1b var(--tw-gradient-from-position); + --tw-gradient-to: rgb(153 27 27 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.via-blue-600 { + --tw-gradient-to: rgb(37 99 235 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #2563eb var(--tw-gradient-via-position), var(--tw-gradient-to); +} + +.to-blue-700 { + --tw-gradient-to: #1d4ed8 var(--tw-gradient-to-position); +} + +.to-gray-800 { + --tw-gradient-to: #1f2937 var(--tw-gradient-to-position); +} + +.to-red-500 { + --tw-gradient-to: #ef4444 var(--tw-gradient-to-position); +} + .decoration-slice { -webkit-box-decoration-break: slice; box-decoration-break: slice; @@ -2920,6 +2997,10 @@ video { padding-bottom: 0.25rem; } +.pb-16 { + padding-bottom: 4rem; +} + .pb-2 { padding-bottom: 0.5rem; } @@ -2928,12 +3009,20 @@ video { padding-bottom: 8rem; } +.pb-4 { + padding-bottom: 1rem; +} + .pb-6 { padding-bottom: 1.5rem; } -.pb-8 { - padding-bottom: 2rem; +.pl-0 { + padding-left: 0px; +} + +.pl-0\.5 { + padding-left: 0.125rem; } .pl-2 { @@ -2948,6 +3037,10 @@ video { padding-top: 0.75rem; } +.pt-4 { + padding-top: 1rem; +} + .text-left { text-align: left; } @@ -3174,6 +3267,11 @@ video { color: rgb(30 64 175 / var(--tw-text-opacity)); } +.text-gray-100 { + --tw-text-opacity: 1; + color: rgb(243 244 246 / var(--tw-text-opacity)); +} + .text-gray-200 { --tw-text-opacity: 1; color: rgb(229 231 235 / var(--tw-text-opacity)); @@ -3656,6 +3754,10 @@ video { transition-duration: 150ms; } +.duration-200 { + transition-duration: 200ms; +} + .duration-300 { transition-duration: 300ms; } @@ -3664,6 +3766,10 @@ video { transition-duration: 700ms; } +.duration-75 { + transition-duration: 75ms; +} + .ease-in { transition-timing-function: cubic-bezier(0.4, 0, 1, 1); } @@ -3810,6 +3916,11 @@ video { display: none; } +.hover\:border-blue-500:hover { + --tw-border-opacity: 1; + border-color: rgb(59 130 246 / var(--tw-border-opacity)); +} + .hover\:border-gray-300:hover { --tw-border-opacity: 1; border-color: rgb(209 213 219 / var(--tw-border-opacity)); @@ -3850,6 +3961,11 @@ video { background-color: rgb(75 85 99 / var(--tw-bg-opacity)); } +.hover\:bg-gray-900:hover { + --tw-bg-opacity: 1; + background-color: rgb(17 24 39 / var(--tw-bg-opacity)); +} + .hover\:bg-indigo-600:hover { --tw-bg-opacity: 1; background-color: rgb(79 70 229 / var(--tw-bg-opacity)); @@ -3875,6 +3991,10 @@ video { background-color: rgb(127 29 29 / var(--tw-bg-opacity)); } +.hover\:bg-gradient-to-br:hover { + background-image: linear-gradient(to bottom right, var(--tw-gradient-stops)); +} + .hover\:font-bold:hover { font-weight: 700; } @@ -3884,6 +4004,11 @@ video { color: rgb(59 130 246 / var(--tw-text-opacity)); } +.hover\:text-gray-300:hover { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + .hover\:text-gray-400:hover { --tw-text-opacity: 1; color: rgb(156 163 175 / var(--tw-text-opacity)); @@ -3994,6 +4119,11 @@ video { --tw-ring-color: rgb(229 231 235 / var(--tw-ring-opacity)); } +.focus\:ring-gray-300:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity)); +} + .focus\:ring-gray-400:focus { --tw-ring-opacity: 1; --tw-ring-color: rgb(156 163 175 / var(--tw-ring-opacity)); @@ -4014,6 +4144,11 @@ video { --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); } +.focus\:ring-purple-200:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(233 213 255 / var(--tw-ring-opacity)); +} + .focus\:ring-red-200:focus { --tw-ring-opacity: 1; --tw-ring-color: rgb(254 202 202 / var(--tw-ring-opacity)); @@ -4045,10 +4180,28 @@ video { text-align: center; } +.disabled\:opacity-50:disabled { + opacity: 0.5; +} + .group:hover .group-hover\:bg-white\/50 { background-color: rgb(255 255 255 / 0.5); } +.group:hover .group-hover\:bg-opacity-0 { + --tw-bg-opacity: 0; +} + +.group:hover .group-hover\:from-red-800 { + --tw-gradient-from: #991b1b var(--tw-gradient-from-position); + --tw-gradient-to: rgb(153 27 27 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.group:hover .group-hover\:to-red-500 { + --tw-gradient-to: #ef4444 var(--tw-gradient-to-position); +} + .group:focus .group-focus\:outline-none { outline: 2px solid transparent; outline-offset: 2px; @@ -4299,6 +4452,10 @@ video { --tw-ring-offset-color: #374151; } +:is(.dark .dark\:ring-offset-gray-800) { + --tw-ring-offset-color: #1f2937; +} + :is(.dark .dark\:hover\:bg-blue-700:hover) { --tw-bg-opacity: 1; background-color: rgb(29 78 216 / var(--tw-bg-opacity)); @@ -4354,6 +4511,11 @@ video { --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); } +:is(.dark .dark\:focus\:ring-blue-600:focus) { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(37 99 235 / var(--tw-ring-opacity)); +} + :is(.dark .dark\:focus\:ring-blue-800:focus) { --tw-ring-opacity: 1; --tw-ring-color: rgb(30 64 175 / var(--tw-ring-opacity)); @@ -4478,10 +4640,6 @@ video { height: 24rem; } - .md\:h-\[calc\(100\%\)\] { - height: calc(100%); - } - .md\:h-full { height: 100%; } @@ -4510,11 +4668,6 @@ video { max-width: 33%; } - .md\:columns-2 { - -moz-columns: 2; - columns: 2; - } - .md\:columns-3 { -moz-columns: 3; columns: 3; @@ -4536,10 +4689,6 @@ video { align-items: center; } - .md\:justify-start { - justify-content: flex-start; - } - .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1rem * var(--tw-space-x-reverse)); @@ -4560,11 +4709,6 @@ video { } @media (min-width: 1024px) { - .lg\:columns-3 { - -moz-columns: 3; - columns: 3; - } - .lg\:columns-5 { -moz-columns: 5; columns: 5; diff --git a/tailwind.config.js b/tailwind.config.js index 4167ac4..a43658e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -4,6 +4,7 @@ module.exports = { content: [ "./*.{html,js}", "./**/*.{html,js}", + "./dynamic/*.{html,js}", 'node_modules/preline/dist/*.js', ], theme: { diff --git a/workflows/lcm_api.json b/workflows/lcm_api.json new file mode 100644 index 0000000..213ed37 --- /dev/null +++ b/workflows/lcm_api.json @@ -0,0 +1,102 @@ +{ + "3": { + "inputs": { + "seed": 416832952734281, + "steps": 5, + "cfg": 1.2, + "sampler_name": "lcm", + "scheduler": "normal", + "denoise": 1, + "model": [ + "10", + 0 + ], + "positive": [ + "6", + 0 + ], + "negative": [ + "7", + 0 + ], + "latent_image": [ + "5", + 0 + ] + }, + "class_type": "KSampler" + }, + "4": { + "inputs": { + "ckpt_name": "Anime\\meinamix_meinaV11.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "5": { + "inputs": { + "width": 512, + "height": 512, + "batch_size": 1 + }, + "class_type": "EmptyLatentImage" + }, + "6": { + "inputs": { + "text": "1girl", + "clip": [ + "4", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": "extra fingers,fewer fingers,(low quality, worst quality:1.4), (bad anatomy), (inaccurate limb:1.2),bad composition, inaccurate eyes, extra digit,fewer digits,(extra arms:1.2),embedding:easynegative", + "clip": [ + "4", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": [ + "3", + 0 + ], + "vae": [ + "4", + 2 + ] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "silverzone/LCM/ComfyUI", + "images": [ + "8", + 0 + ] + }, + "class_type": "SaveImage" + }, + "10": { + "inputs": { + "lora_name": "lcm-lora-sdv1-5.safetensors", + "strength_model": 1, + "strength_clip": 1, + "model": [ + "4", + 0 + ], + "clip": [ + "4", + 1 + ] + }, + "class_type": "LoraLoader" + } +} \ No newline at end of file diff --git a/workflows/lcm_img2img_api.json b/workflows/lcm_img2img_api.json new file mode 100644 index 0000000..1162b5c --- /dev/null +++ b/workflows/lcm_img2img_api.json @@ -0,0 +1,127 @@ +{ + "3": { + "inputs": { + "seed": 81451518754731, + "steps": 4, + "cfg": 1.4000000000000001, + "sampler_name": "lcm", + "scheduler": "normal", + "denoise": 0.8, + "model": [ + "10", + 0 + ], + "positive": [ + "6", + 0 + ], + "negative": [ + "7", + 0 + ], + "latent_image": [ + "13", + 0 + ] + }, + "class_type": "KSampler" + }, + "4": { + "inputs": { + "ckpt_name": "Anime\\meinamix_meinaV11.safetensors" + }, + "class_type": "CheckpointLoaderSimple" + }, + "6": { + "inputs": { + "text": "1girl", + "clip": [ + "4", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "7": { + "inputs": { + "text": "(worst quality:1.6, low quality:1.6), (zombie, sketch, interlocked fingers, comic)", + "clip": [ + "4", + 1 + ] + }, + "class_type": "CLIPTextEncode" + }, + "8": { + "inputs": { + "samples": [ + "3", + 0 + ], + "vae": [ + "4", + 2 + ] + }, + "class_type": "VAEDecode" + }, + "9": { + "inputs": { + "filename_prefix": "silverzone/LCM/ComfyUI", + "images": [ + "8", + 0 + ] + }, + "class_type": "SaveImage" + }, + "10": { + "inputs": { + "lora_name": "lcm-lora-sdv1-5.safetensors", + "strength_model": 1, + "strength_clip": 1, + "model": [ + "4", + 0 + ], + "clip": [ + "4", + 1 + ] + }, + "class_type": "LoraLoader" + }, + "11": { + "inputs": { + "image": "img-2023-11-15-20-33-11.png", + "choose file to upload": "image" + }, + "class_type": "LoadImage" + }, + "12": { + "inputs": { + "upscale_method": "nearest-exact", + "width": 512, + "height": 512, + "crop": "center", + "image": [ + "11", + 0 + ] + }, + "class_type": "ImageScale" + }, + "13": { + "inputs": { + "pixels": [ + "12", + 0 + ], + "vae": [ + "4", + 2 + ] + }, + "class_type": "VAEEncode" + } +} \ No newline at end of file