pythongosssss
2 years ago
committed by
GitHub
6 changed files with 544 additions and 542 deletions
@ -1,476 +1,214 @@ |
|||||||
<!DOCTYPE html> |
<!DOCTYPE html> |
||||||
<html lang="en"> |
<html lang="en"> |
||||||
<head> |
<head> |
||||||
<link rel="stylesheet" type="text/css" href="lib/litegraph.css"> |
<link rel="stylesheet" type="text/css" href="lib/litegraph.css" /> |
||||||
<link rel="stylesheet" type="text/css" href="style.css"> |
<link rel="stylesheet" type="text/css" href="style.css" /> |
||||||
<script type="text/javascript" src="lib/litegraph.core.js"></script> |
<script type="text/javascript" src="lib/litegraph.core.js"></script> |
||||||
|
|
||||||
<script type="module"> |
<script type="module"> |
||||||
import { app } from "/scripts/app.js"; |
import { app } from "/scripts/app.js"; |
||||||
await app.setup(); |
await app.setup(); |
||||||
window.app = app; |
window.app = app; |
||||||
window.graph = app.graph; |
window.graph = app.graph; |
||||||
</script> |
</script> |
||||||
</head> |
</head> |
||||||
<body> |
<body> |
||||||
<script> |
<script> |
||||||
return; |
return; |
||||||
|
|
||||||
function postPrompt(number) { |
let runningNodeId = null; |
||||||
let prompt = graphToPrompt(); |
let progress = null; |
||||||
let full_data = {client_id: clientId, prompt: prompt, extra_data: {extra_pnginfo: {workflow: graph.serialize()}}}; |
let clientId = null; |
||||||
if (number == -1) { |
|
||||||
full_data.front = true; |
function clearGraph() { |
||||||
} else |
graph.clear(); |
||||||
if (number != 0) { |
} |
||||||
full_data.number = number; |
|
||||||
} |
function loadTxt2Img() { |
||||||
|
loadGraphData(graph, default_graph); |
||||||
fetch('/prompt', { |
} |
||||||
method: 'POST', |
|
||||||
headers: { |
function saveGraph() { |
||||||
'Content-Type': 'application/json' |
var json = JSON.stringify(graph.serialize()); // convert the data to a JSON string |
||||||
}, |
var blob = new Blob([json], { type: "application/json" }); |
||||||
body: JSON.stringify(full_data) |
var url = URL.createObjectURL(blob); |
||||||
}) |
var a = document.createElement("a"); |
||||||
.then(data => promptPosted(data)) |
a.style = "display: none"; |
||||||
.catch(error => console.error(error)) |
a.href = url; |
||||||
|
a.download = "workflow.json"; |
||||||
// console.log(JSON.stringify(prompt)); |
document.body.appendChild(a); |
||||||
// console.log(JSON.stringify(graph.serialize())); |
a.click(); |
||||||
|
setTimeout(function () { |
||||||
// restore initial values replaced by dynamic prompting |
document.body.removeChild(a); |
||||||
for (let x in graph._nodes_by_id) { |
window.URL.revokeObjectURL(url); |
||||||
let n = graph._nodes_by_id[x]; |
}, 0); |
||||||
for (let w in n.widgets) { |
} |
||||||
let wid = n.widgets[w]; |
|
||||||
if (wid.dynamic_prompt && wid.dynamic_prompt === true) |
var input = document.createElement("input"); |
||||||
wid.value = wid.value_initial; |
input.setAttribute("type", "file"); |
||||||
} |
input.setAttribute("accept", ".json,image/png"); |
||||||
} |
input.style.display = "none"; |
||||||
} |
document.body.appendChild(input); |
||||||
|
|
||||||
function prompt_file_load(file) |
input.addEventListener("change", function () { |
||||||
{ |
var file = input.files[0]; |
||||||
if (file.type === 'image/png') { |
prompt_file_load(file); |
||||||
const reader = new FileReader(); |
}); |
||||||
reader.onload = (event) => { |
|
||||||
// Get the PNG data as a Uint8Array |
function loadGraph() { |
||||||
const pngData = new Uint8Array(event.target.result); |
input.click(); |
||||||
const dataView = new DataView(pngData.buffer); |
} |
||||||
|
|
||||||
// Check that the PNG signature is present |
document.addEventListener("paste", (e) => { |
||||||
if (dataView.getUint32(0) !== 0x89504e47) { |
let data = (e.clipboardData || window.clipboardData).getData("text/plain"); |
||||||
console.error('Not a valid PNG file'); |
console.log(data); |
||||||
return; |
|
||||||
} |
try { |
||||||
|
data = data.slice(data.indexOf("{")); |
||||||
// Start searching for chunks after the PNG signature |
j = JSON.parse(data); |
||||||
let offset = 8; |
} catch (err) { |
||||||
let txt_chunks = {} |
data = data.slice(data.indexOf("workflow\n")); |
||||||
// Loop through the chunks in the PNG file |
data = data.slice(data.indexOf("{")); |
||||||
while (offset < pngData.length) { |
j = JSON.parse(data); |
||||||
// Get the length of the chunk |
} |
||||||
const length = dataView.getUint32(offset); |
|
||||||
// Get the chunk type |
if (Object.hasOwn(j, "version") && Object.hasOwn(j, "nodes") && Object.hasOwn(j, "extra")) { |
||||||
const type = String.fromCharCode(...pngData.slice(offset + 4, offset + 8)); |
loadGraphData(graph, j); |
||||||
if (type === 'tEXt') { |
} |
||||||
// Get the keyword |
}); |
||||||
let keyword_end = offset + 8; |
|
||||||
while (pngData[keyword_end] !== 0) { |
function deleteQueueElement(type, delete_id, then) { |
||||||
keyword_end++; |
fetch("/" + type, { |
||||||
} |
method: "POST", |
||||||
const keyword = String.fromCharCode(...pngData.slice(offset + 8, keyword_end)); |
headers: { |
||||||
// Get the text |
"Content-Type": "application/json", |
||||||
const text = String.fromCharCode(...pngData.slice(keyword_end + 1, offset + 8 + length)); |
}, |
||||||
txt_chunks[keyword] = text; |
body: JSON.stringify({ delete: [delete_id] }), |
||||||
} |
}) |
||||||
|
.then((data) => { |
||||||
// Get the next chunk |
console.log(data); |
||||||
offset += 12 + length; |
then(); |
||||||
} |
}) |
||||||
console.log(txt_chunks); |
.catch((error) => console.error(error)); |
||||||
// console.log(JSON.parse(txt_chunks["prompt"])); |
} |
||||||
loadGraphData(graph, JSON.parse(txt_chunks["workflow"])); |
|
||||||
}; |
function loadQueue() { |
||||||
reader.readAsArrayBuffer(file); |
loadItems("queue"); |
||||||
} else if (file.type === "application/json" || file.name.endsWith(".json")) { |
} |
||||||
var reader = new FileReader(); |
function loadHistory() { |
||||||
reader.onload = function() { |
loadItems("history"); |
||||||
console.log(reader.result); |
} |
||||||
var jsonData = JSON.parse(reader.result); |
function loadItems(type) { |
||||||
loadGraphData(graph, jsonData); |
fetch("/" + type) |
||||||
}; |
.then((response) => response.json()) |
||||||
reader.readAsText(file); |
.then((data) => { |
||||||
} |
var queue_div = document.getElementById(type + "button-content"); |
||||||
} |
queue_div.style.display = "block"; |
||||||
|
var see_queue_button = document.getElementById("see" + type + "button"); |
||||||
// Get prompt from dropped PNG or json |
let old_w = see_queue_button.style.width; |
||||||
document.addEventListener('drop', (event) => { |
see_queue_button.innerHTML = "Close"; |
||||||
event.preventDefault(); |
|
||||||
event.stopPropagation(); |
let runningcontents; |
||||||
const file = event.dataTransfer.files[0]; |
if (type === "queue") { |
||||||
console.log(file.type); |
runningcontents = document.getElementById("runningcontents"); |
||||||
prompt_file_load(file); |
runningcontents.innerHTML = ""; |
||||||
}); |
} |
||||||
|
let queuecontents = document.getElementById(type + "contents"); |
||||||
let runningNodeId = null; |
queuecontents.innerHTML = ""; |
||||||
let progress = null; |
function append_to_list(list_element, append_to_element, append_delete, state) { |
||||||
let clientId = null; |
let number = list_element[0]; |
||||||
const orig = LGraphCanvas.prototype.drawNodeShape; |
let id = list_element[1]; |
||||||
LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolor, selected, mouse_over) { |
let prompt = list_element[2]; |
||||||
const res = orig.apply(this, arguments); |
let workflow = list_element[3].extra_pnginfo.workflow; |
||||||
|
let a = document.createElement("a"); |
||||||
if(node.id + "" === runningNodeId) { |
a.innerHTML = number + ": "; |
||||||
const shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; |
append_to_element.appendChild(a); |
||||||
ctx.lineWidth = 1; |
let button = document.createElement("button"); |
||||||
ctx.globalAlpha = 0.8; |
button.innerHTML = "Load"; |
||||||
ctx.beginPath(); |
button.style.fontSize = "10px"; |
||||||
if( shape == LiteGraph.BOX_SHAPE ) |
button.workflow = workflow; |
||||||
ctx.rect(-6,-6 + LiteGraph.NODE_TITLE_HEIGHT, 12 + size[0]+1, 12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT ); |
button.onclick = function (event) { |
||||||
else if (shape == LiteGraph.ROUND_SHAPE || (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) ) |
loadGraphData(graph, event.target.workflow); |
||||||
ctx.roundRect(-6,-6 - LiteGraph.NODE_TITLE_HEIGHT, 12 +size[0]+1, 12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT , this.round_radius * 2); |
if (state) { |
||||||
else if (shape == LiteGraph.CARD_SHAPE) |
nodeOutputs = state; |
||||||
ctx.roundRect(-6,-6 + LiteGraph.NODE_TITLE_HEIGHT, 12 +size[0]+1, 12 + size[1] + LiteGraph.NODE_TITLE_HEIGHT , this.round_radius * 2, 2); |
} |
||||||
else if (shape == LiteGraph.CIRCLE_SHAPE) |
}; |
||||||
ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI*2); |
|
||||||
ctx.strokeStyle = "#0f0" |
append_to_element.appendChild(button); |
||||||
ctx.stroke(); |
if (append_delete) { |
||||||
ctx.strokeStyle = fgcolor; |
let button = document.createElement("button"); |
||||||
ctx.globalAlpha = 1; |
button.innerHTML = "Delete"; |
||||||
|
button.style.fontSize = "10px"; |
||||||
if(progress) { |
button.delete_id = id; |
||||||
ctx.fillStyle = "green"; |
button.onclick = function (event) { |
||||||
ctx.fillRect(0, 0, size[0] * (progress.value / progress.max), 6); |
deleteQueueElement(type, event.target.delete_id, () => loadItems(type)); |
||||||
ctx.fillStyle = bgcolor; |
}; |
||||||
} |
append_to_element.appendChild(button); |
||||||
} |
} |
||||||
|
append_to_element.appendChild(document.createElement("br")); |
||||||
return res; |
} |
||||||
} |
|
||||||
|
if (runningcontents) { |
||||||
function updateNodeProgress(v) { |
for (let x in data.queue_running) { |
||||||
progress = v; |
append_to_list(data.queue_running[x], runningcontents, false); |
||||||
graph.setDirtyCanvas(true, false); |
} |
||||||
} |
} |
||||||
|
|
||||||
function setRunningNode(id) { |
let items; |
||||||
progress = null; |
if (type === "queue") { |
||||||
runningNodeId = id; |
items = data.queue_pending; |
||||||
graph.setDirtyCanvas(true, false); |
} else { |
||||||
} |
items = Object.values(data); |
||||||
|
} |
||||||
(() => { |
items.sort((a, b) => a[0] - b[0]); |
||||||
function updateStatus(data) { |
for (let i of items) { |
||||||
document.getElementById("queuesize").innerHTML = "Queue size: " + (data ? data.exec_info.queue_remaining : "ERR"); |
append_to_list(type === "queue" ? i : i.prompt, queuecontents, true, i.outputs); |
||||||
} |
} |
||||||
|
}) |
||||||
//fix for colab and other things that don't support websockets. |
.catch((response) => { |
||||||
function manually_fetch_queue() { |
console.log(response); |
||||||
fetch('/prompt') |
}); |
||||||
.then(response => response.json()) |
} |
||||||
.then(data => { |
|
||||||
updateStatus(data); |
function seeItems(type) { |
||||||
}).catch((response) => {updateStatus(null)}); |
var queue_div = document.getElementById(type + "button-content"); |
||||||
} |
if (queue_div.style.display == "block") { |
||||||
|
closeItems(type); |
||||||
let ws; |
} else { |
||||||
function createSocket(isReconnect) { |
loadItems(type); |
||||||
if(ws) return; |
} |
||||||
|
} |
||||||
let opened = false; |
|
||||||
ws = new WebSocket(`ws${window.location.protocol === "https:"? "s" : ""}://${location.host}/ws`); |
function seeQueue() { |
||||||
|
closeItems("history"); |
||||||
ws.addEventListener("open", () => { |
seeItems("queue"); |
||||||
opened = true; |
} |
||||||
if(isReconnect) { |
|
||||||
closeModal(); |
function seeHistory() { |
||||||
} |
closeItems("queue"); |
||||||
}); |
seeItems("history"); |
||||||
|
} |
||||||
ws.addEventListener("error", () => { |
|
||||||
if(ws) ws.close(); |
function closeItems(type) { |
||||||
manually_fetch_queue(); |
var queue_div = document.getElementById(type + "button-content"); |
||||||
}); |
queue_div.style.display = "none"; |
||||||
|
var see_queue_button = document.getElementById("see" + type + "button"); |
||||||
ws.addEventListener("close", () => { |
see_queue_button.innerHTML = "See " + type[0].toUpperCase() + type.substr(1); |
||||||
setTimeout(() => { |
} |
||||||
ws = null; |
|
||||||
createSocket(true); |
function clearItems(type) { |
||||||
}, 300); |
fetch("/" + type, { |
||||||
if(opened) { |
method: "POST", |
||||||
updateStatus(null); |
headers: { |
||||||
showModal("Reconnecting..."); |
"Content-Type": "application/json", |
||||||
} |
}, |
||||||
}); |
body: JSON.stringify({ clear: true }), |
||||||
|
}) |
||||||
ws.addEventListener("message", (event) => { |
.then((data) => { |
||||||
try { |
loadItems(type); |
||||||
const msg = JSON.parse(event.data); |
}) |
||||||
switch(msg.type) { |
.catch((error) => console.error(error)); |
||||||
case "status": |
} |
||||||
if(msg.data.sid) { |
</script> |
||||||
clientId = msg.data.sid; |
</body> |
||||||
} |
|
||||||
updateStatus(msg.data.status); |
|
||||||
break; |
|
||||||
case "progress": |
|
||||||
updateNodeProgress(msg.data) |
|
||||||
break; |
|
||||||
case "executing": |
|
||||||
setRunningNode(msg.data.node); |
|
||||||
break; |
|
||||||
case "executed": |
|
||||||
nodeOutputs[msg.data.node] = msg.data.output; |
|
||||||
break; |
|
||||||
default: |
|
||||||
throw new Error("Unknown message type") |
|
||||||
} |
|
||||||
} catch (error) { |
|
||||||
console.warn("Unhandled message:", event.data) |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
createSocket(); |
|
||||||
})(); |
|
||||||
|
|
||||||
|
|
||||||
function clearGraph() { |
|
||||||
graph.clear(); |
|
||||||
} |
|
||||||
|
|
||||||
function loadTxt2Img() { |
|
||||||
loadGraphData(graph, default_graph); |
|
||||||
} |
|
||||||
|
|
||||||
function saveGraph() { |
|
||||||
var json = JSON.stringify(graph.serialize()); // convert the data to a JSON string |
|
||||||
var blob = new Blob([json], {type: "application/json"}); |
|
||||||
var url = URL.createObjectURL(blob); |
|
||||||
var a = document.createElement("a"); |
|
||||||
a.style = "display: none"; |
|
||||||
a.href = url; |
|
||||||
a.download = "workflow.json"; |
|
||||||
document.body.appendChild(a); |
|
||||||
a.click(); |
|
||||||
setTimeout(function() { |
|
||||||
document.body.removeChild(a); |
|
||||||
window.URL.revokeObjectURL(url); |
|
||||||
}, 0); |
|
||||||
} |
|
||||||
|
|
||||||
var input = document.createElement("input"); |
|
||||||
input.setAttribute("type", "file"); |
|
||||||
input.setAttribute("accept", ".json,image/png"); |
|
||||||
input.style.display = "none"; |
|
||||||
document.body.appendChild(input); |
|
||||||
|
|
||||||
input.addEventListener('change', function() { |
|
||||||
var file = input.files[0]; |
|
||||||
prompt_file_load(file); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
function loadGraph() { |
|
||||||
input.click(); |
|
||||||
} |
|
||||||
|
|
||||||
document.addEventListener('paste', e=>{ |
|
||||||
let data = (e.clipboardData || window.clipboardData).getData('text/plain'); |
|
||||||
console.log(data); |
|
||||||
|
|
||||||
try { |
|
||||||
data = data.slice(data.indexOf('{')); |
|
||||||
j = JSON.parse(data); |
|
||||||
} catch(err) { |
|
||||||
data = data.slice(data.indexOf('workflow\n')); |
|
||||||
data = data.slice(data.indexOf('{')); |
|
||||||
j = JSON.parse(data); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
if (Object.hasOwn(j, 'version') && Object.hasOwn(j, 'nodes') && Object.hasOwn(j, 'extra')) { |
|
||||||
loadGraphData(graph, j); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
function deleteQueueElement(type, delete_id, then) { |
|
||||||
fetch('/' + type, { |
|
||||||
method: 'POST', |
|
||||||
headers: { |
|
||||||
'Content-Type': 'application/json' |
|
||||||
}, |
|
||||||
body: JSON.stringify({"delete":[delete_id]}) |
|
||||||
}) |
|
||||||
.then(data => { |
|
||||||
console.log(data); |
|
||||||
then(); |
|
||||||
}) |
|
||||||
.catch(error => console.error(error)) |
|
||||||
} |
|
||||||
|
|
||||||
function loadQueue() { |
|
||||||
loadItems("queue") |
|
||||||
} |
|
||||||
function loadHistory() { |
|
||||||
loadItems("history") |
|
||||||
} |
|
||||||
function loadItems(type) { |
|
||||||
fetch('/' + type) |
|
||||||
.then(response => response.json()) |
|
||||||
.then(data => { |
|
||||||
var queue_div = document.getElementById(type + "button-content"); |
|
||||||
queue_div.style.display = 'block'; |
|
||||||
var see_queue_button = document.getElementById("see" + type + "button"); |
|
||||||
let old_w = see_queue_button.style.width; |
|
||||||
see_queue_button.innerHTML = "Close"; |
|
||||||
|
|
||||||
let runningcontents; |
|
||||||
if(type === "queue") { |
|
||||||
runningcontents = document.getElementById("runningcontents"); |
|
||||||
runningcontents.innerHTML = ''; |
|
||||||
} |
|
||||||
let queuecontents = document.getElementById(type + "contents"); |
|
||||||
queuecontents.innerHTML = ''; |
|
||||||
function append_to_list(list_element, append_to_element, append_delete, state) { |
|
||||||
let number = list_element[0]; |
|
||||||
let id = list_element[1]; |
|
||||||
let prompt = list_element[2]; |
|
||||||
let workflow = list_element[3].extra_pnginfo.workflow; |
|
||||||
let a = document.createElement("a"); |
|
||||||
a.innerHTML = number + ": "; |
|
||||||
append_to_element.appendChild(a); |
|
||||||
let button = document.createElement("button"); |
|
||||||
button.innerHTML = "Load"; |
|
||||||
button.style.fontSize = "10px"; |
|
||||||
button.workflow = workflow; |
|
||||||
button.onclick = function(event) { |
|
||||||
loadGraphData(graph, event.target.workflow); |
|
||||||
if(state) { |
|
||||||
nodeOutputs = state; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
append_to_element.appendChild(button); |
|
||||||
if (append_delete) { |
|
||||||
let button = document.createElement("button"); |
|
||||||
button.innerHTML = "Delete"; |
|
||||||
button.style.fontSize = "10px"; |
|
||||||
button.delete_id = id; |
|
||||||
button.onclick = function(event) { |
|
||||||
deleteQueueElement(type, event.target.delete_id, () => loadItems(type)); |
|
||||||
}; |
|
||||||
append_to_element.appendChild(button); |
|
||||||
} |
|
||||||
append_to_element.appendChild(document.createElement("br")); |
|
||||||
} |
|
||||||
|
|
||||||
if(runningcontents) { |
|
||||||
for (let x in data.queue_running) { |
|
||||||
append_to_list(data.queue_running[x], runningcontents, false); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
let items; |
|
||||||
if(type === "queue") { |
|
||||||
items = data.queue_pending; |
|
||||||
} else { |
|
||||||
items = Object.values(data); |
|
||||||
} |
|
||||||
items.sort((a, b) => a[0] - b[0]); |
|
||||||
for (let i of items) { |
|
||||||
append_to_list(type === "queue" ? i : i.prompt, queuecontents, true, i.outputs); |
|
||||||
} |
|
||||||
}).catch((response) => {console.log(response)}); |
|
||||||
} |
|
||||||
|
|
||||||
function seeItems(type) { |
|
||||||
var queue_div = document.getElementById(type + "button-content"); |
|
||||||
if (queue_div.style.display == 'block') { |
|
||||||
closeItems(type) |
|
||||||
} else { |
|
||||||
loadItems(type); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function seeQueue() { |
|
||||||
closeItems("history") |
|
||||||
seeItems("queue") |
|
||||||
} |
|
||||||
|
|
||||||
function seeHistory() { |
|
||||||
closeItems("queue") |
|
||||||
seeItems("history") |
|
||||||
} |
|
||||||
|
|
||||||
function closeItems(type) { |
|
||||||
var queue_div = document.getElementById(type + "button-content"); |
|
||||||
queue_div.style.display = 'none'; |
|
||||||
var see_queue_button = document.getElementById("see" + type + "button"); |
|
||||||
see_queue_button.innerHTML = "See " + type[0].toUpperCase() + type.substr(1) |
|
||||||
} |
|
||||||
|
|
||||||
function clearItems(type) { |
|
||||||
fetch('/' + type, { |
|
||||||
method: 'POST', |
|
||||||
headers: { |
|
||||||
'Content-Type': 'application/json' |
|
||||||
}, |
|
||||||
body: JSON.stringify({"clear":true}) |
|
||||||
}).then(data => { |
|
||||||
loadItems(type); |
|
||||||
}) |
|
||||||
.catch(error => console.error(error)); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
</script> |
|
||||||
|
|
||||||
<span id="menu" style="font-size: 15px;position: absolute; top: 50%; right: 0%; background-color: white; text-align: center; z-index: 100;width:170px"> |
|
||||||
<span id="queuesize">Queue size: X</span><br> |
|
||||||
<button style="font-size: 20px;width: 100%;" id="queuebutton" onclick="postPrompt(0)">Queue Prompt</button><br> |
|
||||||
<span style="left: 0%;"> |
|
||||||
<button style="font-size: 10px;" id="queuebutton" onclick="postPrompt(-1)">Queue Front</button> |
|
||||||
<button style="font-size: 10px; width: 50%;" id="seequeuebutton" onclick="seeQueue()">See Queue</button> |
|
||||||
<button style="font-size: 10px; width: 50%;" id="seehistorybutton" onclick="seeHistory()">See History</button> |
|
||||||
<br> |
|
||||||
</span> |
|
||||||
<div id="queuebutton-content" style="background-color: #e1e1e1;min-width: 160px;display: none;z-index: 101;"> |
|
||||||
<span style="width:100%;padding: 3px;display:inline-block;">Running:</span> |
|
||||||
<div id="runningcontents" style="background-color: #d0d0d0; padding: 5px;"> |
|
||||||
<a>1</a> |
|
||||||
<button style="font-size: 10px;">Load</button> |
|
||||||
<br> |
|
||||||
</div> |
|
||||||
<span style="left: 0%;padding: 3px;display:inline-block;">Queued:</span> |
|
||||||
<div id="queuecontents" style="overflow-y: scroll;height: 100px;background-color: #d0d0d0;padding: 5px;"> |
|
||||||
<a>1</a> |
|
||||||
<button style="font-size: 10px;">Load</button> |
|
||||||
<button style="font-size: 10px;">Delete</button> |
|
||||||
<br> |
|
||||||
<br> |
|
||||||
</div> |
|
||||||
<span style="padding: 5px;display:inline-block;"> |
|
||||||
<button style="font-size: 12px;" onclick="clearItems('queue')">Clear Queue</button> |
|
||||||
<button style="font-size: 12px;" onclick="loadQueue()">Refresh</button> |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div id="historybutton-content" style="background-color: #e1e1e1;min-width: 160px;display: none;z-index: 101;"> |
|
||||||
<span style="width:100%;padding: 3px;display:inline-block;">History:</span> |
|
||||||
<div id="historycontents" style="overflow-y: scroll;height: 100px;background-color: #d0d0d0;padding: 5px;"> |
|
||||||
</div> |
|
||||||
<span style="padding: 5px;display:inline-block;"> |
|
||||||
<button style="font-size: 12px;" onclick="clearItems('history')">Clear History</button> |
|
||||||
<button style="font-size: 12px;" onclick="loadHistory()">Refresh</button> |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
<br> |
|
||||||
<button style="font-size: 20px;" onclick="saveGraph()">Save</button><br> |
|
||||||
<button style="font-size: 20px;" onclick="loadGraph()">Load</button> |
|
||||||
<br> |
|
||||||
<button style="font-size: 20px;" onclick="clearGraph()">Clear</button><br> |
|
||||||
<button style="font-size: 20px;" onclick="loadTxt2Img()">Load Default</button><br> |
|
||||||
</span> |
|
||||||
</body> |
|
||||||
</html> |
</html> |
||||||
|
@ -0,0 +1,45 @@ |
|||||||
|
export function getPngMetadata(file) { |
||||||
|
return new Promise((r) => { |
||||||
|
const reader = new FileReader(); |
||||||
|
reader.onload = (event) => { |
||||||
|
// Get the PNG data as a Uint8Array
|
||||||
|
const pngData = new Uint8Array(event.target.result); |
||||||
|
const dataView = new DataView(pngData.buffer); |
||||||
|
|
||||||
|
// Check that the PNG signature is present
|
||||||
|
if (dataView.getUint32(0) !== 0x89504e47) { |
||||||
|
console.error("Not a valid PNG file"); |
||||||
|
r(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// Start searching for chunks after the PNG signature
|
||||||
|
let offset = 8; |
||||||
|
let txt_chunks = {}; |
||||||
|
// Loop through the chunks in the PNG file
|
||||||
|
while (offset < pngData.length) { |
||||||
|
// Get the length of the chunk
|
||||||
|
const length = dataView.getUint32(offset); |
||||||
|
// Get the chunk type
|
||||||
|
const type = String.fromCharCode(...pngData.slice(offset + 4, offset + 8)); |
||||||
|
if (type === "tEXt") { |
||||||
|
// Get the keyword
|
||||||
|
let keyword_end = offset + 8; |
||||||
|
while (pngData[keyword_end] !== 0) { |
||||||
|
keyword_end++; |
||||||
|
} |
||||||
|
const keyword = String.fromCharCode(...pngData.slice(offset + 8, keyword_end)); |
||||||
|
// Get the text
|
||||||
|
const text = String.fromCharCode(...pngData.slice(keyword_end + 1, offset + 8 + length)); |
||||||
|
txt_chunks[keyword] = text; |
||||||
|
} |
||||||
|
|
||||||
|
offset += 12 + length; |
||||||
|
} |
||||||
|
|
||||||
|
r(txt_chunks); |
||||||
|
}; |
||||||
|
|
||||||
|
reader.readAsArrayBuffer(file); |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,152 @@ |
|||||||
|
import { api } from "./api.js"; |
||||||
|
|
||||||
|
class ComfyDialog { |
||||||
|
constructor() { |
||||||
|
this.element = document.createElement("div"); |
||||||
|
this.element.classList.add("comfy-modal"); |
||||||
|
|
||||||
|
const content = document.createElement("div"); |
||||||
|
content.classList.add("comfy-modal-content"); |
||||||
|
this.textElement = document.createElement("p"); |
||||||
|
content.append(this.textElement); |
||||||
|
|
||||||
|
const closeBtn = document.createElement("button"); |
||||||
|
closeBtn.type = "button"; |
||||||
|
closeBtn.textContent = "CLOSE"; |
||||||
|
content.append(closeBtn); |
||||||
|
closeBtn.onclick = () => this.close(); |
||||||
|
|
||||||
|
this.element.append(content); |
||||||
|
document.body.append(this.element); |
||||||
|
} |
||||||
|
|
||||||
|
close() { |
||||||
|
this.element.style.display = "none"; |
||||||
|
} |
||||||
|
|
||||||
|
show(html) { |
||||||
|
this.textElement.innerHTML = html; |
||||||
|
this.element.style.display = "flex"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class ComfyList { |
||||||
|
constructor() { |
||||||
|
this.element = document.createElement("div"); |
||||||
|
this.element.style.display = "none"; |
||||||
|
this.element.textContent = "hello"; |
||||||
|
} |
||||||
|
|
||||||
|
get visible() { |
||||||
|
return this.element.style.display !== "none"; |
||||||
|
} |
||||||
|
|
||||||
|
async load() { |
||||||
|
// const queue = await api.getQueue();
|
||||||
|
} |
||||||
|
|
||||||
|
async update() { |
||||||
|
if (this.visible) { |
||||||
|
await this.load(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async show() { |
||||||
|
this.element.style.display = "block"; |
||||||
|
await this.load(); |
||||||
|
} |
||||||
|
|
||||||
|
hide() { |
||||||
|
this.element.style.display = "none"; |
||||||
|
} |
||||||
|
|
||||||
|
toggle() { |
||||||
|
if (this.visible) { |
||||||
|
this.hide(); |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
this.show(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export class ComfyUI { |
||||||
|
constructor(app) { |
||||||
|
this.app = app; |
||||||
|
this.dialog = new ComfyDialog(); |
||||||
|
this.queue = new ComfyList(); |
||||||
|
this.history = new ComfyList(); |
||||||
|
|
||||||
|
this.menuContainer = document.createElement("div"); |
||||||
|
this.menuContainer.classList.add("comfy-menu"); |
||||||
|
|
||||||
|
this.queueSize = document.createElement("span"); |
||||||
|
this.menuContainer.append(this.queueSize); |
||||||
|
|
||||||
|
this.addAction("Queue Prompt", () => { |
||||||
|
app.queuePrompt(0); |
||||||
|
}, "queue"); |
||||||
|
|
||||||
|
this.btnContainer = document.createElement("div"); |
||||||
|
this.btnContainer.classList.add("comfy-menu-btns"); |
||||||
|
this.menuContainer.append(this.btnContainer); |
||||||
|
|
||||||
|
this.addAction( |
||||||
|
"Queue Front", |
||||||
|
() => { |
||||||
|
app.queuePrompt(-1); |
||||||
|
}, |
||||||
|
"sm" |
||||||
|
); |
||||||
|
|
||||||
|
this.addAction( |
||||||
|
"See Queue", |
||||||
|
(btn) => { |
||||||
|
btn.textContent = this.queue.toggle() ? "Close" : "See Queue"; |
||||||
|
}, |
||||||
|
"sm" |
||||||
|
); |
||||||
|
|
||||||
|
this.addAction( |
||||||
|
"See History", |
||||||
|
(btn) => { |
||||||
|
btn.textContent = this.history.toggle() ? "Close" : "See History"; |
||||||
|
}, |
||||||
|
"sm" |
||||||
|
); |
||||||
|
|
||||||
|
this.menuContainer.append(this.queue.element); |
||||||
|
this.menuContainer.append(this.history.element); |
||||||
|
|
||||||
|
this.addAction("Save", () => { |
||||||
|
app.queuePrompt(-1); |
||||||
|
}); |
||||||
|
this.addAction("Load", () => { |
||||||
|
app.queuePrompt(-1); |
||||||
|
}); |
||||||
|
this.addAction("Clear", () => { |
||||||
|
app.queuePrompt(-1); |
||||||
|
}); |
||||||
|
this.addAction("Load Default", () => { |
||||||
|
app.queuePrompt(-1); |
||||||
|
}); |
||||||
|
|
||||||
|
document.body.append(this.menuContainer); |
||||||
|
this.setStatus({ exec_info: { queue_remaining: "X" } }); |
||||||
|
} |
||||||
|
|
||||||
|
addAction(text, cb, cls) { |
||||||
|
const btn = document.createElement("button"); |
||||||
|
btn.classList.add("comfy-menu-btn-" + (cls || "lg")); |
||||||
|
btn.textContent = text; |
||||||
|
btn.onclick = () => { |
||||||
|
cb(btn); |
||||||
|
}; |
||||||
|
(cls === "sm" ? this.btnContainer : this.menuContainer).append(btn); |
||||||
|
} |
||||||
|
|
||||||
|
setStatus(status) { |
||||||
|
this.queueSize.textContent = "Queue size: " + (status ? status.exec_info.queue_remaining : "ERR"); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue