Q}{A1B3pQ*dcu$D+GtD=EU%{B1-yRQ!~rj%ZR7d
z4`s>)BC&o8J{MhagDm+h&n{lmHlknt
zSx?Y$cRS!Lwp#A56mD?y>t!{ZY*jbt05%8iB%Du9PkCPTIzON>2Z)5Igqoe-ZB3>dW9H`RQ}VjdugL
Rf2MFpQ&mr;O35zt{{gRV!s!42
literal 0
HcmV?d00001
diff --git a/pyproject.toml b/pyproject.toml
index 90c85ce..332cf6c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "save-image-extended-comfyui"
description = "Save as JXL, AVIF, WebP, JPEG, JPEG2k, customize the folder, sub-folders, and filenames of your images!"
-version = "2.86"
+version = "2.87"
license = { file = "LICENSE" }
dependencies = ["piexif", "imagecodecs", "pillow", "pillow-avif-plugin", "pillow-jxl-plugin"]
diff --git a/save_image_extended.py b/save_image_extended.py
index ab6f377..f67af13 100644
--- a/save_image_extended.py
+++ b/save_image_extended.py
@@ -14,7 +14,7 @@ import numpy
# import cv2 # not faster then PIL
-version = 2.86
+version = 2.87
avif_supported = False
jxl_supported = False
@@ -33,8 +33,9 @@ else:
avif_supported = True
# Jxl requires jxlpy wheel to be compiled, and a valid MSVC environment, which is complex task
+# Therefore we use pillow_jxl which makes more sense
try:
- # jxlpy is in early stages of development. None one has ever compiled it on Windows AFAIK
+ # jxlpy is in early stages of development. No one has ever compiled it on Windows in 2025 AFAIK, please prove me wrong
# from jxlpy import JXLImagePlugin
# from imagecodecs import (jpegxl_encode, jpegxl_decode, jpegxl_check, jpegxl_version, JPEGXL)
import pillow_jxl
diff --git a/web/js/contextmenu.js b/web/js/contextmenu.js
index f82e191..aebaad9 100644
--- a/web/js/contextmenu.js
+++ b/web/js/contextmenu.js
@@ -3,24 +3,28 @@ import { app } from "../../../scripts/app.js";
// Adds context menu entries, code partly from pyssssscustom-scripts
function addMenuHandler(nodeType, cb) {
+ // console.debug("SIE addMenuHandler nodeType:", nodeType);
+ // console.debug("SIE addMenuHandler cb:", cb);
const getOpts = nodeType.prototype.getExtraMenuOptions;
+
+ // the below is never called, i suspect app.js has changed a lot
nodeType.prototype.getExtraMenuOptions = function () {
const r = getOpts.apply(this, arguments);
cb.apply(this, arguments);
+ // console.debug("SIE addMenuHandler arguments:", arguments);
return r;
};
}
+
function addNode(name, nextTo, options) {
- console.log("name:", name);
- console.log("nextTo:", nextTo);
- options = { side: "left", select: true, shiftY: 0, shiftX: 0, ...(options || {}) };
+ // console.debug("SIE addNode name:", name);
+ // console.debug("SIE addNode nextTo:", nextTo);
+ options = { select: true, shiftY: 0, before: false, ...(options || {}) };
const node = LiteGraph.createNode(name);
app.graph.add(node);
-
node.pos = [
- options.side === "left" ? nextTo.pos[0] - (node.size[0] + options.offset): nextTo.pos[0] + nextTo.size[0] + options.offset,
-
+ options.before ? nextTo.pos[0] - node.size[0] - 30 : nextTo.pos[0] + nextTo.size[0] + 30,
nextTo.pos[1] + options.shiftY,
];
if (options.select) {
@@ -29,12 +33,49 @@ function addNode(name, nextTo, options) {
return node;
}
+// https://docs.comfy.org/custom-nodes/js/javascript_overview
app.registerExtension({
- name: "SIEContextmenu",
+ name: "SIE.QuickNodes",
+ async beforeRegisterNodeDef(nodeType, nodeData, app) {
+ if (
+ // this could go forever if we want to list all nodes that can output IMAGE
+ // nodeData.name === "VAEDecode" ||
+ // nodeData.name === "LoadImage" ||
+
+ // fortunately, output is an array where "IMAGE" is always the first item.
+ nodeData.output[0] == "IMAGE"
+ ) {
+ // console.debug("SIE.QuickNodes VAEDecode nodeType:", nodeType);
+ // console.debug("SIE.QuickNodes VAEDecode nodeData:", nodeData);
+ // console.debug("SIE.QuickNodes VAEDecode app:", app);
+
+ // https://docs.comfy.org/custom-nodes/js/javascript_examples#node-menu
+ // Code below adds SaveImageExtended option on right-click menu for VAEDecode
+ addMenuHandler(nodeType, function (_, options) {
+ options.unshift(
+ {
+ content: "💾 Add SaveImageExtended",
+ callback: () => {
+ // const saveNode = addNode("SaveImageExtended", this, { before: true });
+ const saveNode = addNode("SaveImageExtended", this);
+ this.connect(0, saveNode, 0);
+ },
+ },
+ );
+ });
+ }
+ },
+},
+
+// https://docs.comfy.org/custom-nodes/walkthrough#write-a-client-extension
+// I forgot why I added this one, probably not needed after all
+{
+ name: "SIE.Contextmenu",
async setup(app) {
+ // console.debug("SIEContextmenu app:", app);
app.ui.settings.addSetting({
id: "SIE.helpPopup",
- name: "🦛 SIE: Help popups",
+ name: "💾 SIE: Help popups",
defaultValue: true,
type: "boolean",
options: (value) => [
@@ -42,5 +83,11 @@ app.registerExtension({
{ value: false, text: "Off", selected: value === false },
],
});
+
+
}
});
+
+
+
+
diff --git a/web/js/purify.min.js b/web/js/purify.min.js
index 7fa10b6..709ac41 100644
--- a/web/js/purify.min.js
+++ b/web/js/purify.min.js
@@ -1,3 +1,3 @@
-/*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.0.8/LICENSE */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).DOMPurify=t()}(this,(function(){"use strict";var e=Object.hasOwnProperty,t=Object.setPrototypeOf,n=Object.isFrozen,r=Object.keys,o=Object.freeze,i=Object.seal,a="undefined"!=typeof Reflect&&Reflect,l=a.apply,c=a.construct;l||(l=function(e,t,n){return e.apply(t,n)}),o||(o=function(e){return e}),i||(i=function(e){return e}),c||(c=function(e,t){return new(Function.prototype.bind.apply(e,[null].concat(function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t1?n-1:0),o=1;o/gm),I=i(/^data-[\-\w.\u00B7-\uFFFF]/),U=i(/^aria-[\-\w]+$/),j=i(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),P=i(/^(?:\w+script|data):/i),G=i(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g),W="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function B(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:q(),n=function(t){return e(t)};if(n.version="2.0.12",n.removed=[],!t||!t.document||9!==t.document.nodeType)return n.isSupported=!1,n;var i=t.document,a=!1,l=t.document,c=t.DocumentFragment,S=t.HTMLTemplateElement,k=t.Node,V=t.NodeFilter,Y=t.NamedNodeMap,X=void 0===Y?t.NamedNodeMap||t.MozNamedAttrMap:Y,$=t.Text,J=t.Comment,Q=t.DOMParser,Z=t.trustedTypes;if("function"==typeof S){var ee=l.createElement("template");ee.content&&ee.content.ownerDocument&&(l=ee.content.ownerDocument)}var te=K(Z,i),ne=te&&He?te.createHTML(""):"",re=l,oe=re.implementation,ie=re.createNodeIterator,ae=re.getElementsByTagName,le=re.createDocumentFragment,ce=i.importNode,se={};n.isSupported=oe&&void 0!==oe.createHTMLDocument&&9!==l.documentMode;var ue=F,de=z,fe=I,pe=U,me=P,ye=G,ge=j,he=null,ve=L({},[].concat(B(E),B(M),B(D),B(R),B(N))),be=null,Te=L({},[].concat(B(O),B(w),B(H),B(C))),Ae=null,xe=null,Se=!0,ke=!0,Le=!1,_e=!1,Ee=!1,Me=!1,De=!1,Re=!1,Ne=!1,Oe=!1,we=!1,He=!1,Ce=!0,Fe=!0,ze=!1,Ie={},Ue=L({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","plaintext","script","style","svg","template","thead","title","video","xmp"]),je=null,Pe=L({},["audio","video","img","source","image","track"]),Ge=null,We=L({},["alt","class","for","id","label","name","pattern","placeholder","summary","title","value","style","xmlns"]),Be=null,qe=l.createElement("form"),Ke=function(e){Be&&Be===e||(e&&"object"===(void 0===e?"undefined":W(e))||(e={}),he="ALLOWED_TAGS"in e?L({},e.ALLOWED_TAGS):ve,be="ALLOWED_ATTR"in e?L({},e.ALLOWED_ATTR):Te,Ge="ADD_URI_SAFE_ATTR"in e?L(_(We),e.ADD_URI_SAFE_ATTR):We,je="ADD_DATA_URI_TAGS"in e?L(_(Pe),e.ADD_DATA_URI_TAGS):Pe,Ae="FORBID_TAGS"in e?L({},e.FORBID_TAGS):{},xe="FORBID_ATTR"in e?L({},e.FORBID_ATTR):{},Ie="USE_PROFILES"in e&&e.USE_PROFILES,Se=!1!==e.ALLOW_ARIA_ATTR,ke=!1!==e.ALLOW_DATA_ATTR,Le=e.ALLOW_UNKNOWN_PROTOCOLS||!1,_e=e.SAFE_FOR_JQUERY||!1,Ee=e.SAFE_FOR_TEMPLATES||!1,Me=e.WHOLE_DOCUMENT||!1,Ne=e.RETURN_DOM||!1,Oe=e.RETURN_DOM_FRAGMENT||!1,we=e.RETURN_DOM_IMPORT||!1,He=e.RETURN_TRUSTED_TYPE||!1,Re=e.FORCE_BODY||!1,Ce=!1!==e.SANITIZE_DOM,Fe=!1!==e.KEEP_CONTENT,ze=e.IN_PLACE||!1,ge=e.ALLOWED_URI_REGEXP||ge,Ee&&(ke=!1),Oe&&(Ne=!0),Ie&&(he=L({},[].concat(B(N))),be=[],!0===Ie.html&&(L(he,E),L(be,O)),!0===Ie.svg&&(L(he,M),L(be,w),L(be,C)),!0===Ie.svgFilters&&(L(he,D),L(be,w),L(be,C)),!0===Ie.mathMl&&(L(he,R),L(be,H),L(be,C))),e.ADD_TAGS&&(he===ve&&(he=_(he)),L(he,e.ADD_TAGS)),e.ADD_ATTR&&(be===Te&&(be=_(be)),L(be,e.ADD_ATTR)),e.ADD_URI_SAFE_ATTR&&L(Ge,e.ADD_URI_SAFE_ATTR),Fe&&(he["#text"]=!0),Me&&L(he,["html","head","body"]),he.table&&(L(he,["tbody"]),delete Ae.tbody),o&&o(e),Be=e)},Ve=function(e){p(n.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){e.outerHTML=ne}},Ye=function(e,t){try{p(n.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(n.removed,{attribute:null,from:t})}t.removeAttribute(e)},Xe=function(e){var t=void 0,n=void 0;if(Re)e=""+e;else{var r=g(e,/^[\r\n\t ]+/);n=r&&r[0]}var o=te?te.createHTML(e):e;try{t=(new Q).parseFromString(o,"text/html")}catch(e){}if(a&&L(Ae,["title"]),!t||!t.documentElement){var i=(t=oe.createHTMLDocument("")).body;i.parentNode.removeChild(i.parentNode.firstElementChild),i.outerHTML=o}return e&&n&&t.body.insertBefore(l.createTextNode(n),t.body.childNodes[0]||null),ae.call(t,Me?"html":"body")[0]};n.isSupported&&function(){try{var e=Xe("</title><img>");T(/<\/title/,e.querySelector("title").innerHTML)&&(a=!0)}catch(e){}}();var $e=function(e){return ie.call(e.ownerDocument||e,e,V.SHOW_ELEMENT|V.SHOW_COMMENT|V.SHOW_TEXT,(function(){return V.FILTER_ACCEPT}),!1)},Je=function(e){return!(e instanceof $||e instanceof J)&&!("string"==typeof e.nodeName&&"string"==typeof e.textContent&&"function"==typeof e.removeChild&&e.attributes instanceof X&&"function"==typeof e.removeAttribute&&"function"==typeof e.setAttribute&&"string"==typeof e.namespaceURI)},Qe=function(e){return"object"===(void 0===k?"undefined":W(k))?e instanceof k:e&&"object"===(void 0===e?"undefined":W(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName},Ze=function(e,t,r){se[e]&&s(se[e],(function(e){e.call(n,t,r,Be)}))},et=function(e){var t=void 0;if(Ze("beforeSanitizeElements",e,null),Je(e))return Ve(e),!0;var r=y(e.nodeName);if(Ze("uponSanitizeElement",e,{tagName:r,allowedTags:he}),("svg"===r||"math"===r)&&0!==e.querySelectorAll("p, br").length)return Ve(e),!0;if(!he[r]||Ae[r]){if(Fe&&!Ue[r]&&"function"==typeof e.insertAdjacentHTML)try{var o=e.innerHTML;e.insertAdjacentHTML("AfterEnd",te?te.createHTML(o):o)}catch(e){}return Ve(e),!0}return"noscript"===r&&T(/<\/noscript/i,e.innerHTML)||"noembed"===r&&T(/<\/noembed/i,e.innerHTML)?(Ve(e),!0):(!_e||e.firstElementChild||e.content&&e.content.firstElementChild||!T(/