You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
808 lines
30 KiB
808 lines
30 KiB
<html> |
|
<head> |
|
<link rel="stylesheet" type="text/css" href="litegraph.css"> |
|
<script type="text/javascript" src="litegraph.core.js"></script> |
|
</head> |
|
<style> |
|
.customtext_input { |
|
background-color: #FFFFFF; |
|
} |
|
@media (prefers-color-scheme: dark) { |
|
body { |
|
background-color: #202020; |
|
} |
|
.customtext_input { |
|
background-color: #202020; |
|
color: white; |
|
} |
|
} |
|
</style> |
|
<body style='width:100%; height:100%; overflow: hidden;'> |
|
<style> |
|
.modal { |
|
display: none; /* Hidden by default */ |
|
position: fixed; /* Stay in place */ |
|
z-index: 100; /* Sit on top */ |
|
left: 50%; /* Center the modal horizontally */ |
|
top: 50%; /* Center the modal vertically */ |
|
transform: translate(-50%, -50%); /* Use this to center the modal */ |
|
width: 50%; /* Set a width for the modal */ |
|
height: auto; /* Set a height for the modal */ |
|
padding: 30px; |
|
background-color: #ff0000; /* Modal background */ |
|
box-shadow: 0px 0px 20px #888888; |
|
border-radius: 10px; |
|
text-align: center; |
|
} |
|
|
|
.close { |
|
color: #aaaaaa; |
|
font-size: 24px; /* Decreased font-size */ |
|
font-weight: bold; |
|
position: absolute; |
|
bottom: 10px; /* move the close button up a bit */ |
|
left: 50%; /* center the close button horizontally */ |
|
transform: translateX(-50%); /* use this to center the close button horizontally */ |
|
width: 100%; |
|
text-align: center; /* center the text inside the button */ |
|
} |
|
|
|
.close:hover, |
|
.close:focus { |
|
color: #000; |
|
text-decoration: none; |
|
cursor: pointer; |
|
} |
|
|
|
#modal-text { |
|
white-space: pre-line; /* This will respect line breaks */ |
|
margin-bottom: 20px; /* Add some margin between the text and the close button*/ |
|
} |
|
</style> |
|
<div id="myErrorModal" class="modal"> |
|
<div class="modal-content"> |
|
<p id="modal-text"></p> |
|
<span class="close">CLOSE</span> |
|
</div> |
|
</div> |
|
|
|
<canvas id='mycanvas' width='1000' height='1000' style='width: 100%; height: 100%;'></canvas> |
|
<script> |
|
|
|
var graph = new LGraph(); |
|
|
|
var canvas = new LGraphCanvas("#mycanvas", graph); |
|
|
|
const ccc = document.getElementById("mycanvas"); |
|
const ctx = ccc.getContext("2d"); |
|
|
|
// Resize the canvas to match the size of the canvas element |
|
function resizeCanvas() { |
|
ccc.width = ccc.offsetWidth; |
|
ccc.height = ccc.offsetHeight; |
|
canvas.draw(true, true); |
|
} |
|
// call the function when the page loads |
|
resizeCanvas(); |
|
// call the function when the window is resized |
|
window.addEventListener("resize", resizeCanvas); |
|
|
|
var default_graph = {"last_node_id":9,"last_link_id":9,"nodes":[{"id":7,"type":"CLIPTextEncode","pos":[413,389],"size":{"0":425.27801513671875,"1":180.6060791015625},"flags":{},"order":3,"mode":0,"inputs":[{"name":"clip","type":"CLIP","link":5}],"outputs":[{"name":"CONDITIONING","type":"CONDITIONING","links":[6],"slot_index":0}],"properties":{},"widgets_values":["bad hands"]},{"id":6,"type":"CLIPTextEncode","pos":[415,186],"size":{"0":422.84503173828125,"1":164.31304931640625},"flags":{},"order":2,"mode":0,"inputs":[{"name":"clip","type":"CLIP","link":3}],"outputs":[{"name":"CONDITIONING","type":"CONDITIONING","links":[4],"slot_index":0}],"properties":{},"widgets_values":["masterpiece best quality girl"]},{"id":5,"type":"EmptyLatentImage","pos":[473,609],"size":{"0":315,"1":106},"flags":{},"order":1,"mode":0,"outputs":[{"name":"LATENT","type":"LATENT","links":[2],"slot_index":0}],"properties":{},"widgets_values":[512,512,1]},{"id":3,"type":"KSampler","pos":[863,186],"size":{"0":315,"1":262},"flags":{},"order":4,"mode":0,"inputs":[{"name":"model","type":"MODEL","link":1},{"name":"positive","type":"CONDITIONING","link":4},{"name":"negative","type":"CONDITIONING","link":6},{"name":"latent_image","type":"LATENT","link":2}],"outputs":[{"name":"LATENT","type":"LATENT","links":[7],"slot_index":0}],"properties":{},"widgets_values":[8566257,true,20,8,"sample_euler","normal",1]},{"id":8,"type":"VAEDecode","pos":[1209,188],"size":{"0":210,"1":46},"flags":{},"order":5,"mode":0,"inputs":[{"name":"samples","type":"LATENT","link":7},{"name":"vae","type":"VAE","link":8}],"outputs":[{"name":"IMAGE","type":"IMAGE","links":[9],"slot_index":0}],"properties":{}},{"id":9,"type":"SaveImage","pos":[1451,189],"size":{"0":210,"1":26},"flags":{},"order":6,"mode":0,"inputs":[{"name":"images","type":"IMAGE","link":9}],"properties":{}},{"id":4,"type":"CheckpointLoader","pos":[26,474],"size":{"0":315,"1":122},"flags":{},"order":0,"mode":0,"outputs":[{"name":"MODEL","type":"MODEL","links":[1],"slot_index":0},{"name":"CLIP","type":"CLIP","links":[3,5],"slot_index":1},{"name":"VAE","type":"VAE","links":[8],"slot_index":2}],"properties":{},"widgets_values":["v1-inference.yaml","v1-5-pruned-emaonly.ckpt"]}],"links":[[1,4,0,3,0,"MODEL"],[2,5,0,3,3,"LATENT"],[3,4,1,6,0,"CLIP"],[4,6,0,3,1,"CONDITIONING"],[5,4,1,7,0,"CLIP"],[6,7,0,3,2,"CONDITIONING"],[7,3,0,8,0,"LATENT"],[8,4,2,8,1,"VAE"],[9,8,0,9,0,"IMAGE"]],"groups":[],"config":{},"extra":{},"version":0.4} |
|
|
|
function loadGraphData(graph, graph_data) |
|
{ |
|
graph.configure( graph_data); |
|
for (let n in graph._nodes) { |
|
n = graph._nodes[n]; |
|
s = n.computeSize(); |
|
s[0] = Math.max(n.size[0], s[0]); |
|
s[1] = Math.max(n.size[1], s[1]); |
|
n.size = s; |
|
} |
|
} |
|
|
|
function afterLoadGraph() |
|
{ |
|
let workflow = null; |
|
try { |
|
workflow = JSON.parse(localStorage.getItem("workflow")); |
|
loadGraphData(graph, workflow); |
|
} catch(err) { |
|
} |
|
|
|
if (!workflow) { |
|
loadGraphData(graph, default_graph); |
|
} |
|
|
|
function saveGraph() { |
|
localStorage.setItem("workflow", JSON.stringify(graph.serialize())); |
|
} |
|
|
|
setInterval(saveGraph, 1000); |
|
|
|
} |
|
|
|
function onObjectInfo(json) { |
|
for (let key in json) { |
|
function MyNode() |
|
{ |
|
j = MyNode.__json_data; |
|
inp = j['input']['required']; |
|
this.class_comfy = MyNode.class_type_comfy; |
|
this._widgets = [] |
|
min_height = 1; |
|
min_width = 1; |
|
for (let x in inp) { |
|
let default_val = min_val = max_val = step_val = multiline = dynamic_prompt = undefined; |
|
if (inp[x].length > 1) { |
|
default_val = inp[x][1]['default']; |
|
min_val = inp[x][1]['min']; |
|
max_val = inp[x][1]['max']; |
|
step_val = inp[x][1]['step']; |
|
multiline = inp[x][1]['multiline']; |
|
dynamic_prompt = inp[x][1]['dynamic_prompt']; |
|
} |
|
|
|
let type = inp[x][0]; |
|
if (Array.isArray(type)) { |
|
w = this.addWidget("combo", x, type[0], function(v){}, { values: type } ); |
|
this._widgets += [w] |
|
} else if (type == "INT") { |
|
if (default_val == undefined) default_val = 0; |
|
if (min_val == undefined) min_val = 0; |
|
if (max_val == undefined) max_val = 2048; |
|
if (step_val == undefined) step_val = 1; |
|
w = this.addWidget("number", x, default_val, function(v){let s = this.options.step / 10;this.value = Math.round( v / s ) * s;}, { min: min_val, max: max_val, step: 10.0 * step_val} ); |
|
this._widgets += [w] |
|
if (x == "seed" || x == "noise_seed") { |
|
w1 = this.addWidget("toggle", "Random seed after every gen", true, function(v){}, { on: "enabled", off:"disabled"} ); |
|
w1.to_randomize = w; |
|
this._widgets += [w1] |
|
} |
|
} else if (type == "FLOAT") { |
|
if (default_val == undefined) default_val = 0; |
|
if (min_val == undefined) min_val = 0; |
|
if (max_val == undefined) max_val = 2048; |
|
if (step_val == undefined) step_val = 0.5; |
|
|
|
// if (min_val == 0.0 && max_val == 1.0) { |
|
// w = this.slider = this.addWidget("slider", x, default_val, function(v){}, { min: min_val, max: max_val} ); |
|
// } else { |
|
w = this.addWidget("number", x, default_val, function(v){}, { min: min_val, max: max_val, step: 10.0 * step_val} ); |
|
// } |
|
this._widgets += [w] |
|
} else if (type == "STRING") { |
|
if (default_val == undefined) default_val = ""; |
|
if (multiline == undefined) multiline = false; |
|
if (dynamic_prompt == undefined) dynamic_prompt = false; |
|
|
|
if (multiline) { |
|
var w = { |
|
type: "customtext", |
|
name: x, |
|
dynamic_prompt: dynamic_prompt, |
|
get value() { return this.input_div.innerText;}, |
|
set value(x) { this.input_div.innerText = x;}, |
|
callback: function(v){console.log(v);}, |
|
options: {}, |
|
draw: function(ctx, node, widget_width, y, H){ |
|
var show_text = canvas.ds.scale > 0.5; |
|
// this.input_div.style.top = `${y}px`; |
|
let t = ctx.getTransform(); |
|
let margin = 10; |
|
let x_div = t.a * margin * 2 + t.e; |
|
let y_div = t.d * (y + H) + t.f; |
|
let width_div = (widget_width - margin * 2 - 3) * t.a; |
|
let height_div = (this.parent.size[1] - (y + H) - 3)* t.d; |
|
this.input_div.style.left = `${x_div}px`; |
|
this.input_div.style.top = `${y_div}px`; |
|
this.input_div.style.width = width_div; |
|
this.input_div.style.height = height_div; |
|
this.input_div.style.position = 'absolute'; |
|
this.input_div.style.zIndex = 1; |
|
this.input_div.style.fontSize = t.d * 10.0; |
|
|
|
if (show_text) { |
|
this.input_div.hidden = false; |
|
} else { |
|
this.input_div.hidden = true; |
|
} |
|
|
|
ctx.save(); |
|
// ctx.fillText(String(this.value).substr(0,30), 0, y + H * 0.7); |
|
ctx.restore(); |
|
}, |
|
}; |
|
w.input_div = document.createElement('div'); |
|
w.input_div.contentEditable = true; |
|
w.input_div.className = "customtext_input"; |
|
w.input_div.style.overflow = 'hidden'; |
|
w.input_div.style.overflowY = 'auto'; |
|
w.input_div.style.padding = 2; |
|
w.input_div.innerText = default_val; |
|
document.addEventListener('click', function(event) { |
|
if (!w.input_div.contains(event.target)) { |
|
w.input_div.blur(); |
|
} |
|
}); |
|
w.parent = this; |
|
min_height = Math.max(min_height, 200); |
|
min_width = Math.max(min_width, 400); |
|
ccc.parentNode.appendChild(w.input_div); |
|
|
|
w = this.addCustomWidget(w); |
|
canvas.onDrawBackground = function() { |
|
for (let n in graph._nodes) { |
|
n = graph._nodes[n]; |
|
for (let w in n.widgets) { |
|
let wid = n.widgets[w]; |
|
if (Object.hasOwn(wid, 'input_div')) { |
|
wid.input_div.style.left = -8000; |
|
wid.input_div.style.position = 'absolute'; |
|
} |
|
} |
|
} |
|
} |
|
// w = this.addWidget("text", x, "", function(v){}, { multiline:true } ); |
|
console.log(w, this); |
|
this._widgets += [w] |
|
this.onRemoved = function() { |
|
for (let y in this.widgets) { |
|
if (this.widgets[y].input_div) { |
|
this.widgets[y].input_div.remove(); |
|
} |
|
} |
|
} |
|
} else { |
|
w = this.addWidget("text", x, default_val, function(v){}, { multiline:false } ); |
|
w.dynamic_prompt = dynamic_prompt; |
|
this._widgets += [w]; |
|
} |
|
} else { |
|
this.addInput(x, type); |
|
} |
|
} |
|
|
|
out = j['output']; |
|
for (let x in out) { |
|
this.addOutput(out[x], out[x]); |
|
} |
|
s = this.computeSize(); |
|
s[0] *= 1.5; |
|
s[0] = Math.max(min_width, s[0]); |
|
s[1] = Math.max(min_height, s[1]); |
|
this.size = s; |
|
this.serialize_widgets = true; |
|
} |
|
MyNode.title = json[key]['name']; |
|
MyNode.class_type_comfy = json[key]['name']; |
|
MyNode.__json_data = json[key] |
|
|
|
LiteGraph.registerNodeType(key, MyNode); |
|
MyNode.category = json[key]['category']; |
|
}; |
|
|
|
afterLoadGraph(); |
|
// loadGraphData(graph, JSON.parse(base_txt2img_graph)); |
|
} |
|
|
|
fetch("object_info", {cache: "no-store"}) |
|
.then(response => response.json()) |
|
.then(json => onObjectInfo(json)); |
|
|
|
|
|
//register in the system |
|
graph.start(); |
|
// LiteGraph.registerNodeType("testing", MyAddNode); |
|
|
|
|
|
graph.onNodeRemoved = function(n) { |
|
for (let y in n.widgets) { |
|
if (n.widgets[y].input_div) { |
|
n.widgets[y].input_div.remove(); |
|
} |
|
} |
|
} |
|
|
|
function graphToPrompt() { |
|
let s = graph.serialize(); |
|
let output = {}; |
|
// console.log(s['nodes']); |
|
nodes = s['nodes'] |
|
|
|
for (let x in nodes) { |
|
let n = graph.getNodeById(nodes[x].id); |
|
let input_ = {}; |
|
|
|
// dynamic prompts handling |
|
if (n.widgets && n.widgets.length > 0) { |
|
// find widgets declared as supporting dynamic prompting |
|
var supportedWidgets = n.widgets.filter(e => e.dynamic_prompt === true); |
|
if (supportedWidgets.length > 0) { |
|
|
|
// resolve dynamic prompts for all widgets supporting it in this node |
|
for (let i in supportedWidgets) |
|
{ |
|
var widget = supportedWidgets[i]; |
|
|
|
// store the unresolved prompt to restore it after sending the resolved prompt to the backend |
|
// use of .innerText which keep \n symbols in order to not break multilines support |
|
widget.value_initial = widget.input_div.innerText; |
|
|
|
// resolve the string |
|
var prompt = widget.input_div.innerText; |
|
while (prompt.replace("\\{", "").includes('{') && prompt.replace("\\}", "").includes('}')) { |
|
const startIndex = prompt.replace("\\{", "00").indexOf('{'); |
|
const endIndex = prompt.replace("\\}", "00").indexOf('}'); |
|
|
|
const optionsString = prompt.substring(startIndex + 1, endIndex); |
|
const options = optionsString.split('|'); |
|
|
|
const randomIndex = Math.floor(Math.random() * options.length); |
|
const randomOption = options[randomIndex]; |
|
|
|
prompt = prompt.substring(0, startIndex) + randomOption + prompt.substring(endIndex + 1); |
|
} |
|
|
|
widget.value = prompt; |
|
} |
|
} |
|
} |
|
|
|
for (let y in n.widgets) { |
|
if (!Object.hasOwn(n.widgets[y], 'to_randomize')) { //don't include "Random seed after every gen" in prompt. |
|
if (n.widgets[y].dynamic_prompt && n.widgets[y].dynamic_prompt === true) { |
|
input_[n.widgets[y].name] = n.widgets[y].value.replace("\\{", "{").replace("\\}", "}"); |
|
} else { |
|
input_[n.widgets[y].name] = n.widgets[y].value; |
|
} |
|
} |
|
} |
|
for (let y in n.inputs) { |
|
let parent_node = n.getInputNode(y); |
|
if (parent_node) { |
|
for (let z in parent_node.outputs) { |
|
let c_nodes = parent_node.getOutputNodes(z); |
|
// console.log(c_nodes, z); |
|
if (c_nodes) { |
|
for (let zz in c_nodes) { |
|
if (c_nodes[zz].id == n.id && parent_node.outputs[z].links.includes(n.inputs[y].link)) { |
|
input_[n.inputs[y].name] = [String(parent_node.id), parseInt(z)]; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
let node = {} |
|
node['inputs'] = input_; |
|
node['class_type'] = n.class_comfy; |
|
// inputs = x['inputs'] |
|
// inputs['name'], inputs['id'] |
|
// console.log(x, n); |
|
// console.log(node); |
|
output[String(n.id)] = node; |
|
} |
|
|
|
return output; |
|
} |
|
|
|
function closeModal() { |
|
var modal = document.getElementById("myErrorModal"); |
|
modal.style.display = "none"; |
|
} |
|
|
|
function showModal(text) { |
|
var modal = document.getElementById("myErrorModal"); |
|
var modalText = document.getElementById("modal-text"); |
|
modalText.innerHTML = text; |
|
modal.style.display = "block"; |
|
var closeBtn = modal.getElementsByClassName("close")[0]; |
|
closeBtn.onclick = function(event) {closeModal();} |
|
} |
|
|
|
|
|
function promptPosted(data) |
|
{ |
|
if (data.status != 200) { |
|
data.text().then(dt => showModal(dt)); |
|
return; |
|
} |
|
|
|
let s = graph.serialize(); |
|
let output = {}; |
|
// console.log(s['nodes']); |
|
nodes = s['nodes'] |
|
|
|
for (let x in nodes) { |
|
let n = graph.getNodeById(nodes[x].id); |
|
for (let w in n.widgets) { |
|
let wid = n.widgets[w]; |
|
if (Object.hasOwn(wid, 'to_randomize')) { |
|
if (wid.value) { |
|
wid.to_randomize.value = Math.floor(Math.random() * 1125899906842624); |
|
} |
|
} |
|
} |
|
} |
|
|
|
canvas.draw(true, true); |
|
loadQueueIfVisible(); |
|
} |
|
|
|
function postPrompt(number) { |
|
let prompt = graphToPrompt(); |
|
let full_data = {prompt: prompt, extra_data: {extra_pnginfo: {workflow: graph.serialize()}}}; |
|
if (number == -1) { |
|
full_data.front = true; |
|
} else |
|
if (number != 0) { |
|
full_data.number = number; |
|
} |
|
|
|
fetch('/prompt', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: JSON.stringify(full_data) |
|
}) |
|
.then(data => promptPosted(data)) |
|
.catch(error => console.error(error)) |
|
|
|
// console.log(JSON.stringify(prompt)); |
|
// console.log(JSON.stringify(graph.serialize())); |
|
|
|
// restore initial values replaced by dynamic prompting |
|
for (let x in graph._nodes_by_id) { |
|
let n = graph._nodes_by_id[x]; |
|
for (let w in n.widgets) { |
|
let wid = n.widgets[w]; |
|
if (wid.dynamic_prompt && wid.dynamic_prompt === true) |
|
wid.value = wid.value_initial; |
|
} |
|
} |
|
} |
|
|
|
|
|
function promptToGraph(prompt) { |
|
for (let x in prompt) { |
|
|
|
} |
|
} |
|
|
|
function prompt_file_load(file) |
|
{ |
|
if (file.type === 'image/png') { |
|
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'); |
|
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; |
|
} |
|
|
|
// Get the next chunk |
|
offset += 12 + length; |
|
} |
|
console.log(txt_chunks); |
|
// console.log(JSON.parse(txt_chunks["prompt"])); |
|
loadGraphData(graph, JSON.parse(txt_chunks["workflow"])); |
|
}; |
|
reader.readAsArrayBuffer(file); |
|
} else if (file.type === "application/json" || file.name.endsWith(".json")) { |
|
var reader = new FileReader(); |
|
reader.onload = function() { |
|
console.log(reader.result); |
|
var jsonData = JSON.parse(reader.result); |
|
loadGraphData(graph, jsonData); |
|
}; |
|
reader.readAsText(file); |
|
} |
|
} |
|
|
|
// Get prompt from dropped PNG or json |
|
document.addEventListener('drop', (event) => { |
|
event.preventDefault(); |
|
event.stopPropagation(); |
|
const file = event.dataTransfer.files[0]; |
|
console.log(file.type); |
|
prompt_file_load(file); |
|
}); |
|
|
|
|
|
(() => { |
|
function updateStatus(data) { |
|
document.getElementById("queuesize").innerHTML = "Queue size: " + (data ? data.exec_info.queue_remaining : "ERR"); |
|
} |
|
|
|
//fix for colab and other things that don't support websockets. |
|
function manually_fetch_queue() { |
|
fetch('/prompt') |
|
.then(response => response.json()) |
|
.then(data => { |
|
updateStatus(data); |
|
}).catch((response) => {updateStatus(null)}); |
|
} |
|
|
|
let ws; |
|
function createSocket(isReconnect) { |
|
if(ws) return; |
|
|
|
let opened = false; |
|
ws = new WebSocket(`ws${window.location.protocol === "https:"? "s" : ""}://${location.host}/ws`); |
|
|
|
ws.addEventListener("open", () => { |
|
opened = true; |
|
if(isReconnect) { |
|
closeModal(); |
|
} |
|
}); |
|
|
|
ws.addEventListener("error", () => { |
|
if(ws) ws.close(); |
|
manually_fetch_queue(); |
|
}); |
|
|
|
ws.addEventListener("close", () => { |
|
setTimeout(() => { |
|
ws = null; |
|
createSocket(true); |
|
}, 300); |
|
if(opened) { |
|
updateStatus(null); |
|
showModal("Reconnecting..."); |
|
} |
|
}); |
|
|
|
ws.addEventListener("message", (event) => { |
|
try { |
|
const msg = JSON.parse(event.data); |
|
switch(msg.type) { |
|
case "status": |
|
updateStatus(msg.status) |
|
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(delete_id, then) { |
|
fetch('/queue', { |
|
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() { |
|
fetch('/queue') |
|
.then(response => response.json()) |
|
.then(data => { |
|
var queue_div = document.getElementById("queuebutton-content"); |
|
queue_div.style.display = 'block'; |
|
var see_queue_button = document.getElementById("seequeuebutton"); |
|
let old_w = see_queue_button.style.width; |
|
see_queue_button.innerHTML = "Close"; |
|
|
|
let runningcontents = document.getElementById("runningcontents"); |
|
runningcontents.innerHTML = ''; |
|
let queuecontents = document.getElementById("queuecontents"); |
|
queuecontents.innerHTML = ''; |
|
function append_to_list(list_element, append_to_element, append_delete) { |
|
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); |
|
}; |
|
|
|
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(event.target.delete_id, loadQueue); |
|
}; |
|
append_to_element.appendChild(button); |
|
} |
|
append_to_element.appendChild(document.createElement("br")); |
|
} |
|
for (let x in data.queue_running) { |
|
append_to_list(data.queue_running[x], runningcontents, false); |
|
} |
|
|
|
data.queue_pending.sort((a, b) => a[0] - b[0]); |
|
for (let x in data.queue_pending) { |
|
append_to_list(data.queue_pending[x], queuecontents, true); |
|
} |
|
}).catch((response) => {console.log(response)}); |
|
} |
|
|
|
function loadQueueIfVisible() |
|
{ |
|
var queue_div = document.getElementById("queuebutton-content"); |
|
if (queue_div.style.display == 'block') { |
|
loadQueue(); |
|
} |
|
} |
|
|
|
function seeQueue() { |
|
var queue_div = document.getElementById("queuebutton-content"); |
|
if (queue_div.style.display == 'block') { |
|
queue_div.style.display = 'none'; |
|
var see_queue_button = document.getElementById("seequeuebutton"); |
|
see_queue_button.innerHTML = "See Queue" |
|
} else { |
|
loadQueue(); |
|
} |
|
} |
|
|
|
function closeQueue() { |
|
var queue_div = document.getElementById("queuebutton-content"); |
|
queue_div.style.display = 'none'; |
|
} |
|
|
|
function clearQueue() { |
|
fetch('/queue', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json' |
|
}, |
|
body: JSON.stringify({"clear":true}) |
|
}).then(data => { |
|
loadQueue(); |
|
}) |
|
.catch(error => console.error(error)); |
|
} |
|
|
|
|
|
</script> |
|
|
|
<span 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> |
|
<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="clearQueue()">Clear Queue</button> |
|
<button style="font-size: 12px;" onclick="loadQueue()">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>
|
|
|