support fuzzy search
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
|
||||
转成base64的节点都是输出节点,websocket消息中会包含base64Images和base64Type属性(具体格式请查看ImageNode.py中的ImageToBase64Advanced类源代码,或者自己搭建简单流程运行在浏览器开发者工具-->网络中查看)
|
||||
## 安装
|
||||
- 通过ComfyUI-Manager安装
|
||||
- 在ComfyUI安装目录根目录下打开命令行终端,执行以下命令
|
||||
- 方式1:通过ComfyUI-Manager安装
|
||||
- 方式2:在ComfyUI安装目录根目录下打开命令行终端,执行以下命令
|
||||
```sh
|
||||
cd custom_nodes
|
||||
git clone https://github.com/lldacing/comfyui-easyapi-nodes.git
|
||||
@@ -38,13 +38,16 @@
|
||||
| IntToList | 整型转列表 |
|
||||
| ListMerge | 列表合并 |
|
||||
| JoinList | 列表根据指定分隔符连接 |
|
||||
| ShowString | 显示字符串 |
|
||||
| ShowInt | 显示整型 |
|
||||
| ShowFloat | 显示浮点型 |
|
||||
| ShowNumber | 显示数字 |
|
||||
| ShowString | 显示字符串(可指定消息中key值) |
|
||||
| ShowInt | 显示整型(可指定消息中key值) |
|
||||
| ShowFloat | 显示浮点型(可指定消息中key值) |
|
||||
| ShowNumber | 显示数字(可指定消息中key值) |
|
||||
|
||||
Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带宽可能也会有瓶颈,条件允许可以把图片上传到OSS服务器得到URL,然后用LoadImageFromUrl加载,由于无相关OSS账号,上传OSS节点需自行编写,暂不支持。
|
||||
|
||||
### [示例](example/example.png)
|
||||

|
||||
|
||||
## 功能
|
||||
- 扩展Save(Api Format)菜单。
|
||||
- 复制工作流
|
||||
@@ -68,7 +71,12 @@ Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带
|
||||
|
||||
- 是否自动展开当前菜单下的子菜单
|
||||
配置路径:Settings -> [EasyApi] Auto Open Sub Menu
|
||||
|
||||

|
||||
- 模糊搜索
|
||||
配置路径:Settings -> [EasyApi] Fuzzy Search
|
||||

|
||||
|
||||
|
||||
- 菜单扩展
|
||||
- 重设某个节点的id(Node Context Menu)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 384 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 18 KiB |
+286
-11
@@ -41,17 +41,292 @@ app.registerExtension({
|
||||
LiteGraph.ContextMenu.prototype = ctxMenu.prototype;
|
||||
};
|
||||
app.ui.settings.addSetting({
|
||||
id: "Easyapi.ContextMenu.autoopen",
|
||||
name: "[EasyApi] Auto Open Sub Menu",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
onChange(value) {
|
||||
if (value) {
|
||||
replace(value);
|
||||
} else {
|
||||
LiteGraph.ContextMenu = ctxMenu;
|
||||
id: "Easyapi.ContextMenu.autoopen",
|
||||
name: "[EasyApi] Auto Open Sub Menu",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
onChange(value) {
|
||||
if (value) {
|
||||
replace(value);
|
||||
} else {
|
||||
LiteGraph.ContextMenu = ctxMenu;
|
||||
}
|
||||
},
|
||||
});
|
||||
app.ui.settings.addSetting({
|
||||
id: "Easyapi.Search.fuzzy",
|
||||
name: "[EasyApi] Fuzzy Search",
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
onChange(value) {
|
||||
LiteGraph.search_fuzzy_match = value;
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.EasyApi.Search",
|
||||
async init(app) {
|
||||
function fuzzy_match_filter(inputStr, str, matchedPos) {
|
||||
matchedPos = matchedPos || [];
|
||||
let splitStr = inputStr.split("");
|
||||
let j = splitStr.shift()
|
||||
for (let i in str) {
|
||||
if (str[i] == j) {
|
||||
matchedPos.push(i)
|
||||
if (splitStr.length == 0) {
|
||||
return true;
|
||||
}
|
||||
j = splitStr.shift()
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function highlight_keywords(oldText, recordPos) {
|
||||
let newText = "";
|
||||
let pos = recordPos.shift();
|
||||
let split = oldText.split("")
|
||||
for (let j in split) {
|
||||
let c = oldText[j];
|
||||
if (j == pos) {
|
||||
newText += "<span style='color: red'>" + c + "</span>";
|
||||
if (recordPos.length > 0) {
|
||||
pos = recordPos.shift();
|
||||
} else {
|
||||
newText += oldText.slice(parseInt(j) + 1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
newText += c;
|
||||
}
|
||||
}
|
||||
return newText;
|
||||
}
|
||||
|
||||
let oldOnSearchBox = app.canvas.onSearchBox
|
||||
let oldShowSearchBox = LGraphCanvas.prototype.showSearchBox
|
||||
LGraphCanvas.prototype.showSearchBox = function(event, options) {
|
||||
let that = this;
|
||||
// copy from oldShowSearBox
|
||||
let def_options = { slot_from: null
|
||||
,node_from: null
|
||||
,node_to: null
|
||||
,do_type_filter: LiteGraph.search_filter_enabled
|
||||
,type_filter_in: false // these are default: pass to set initially set values
|
||||
,type_filter_out: false
|
||||
,show_general_if_none_on_typefilter: true
|
||||
,show_general_after_typefiltered: true
|
||||
,hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave
|
||||
,show_all_if_empty: true
|
||||
,show_all_on_open: LiteGraph.search_show_all_on_open
|
||||
,search_fuzzy_match: LiteGraph.search_fuzzy_match || false // easyapi custom setting
|
||||
};
|
||||
options = Object.assign(def_options, options || {});
|
||||
if (!options.search_fuzzy_match) {
|
||||
app.canvas.onSearchBox = oldOnSearchBox;
|
||||
return oldShowSearchBox?.apply(this, [event, options]);
|
||||
}
|
||||
let dialog = oldShowSearchBox?.apply(this, [event, options])
|
||||
|
||||
app.canvas.onSearchBox = function (helper, inputStr, graphcanvas) {
|
||||
// let options = {}
|
||||
var c = 0;
|
||||
let str = inputStr.toLowerCase();
|
||||
var filter = graphcanvas.filter || graphcanvas.graph.filter;
|
||||
|
||||
// filter by type preprocess
|
||||
if (options.do_type_filter && that.search_box) {
|
||||
var sIn = that.search_box.querySelector(".slot_in_type_filter");
|
||||
var sOut = that.search_box.querySelector(".slot_out_type_filter");
|
||||
} else {
|
||||
var sIn = false;
|
||||
var sOut = false;
|
||||
}
|
||||
|
||||
let filtered = [];
|
||||
|
||||
//extras
|
||||
for (var i in LiteGraph.searchbox_extras) {
|
||||
var extra = LiteGraph.searchbox_extras[i];
|
||||
if ((!options.show_all_if_empty || str) && !fuzzy_match_filter(str, extra.desc)) {
|
||||
continue;
|
||||
}
|
||||
var ctor = LiteGraph.registered_node_types[ extra.type ];
|
||||
if( ctor && ctor.filter != filter )
|
||||
continue;
|
||||
if( ! inner_test_filter(extra.type) )
|
||||
continue;
|
||||
// addResult( extra.desc, "searchbox_extra" );
|
||||
filtered.push(extra.desc)
|
||||
if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let temp_filtered = []
|
||||
if (Array.prototype.filter) { //filter supported
|
||||
var keys = Object.keys( LiteGraph.registered_node_types ); //types
|
||||
temp_filtered = keys.filter( inner_test_filter );
|
||||
} else {
|
||||
temp_filtered = [];
|
||||
for (var i in LiteGraph.registered_node_types) {
|
||||
if( inner_test_filter(i) )
|
||||
temp_filtered.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < temp_filtered.length; i++) {
|
||||
// addResult(filtered[i]);
|
||||
filtered.push(temp_filtered[i]);
|
||||
if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add general type if filtering
|
||||
if (options.show_general_after_typefiltered
|
||||
&& (sIn.value || sOut.value)
|
||||
){
|
||||
let filtered_extra = [];
|
||||
for (var i in LiteGraph.registered_node_types) {
|
||||
if( inner_test_filter(i, {inTypeOverride: sIn&&sIn.value?"*":false, outTypeOverride: sOut&&sOut.value?"*":false}) )
|
||||
filtered_extra.push(i);
|
||||
}
|
||||
for (var i = 0; i < filtered_extra.length; i++) {
|
||||
// addResult(filtered_extra[i], "generic_type");
|
||||
filtered.push(filtered_extra[i]);
|
||||
if (LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// check il filtering gave no results
|
||||
if ((sIn.value || sOut.value) &&
|
||||
( (helper.childNodes.length == 0 && options.show_general_if_none_on_typefilter) )
|
||||
){
|
||||
var filtered_extra = [];
|
||||
for (var i in LiteGraph.registered_node_types) {
|
||||
if( inner_test_filter(i, {skipFilter: true}) )
|
||||
filtered_extra.push(i);
|
||||
}
|
||||
for (var i = 0; i < filtered_extra.length; i++) {
|
||||
// addResult(filtered_extra[i], "not_in_filter");
|
||||
filtered.push(filtered_extra[i]);
|
||||
if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inner_test_filter( type, optsIn )
|
||||
{
|
||||
var optsIn = optsIn || {};
|
||||
var optsDef = { skipFilter: false
|
||||
,inTypeOverride: false
|
||||
,outTypeOverride: false
|
||||
};
|
||||
var opts = Object.assign(optsDef,optsIn);
|
||||
var ctor = LiteGraph.registered_node_types[ type ];
|
||||
if(filter && ctor.filter != filter )
|
||||
return false;
|
||||
if ((!options.show_all_if_empty || str) && !fuzzy_match_filter(str, type.toLowerCase()) && (!ctor.title || !fuzzy_match_filter(str, ctor.title.toLowerCase())))
|
||||
return false;
|
||||
|
||||
// filter by slot IN, OUT types
|
||||
if(options.do_type_filter && !opts.skipFilter){
|
||||
var sType = type;
|
||||
|
||||
var sV = sIn.value;
|
||||
if (opts.inTypeOverride!==false) sV = opts.inTypeOverride;
|
||||
//if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1
|
||||
|
||||
if(sIn && sV){
|
||||
//console.log("will check filter against "+sV);
|
||||
if (LiteGraph.registered_slot_in_types[sV] && LiteGraph.registered_slot_in_types[sV].nodes){ // type is stored
|
||||
//console.debug("check "+sType+" in "+LiteGraph.registered_slot_in_types[sV].nodes);
|
||||
var doesInc = LiteGraph.registered_slot_in_types[sV].nodes.includes(sType);
|
||||
if (doesInc!==false){
|
||||
//console.log(sType+" HAS "+sV);
|
||||
}else{
|
||||
/*console.debug(LiteGraph.registered_slot_in_types[sV]);
|
||||
console.log(+" DONT includes "+type);*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sV = sOut.value;
|
||||
if (opts.outTypeOverride!==false) sV = opts.outTypeOverride;
|
||||
//if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1
|
||||
|
||||
if(sOut && sV){
|
||||
//console.log("search will check filter against "+sV);
|
||||
if (LiteGraph.registered_slot_out_types[sV] && LiteGraph.registered_slot_out_types[sV].nodes){ // type is stored
|
||||
//console.debug("check "+sType+" in "+LiteGraph.registered_slot_out_types[sV].nodes);
|
||||
var doesInc = LiteGraph.registered_slot_out_types[sV].nodes.includes(sType);
|
||||
if (doesInc!==false){
|
||||
//console.log(sType+" HAS "+sV);
|
||||
}else{
|
||||
/*console.debug(LiteGraph.registered_slot_out_types[sV]);
|
||||
console.log(+" DONT includes "+type);*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (helper.dataset["observer"] != "true") {
|
||||
helper.dataset["observer"] = "true";
|
||||
let observConfig = { attributes: false, childList: true };
|
||||
let observer = new MutationObserver(mutations => {
|
||||
for (let i = 0; i < mutations.length; i++) {
|
||||
let mutationRecord = mutations[i];
|
||||
let searchWord = helper.parentNode.querySelector("input").value;
|
||||
if (searchWord.trim().length == 0) {
|
||||
break;
|
||||
}
|
||||
for (let item of mutationRecord.addedNodes) {
|
||||
if (!item.dataset["fuzzy"] && decodeURIComponent(item.dataset["type"]) in LiteGraph.registered_node_types) {
|
||||
item.dataset["fuzzy"] = "true";
|
||||
let type = decodeURIComponent(item.dataset["type"])
|
||||
let newDom = document.createElement("span");
|
||||
let recordPos = []
|
||||
if (fuzzy_match_filter(searchWord, type.toLowerCase(), recordPos)) {
|
||||
let oldDom = item.firstChild.nextSibling;
|
||||
let oldText = oldDom.textContent;
|
||||
let newText = highlight_keywords(oldText, recordPos);
|
||||
oldDom.innerHTML = newText;
|
||||
} else {
|
||||
recordPos = []
|
||||
let node = LiteGraph.registered_node_types[type];
|
||||
if (node.title && fuzzy_match_filter(searchWord, node.title.toLowerCase(), recordPos)) {
|
||||
let oldDom = item.firstChild;
|
||||
let oldText = oldDom.textContent;
|
||||
let newText = highlight_keywords(oldText, recordPos);
|
||||
newDom.innerHTML = newText;
|
||||
item.replaceChild(newDom, oldDom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(helper, observConfig);
|
||||
let oldClose = helper.parentNode.close;
|
||||
helper.parentNode.close = function () {
|
||||
observer.disconnect();
|
||||
oldClose?.apply(this, arguments)
|
||||
};
|
||||
}
|
||||
|
||||
return filtered;
|
||||
};
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -525,7 +525,7 @@ class EasyApiWorkflows {
|
||||
options.splice(options.length - 1, 0, null);
|
||||
}
|
||||
let hasInputLink = node.inputs?.filter(input => input?.link).length > 0;
|
||||
let hasOutputLink = node.outputs?.filter(output => output?.links).length > 0;
|
||||
let hasOutputLink = node.outputs?.filter(output => output?.links?.length > 0).length > 0;
|
||||
options.splice(options.length - 1, 0,
|
||||
{
|
||||
content: "Set Node Id (EasyApi)",
|
||||
@@ -788,6 +788,9 @@ class EasyApiWorkflows {
|
||||
app.nodePreviewImages[newNodeId] = app.nodePreviewImages[oldNodeId]
|
||||
delete app.nodePreviewImages[oldNodeId]
|
||||
}
|
||||
if (newNodeId > app.graph.last_node_id) {
|
||||
app.graph.last_node_id = newNodeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user