FizzleDorf

Untitled

Mar 26th, 2023
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.15 KB | None | 0 0
  1. function getNumberDefaults(inputData, defaultStep) {
  2. let defaultVal = inputData[1]["default"];
  3. let { min, max, step } = inputData[1];
  4.  
  5. if (defaultVal == undefined) defaultVal = 0;
  6. if (min == undefined) min = 0;
  7. if (max == undefined) max = 2048;
  8. if (step == undefined) step = defaultStep;
  9.  
  10. return { val: defaultVal, config: { min, max, step: 10.0 * step } };
  11. }
  12. /*
  13.  
  14. export function addRandomizeWidget(node, targetWidget, name, defaultValue = false) {
  15. const randomize = node.addWidget("toggle", name, defaultValue, function (v) {}, {
  16. on: "enabled",
  17. off: "disabled",
  18. serialize: false, // Don't include this in prompt.
  19. });
  20.  
  21. randomize.afterQueued = () => {
  22. if (randomize.value) {
  23. const min = targetWidget.options?.min;
  24. let max = targetWidget.options?.max;
  25. if (min != null || max != null) {
  26. if (max) {
  27. // limit max to something that javascript can handle
  28. max = Math.min(1125899906842624, max);
  29. }
  30. targetWidget.value = Math.floor(Math.random() * ((max ?? 9999999999) - (min ?? 0) + 1) + (min ?? 0));
  31. } else {
  32. targetWidget.value = Math.floor(Math.random() * 1125899906842624);
  33. }
  34. }
  35. };
  36. return randomize;
  37. }
  38. */
  39.  
  40. export function addSeedControlWidget(node, targetWidget, defaultValue = "fixedSeed") {
  41. const seedControl = node.addWidget("select", name, defaultValue, function (v) {}, {
  42. options: [
  43. { value: "fixedSeed", label: "Fixed Seed" },
  44. { value: "increment", label: "Increment" },
  45. { value: "decrement", label: "Decrement" },
  46. { value: "randomize", label: "Randomize" },
  47. ],
  48. serialize: false, // Don't include this in prompt.
  49. });
  50.  
  51. seedControl.afterQueued = () => {
  52. if (seedControl.value === "increment") {
  53. targetWidget.value += 1;
  54. } else if (seedControl.value === "decrement") {
  55. targetWidget.value -= 1;
  56. } else if (seedControl.value === "randomize") {
  57. const min = targetWidget.options?.min;
  58. let max = targetWidget.options?.max;
  59. if (min != null || max != null) {
  60. if (max) {
  61. // limit max to something that javascript can handle
  62. max = Math.min(1125899906842624, max);
  63. }
  64. targetWidget.value = Math.floor(Math.random() * ((max ?? 9999999999) - (min ?? 0) + 1) + (min ?? 0));
  65. } else {
  66. targetWidget.value = Math.floor(Math.random() * 1125899906842624);
  67. }
  68. }
  69. };
  70.  
  71. //Add the seedControl widget to the node graph
  72. node.addWidget("div", null, null, function () {
  73. seedControl.render(null);
  74. }, {});
  75.  
  76. return seedControl;
  77. }
  78.  
  79.  
  80. function seedWidget(node, inputName, inputData) {
  81. const seed = ComfyWidgets.INT(node, inputName, inputData);
  82. const seedControl = addSeedControlWidget(node, seed.widget);
  83.  
  84. seed.widget.linkedWidgets = [seedControl];
  85. return { widget: seed, seedControl };
  86. }
  87.  
  88. const MultilineSymbol = Symbol();
  89. const MultilineResizeSymbol = Symbol();
  90.  
  91. function addMultilineWidget(node, name, opts, app) {
  92. const MIN_SIZE = 50;
  93.  
  94. function computeSize(size) {
  95. if (node.widgets[0].last_y == null) return;
  96.  
  97. let y = node.widgets[0].last_y;
  98. let freeSpace = size[1] - y;
  99.  
  100. // Compute the height of all non customtext widgets
  101. let widgetHeight = 0;
  102. const multi = [];
  103. for (let i = 0; i < node.widgets.length; i++) {
  104. const w = node.widgets[i];
  105. if (w.type === "customtext") {
  106. multi.push(w);
  107. } else {
  108. if (w.computeSize) {
  109. widgetHeight += w.computeSize()[1] + 4;
  110. } else {
  111. widgetHeight += LiteGraph.NODE_WIDGET_HEIGHT + 4;
  112. }
  113. }
  114. }
  115.  
  116. // See how large each text input can be
  117. freeSpace -= widgetHeight;
  118. freeSpace /= multi.length;
  119.  
  120. if (freeSpace < MIN_SIZE) {
  121. // There isnt enough space for all the widgets, increase the size of the node
  122. freeSpace = MIN_SIZE;
  123. node.size[1] = y + widgetHeight + freeSpace * multi.length;
  124. node.graph.setDirtyCanvas(true);
  125. }
  126.  
  127. // Position each of the widgets
  128. for (const w of node.widgets) {
  129. w.y = y;
  130. if (w.type === "customtext") {
  131. y += freeSpace;
  132. } else if (w.computeSize) {
  133. y += w.computeSize()[1] + 4;
  134. } else {
  135. y += LiteGraph.NODE_WIDGET_HEIGHT + 4;
  136. }
  137. }
  138.  
  139. node.inputHeight = freeSpace;
  140. }
  141.  
  142. const widget = {
  143. type: "customtext",
  144. name,
  145. get value() {
  146. return this.inputEl.value;
  147. },
  148. set value(x) {
  149. this.inputEl.value = x;
  150. },
  151. draw: function (ctx, _, widgetWidth, y, widgetHeight) {
  152. if (!this.parent.inputHeight) {
  153. // If we are initially offscreen when created we wont have received a resize event
  154. // Calculate it here instead
  155. computeSize(node.size);
  156. }
  157. const visible = app.canvas.ds.scale > 0.5 && this.type === "customtext";
  158. const t = ctx.getTransform();
  159. const margin = 10;
  160. Object.assign(this.inputEl.style, {
  161. left: `${t.a * margin + t.e}px`,
  162. top: `${t.d * (y + widgetHeight - margin - 3) + t.f}px`,
  163. width: `${(widgetWidth - margin * 2 - 3) * t.a}px`,
  164. height: `${(this.parent.inputHeight - margin * 2 - 4) * t.d}px`,
  165. position: "absolute",
  166. zIndex: 1,
  167. fontSize: `${t.d * 10.0}px`,
  168. });
  169. this.inputEl.hidden = !visible;
  170. },
  171. };
  172. widget.inputEl = document.createElement("textarea");
  173. widget.inputEl.className = "comfy-multiline-input";
  174. widget.inputEl.value = opts.defaultVal;
  175. widget.inputEl.placeholder = opts.placeholder || "";
  176. document.addEventListener("mousedown", function (event) {
  177. if (!widget.inputEl.contains(event.target)) {
  178. widget.inputEl.blur();
  179. }
  180. });
  181. widget.parent = node;
  182. document.body.appendChild(widget.inputEl);
  183.  
  184. node.addCustomWidget(widget);
  185.  
  186. app.canvas.onDrawBackground = function () {
  187. // Draw node isnt fired once the node is off the screen
  188. // if it goes off screen quickly, the input may not be removed
  189. // this shifts it off screen so it can be moved back if the node is visible.
  190. for (let n in app.graph._nodes) {
  191. n = graph._nodes[n];
  192. for (let w in n.widgets) {
  193. let wid = n.widgets[w];
  194. if (Object.hasOwn(wid, "inputEl")) {
  195. wid.inputEl.style.left = -8000 + "px";
  196. wid.inputEl.style.position = "absolute";
  197. }
  198. }
  199. }
  200. };
  201.  
  202. node.onRemoved = function () {
  203. // When removing this node we need to remove the input from the DOM
  204. for (let y in this.widgets) {
  205. if (this.widgets[y].inputEl) {
  206. this.widgets[y].inputEl.remove();
  207. }
  208. }
  209. };
  210.  
  211. widget.onRemove = () => {
  212. widget.inputEl?.remove();
  213.  
  214. // Restore original size handler if we are the last
  215. if (!--node[MultilineSymbol]) {
  216. node.onResize = node[MultilineResizeSymbol];
  217. delete node[MultilineSymbol];
  218. delete node[MultilineResizeSymbol];
  219. }
  220. };
  221.  
  222. if (node[MultilineSymbol]) {
  223. node[MultilineSymbol]++;
  224. } else {
  225. node[MultilineSymbol] = 1;
  226. const onResize = (node[MultilineResizeSymbol] = node.onResize);
  227.  
  228. node.onResize = function (size) {
  229. computeSize(size);
  230.  
  231. // Call original resizer handler
  232. if (onResize) {
  233. onResize.apply(this, arguments);
  234. }
  235. };
  236. }
  237.  
  238. return { minWidth: 400, minHeight: 200, widget };
  239. }
  240.  
  241. export const ComfyWidgets = {
  242. "INT:seed": seedWidget,
  243. "INT:noise_seed": seedWidget,
  244. FLOAT(node, inputName, inputData) {
  245. const { val, config } = getNumberDefaults(inputData, 0.5);
  246. return { widget: node.addWidget("number", inputName, val, () => {}, config) };
  247. },
  248. INT(node, inputName, inputData) {
  249. const { val, config } = getNumberDefaults(inputData, 1);
  250. Object.assign(config, { precision: 0 });
  251. return {
  252. widget: node.addWidget(
  253. "number",
  254. inputName,
  255. val,
  256. function (v) {
  257. const s = this.options.step / 10;
  258. this.value = Math.round(v / s) * s;
  259. },
  260. config
  261. ),
  262. };
  263. },
  264. STRING(node, inputName, inputData, app) {
  265. const defaultVal = inputData[1].default || "";
  266. const multiline = !!inputData[1].multiline;
  267.  
  268. if (multiline) {
  269. return addMultilineWidget(node, inputName, { defaultVal, ...inputData[1] }, app);
  270. } else {
  271. return { widget: node.addWidget("text", inputName, defaultVal, () => {}, {}) };
  272. }
  273. },
  274. COMBO(node, inputName, inputData) {
  275. const type = inputData[0];
  276. let defaultValue = type[0];
  277. if (inputData[1] && inputData[1].default) {
  278. defaultValue = inputData[1].default;
  279. }
  280. return { widget: node.addWidget("combo", inputName, defaultValue, () => {}, { values: type }) };
  281. },
  282. IMAGEUPLOAD(node, inputName, inputData, app) {
  283. const imageWidget = node.widgets.find((w) => w.name === "image");
  284. let uploadWidget;
  285.  
  286. function showImage(name) {
  287. // Position the image somewhere sensible
  288. if (!node.imageOffset) {
  289. node.imageOffset = uploadWidget.last_y ? uploadWidget.last_y + 25 : 75;
  290. }
  291.  
  292. const img = new Image();
  293. img.onload = () => {
  294. node.imgs = [img];
  295. app.graph.setDirtyCanvas(true);
  296. };
  297. img.src = `/view?filename=${name}&type=input`;
  298. }
  299.  
  300. // Add our own callback to the combo widget to render an image when it changes
  301. const cb = node.callback;
  302. imageWidget.callback = function () {
  303. showImage(imageWidget.value);
  304. if (cb) {
  305. return cb.apply(this, arguments);
  306. }
  307. };
  308.  
  309. // On load if we have a value then render the image
  310. // The value isnt set immediately so we need to wait a moment
  311. // No change callbacks seem to be fired on initial setting of the value
  312. requestAnimationFrame(() => {
  313. if (imageWidget.value) {
  314. showImage(imageWidget.value);
  315. }
  316. });
  317.  
  318. async function uploadFile(file, updateNode) {
  319. try {
  320. // Wrap file in formdata so it includes filename
  321. const body = new FormData();
  322. body.append("image", file);
  323. const resp = await fetch("/upload/image", {
  324. method: "POST",
  325. body,
  326. });
  327.  
  328. if (resp.status === 200) {
  329. const data = await resp.json();
  330. // Add the file as an option and update the widget value
  331. if (!imageWidget.options.values.includes(data.name)) {
  332. imageWidget.options.values.push(data.name);
  333. }
  334.  
  335. if (updateNode) {
  336. showImage(data.name);
  337.  
  338. imageWidget.value = data.name;
  339. }
  340. } else {
  341. alert(resp.status + " - " + resp.statusText);
  342. }
  343. } catch (error) {
  344. alert(error);
  345. }
  346. }
  347.  
  348. const fileInput = document.createElement("input");
  349. Object.assign(fileInput, {
  350. type: "file",
  351. accept: "image/jpeg,image/png",
  352. style: "display: none",
  353. onchange: async () => {
  354. if (fileInput.files.length) {
  355. await uploadFile(fileInput.files[0], true);
  356. }
  357. },
  358. });
  359. document.body.append(fileInput);
  360.  
  361. // Create the button widget for selecting the files
  362. uploadWidget = node.addWidget("button", "choose file to upload", "image", () => {
  363. fileInput.click();
  364. });
  365. uploadWidget.serialize = false;
  366.  
  367. // Add handler to check if an image is being dragged over our node
  368. node.onDragOver = function (e) {
  369. if (e.dataTransfer && e.dataTransfer.items) {
  370. const image = [...e.dataTransfer.items].find((f) => f.kind === "file" && f.type.startsWith("image/"));
  371. return !!image;
  372. }
  373.  
  374. return false;
  375. };
  376.  
  377. // On drop upload files
  378. node.onDragDrop = function (e) {
  379. console.log("onDragDrop called");
  380. let handled = false;
  381. for (const file of e.dataTransfer.files) {
  382. if (file.type.startsWith("image/")) {
  383. uploadFile(file, !handled); // Dont await these, any order is fine, only update on first one
  384. handled = true;
  385. }
  386. }
  387.  
  388. return handled;
  389. };
  390.  
  391. return { widget: uploadWidget };
  392. },
  393. };
  394.  
Advertisement
Add Comment
Please, Sign In to add comment