mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2025-12-22 04:40:49 +08:00
fix search sorting
This commit is contained in:
parent
39eaa76b8a
commit
dfd953b2ae
63
js/common.js
63
js/common.js
@ -347,6 +347,55 @@ export function md5(inputString) {
|
|||||||
return rh(a)+rh(b)+rh(c)+rh(d);
|
return rh(a)+rh(b)+rh(c)+rh(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const levenArray = [];
|
||||||
|
const levenCodeCache = [];
|
||||||
|
export const leven = (first, second) => {
|
||||||
|
if (first === second) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const swap = first;
|
||||||
|
if (first.length > second.length) {
|
||||||
|
first = second;
|
||||||
|
second = swap;
|
||||||
|
}
|
||||||
|
let firstLength = first.length;
|
||||||
|
let secondLength = second.length;
|
||||||
|
while (firstLength > 0 && (first.charCodeAt(~-firstLength) === second.charCodeAt(~-secondLength))) {
|
||||||
|
firstLength--;
|
||||||
|
secondLength--;
|
||||||
|
}
|
||||||
|
let start = 0;
|
||||||
|
while (start < firstLength && (first.charCodeAt(start) === second.charCodeAt(start))) {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
firstLength -= start;
|
||||||
|
secondLength -= start;
|
||||||
|
if (firstLength === 0) {
|
||||||
|
return secondLength;
|
||||||
|
}
|
||||||
|
let bCharacterCode;
|
||||||
|
let result;
|
||||||
|
let temporary;
|
||||||
|
let temporary2;
|
||||||
|
let index = 0;
|
||||||
|
let index2 = 0;
|
||||||
|
while (index < firstLength) {
|
||||||
|
levenCodeCache[index] = first.charCodeAt(start + index);
|
||||||
|
levenArray[index] = ++index;
|
||||||
|
}
|
||||||
|
while (index2 < secondLength) {
|
||||||
|
bCharacterCode = second.charCodeAt(start + index2);
|
||||||
|
temporary = index2++;
|
||||||
|
result = index2;
|
||||||
|
for (index = 0; index < firstLength; index++) {
|
||||||
|
temporary2 = bCharacterCode === levenCodeCache[index] ? temporary : temporary + 1;
|
||||||
|
temporary = levenArray[index];
|
||||||
|
result = levenArray[index] = temporary > result ? (temporary2 > result ? result + 1 : temporary2) : (temporary2 > temporary ? temporary + 1 : temporary2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchData(route, options) {
|
export async function fetchData(route, options) {
|
||||||
let err;
|
let err;
|
||||||
const res = await api.fetchApi(route, options).catch(e => {
|
const res = await api.fetchApi(route, options).catch(e => {
|
||||||
@ -595,12 +644,10 @@ export function showPopover(target, text, className, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let $tooltip;
|
let $tooltip;
|
||||||
export function hideTooltip(target) {
|
export function hideTooltip() {
|
||||||
if ($tooltip) {
|
if ($tooltip) {
|
||||||
$tooltip.style.display = "none";
|
$tooltip.remove();
|
||||||
$tooltip.innerHTML = "";
|
$tooltip = null;
|
||||||
$tooltip.style.top = "0px";
|
|
||||||
$tooltip.style.left = "0px";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function showTooltip(target, text, className = 'cn-tooltip', styleMap = {}) {
|
export function showTooltip(target, text, className = 'cn-tooltip', styleMap = {}) {
|
||||||
@ -639,11 +686,7 @@ function initTooltip () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const mouseleaveHandler = (e) => {
|
const mouseleaveHandler = (e) => {
|
||||||
const target = e.target;
|
hideTooltip();
|
||||||
const text = target.getAttribute('tooltip');
|
|
||||||
if (text) {
|
|
||||||
hideTooltip(target);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
document.body.removeEventListener('mouseenter', mouseenterHandler, true);
|
document.body.removeEventListener('mouseenter', mouseenterHandler, true);
|
||||||
document.body.removeEventListener('mouseleave', mouseleaveHandler, true);
|
document.body.removeEventListener('mouseleave', mouseleaveHandler, true);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
||||||
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
||||||
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
|
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
|
||||||
showPopover, hidePopover
|
showPopover, hidePopover, leven
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
|
|
||||||
// https://cenfun.github.io/turbogrid/api.html
|
// https://cenfun.github.io/turbogrid/api.html
|
||||||
@ -418,11 +418,7 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
".cn-manager-keywords": {
|
".cn-manager-keywords": {
|
||||||
input: (e) => {
|
input: (e) => {
|
||||||
const keywords = `${e.target.value}`.trim();
|
this.onKeywordsChange(e);
|
||||||
if (keywords !== this.keywords) {
|
|
||||||
this.keywords = keywords;
|
|
||||||
this.updateGrid();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
focus: (e) => e.target.select()
|
focus: (e) => e.target.select()
|
||||||
},
|
},
|
||||||
@ -539,7 +535,7 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
this.addHighlight(d.rowItem);
|
this.addHighlight(d.rowItem);
|
||||||
|
|
||||||
if (d.columnItem.id === "nodes") {
|
if (d.columnItem && d.columnItem.id === "nodes") {
|
||||||
this.showNodes(d);
|
this.showNodes(d);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -596,6 +592,14 @@ export class CustomNodesManager {
|
|||||||
return autoHeightColumns.includes(columnItem.id)
|
return autoHeightColumns.includes(columnItem.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
rowFilteredSort: () => {
|
||||||
|
if (this.keywords) {
|
||||||
|
return {
|
||||||
|
id: 'sort_score'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// updateGrid handler for filter and keywords
|
// updateGrid handler for filter and keywords
|
||||||
rowFilter: (rowItem) => {
|
rowFilter: (rowItem) => {
|
||||||
|
|
||||||
@ -612,12 +616,109 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calculate sort score
|
||||||
|
if (shouldShown && this.keywords) {
|
||||||
|
rowItem.sort_score = this.calculateSortScore(rowItem, searchableColumns);
|
||||||
|
}
|
||||||
|
|
||||||
return shouldShown;
|
return shouldShown;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onKeywordsChange(e) {
|
||||||
|
this.grid.showLoading();
|
||||||
|
// debounce for performance
|
||||||
|
clearTimeout(this.timeKeywords);
|
||||||
|
this.timeKeywords = setTimeout(() => {
|
||||||
|
this.grid.hideLoading();
|
||||||
|
const keywords = `${e.target.value}`.trim();
|
||||||
|
if (keywords !== this.keywords) {
|
||||||
|
this.keywords = keywords;
|
||||||
|
if (keywords) {
|
||||||
|
this.grid.removeSortColumn();
|
||||||
|
} else {
|
||||||
|
this.grid.sortRows("id", {
|
||||||
|
sortAsc: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.updateGrid();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateSortScore(rowItem, searchableColumns) {
|
||||||
|
const keywords = this.keywords.split(/\s+/g).filter((s) => s);
|
||||||
|
const lowerKeywords = keywords.map(k => k.toLowerCase());
|
||||||
|
const matchedList = searchableColumns.map(id => {
|
||||||
|
const { highlightKey, textKey } = this.grid.options.highlightKeywords;
|
||||||
|
const highlight = rowItem[`${highlightKey}${id}`];
|
||||||
|
if (!highlight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const text = `${rowItem[`${textKey}${id}`] || rowItem[id]}`;
|
||||||
|
const lowerText = text.toLowerCase();
|
||||||
|
const matchedItems = keywords.map((key, i) => {
|
||||||
|
// multiple matched
|
||||||
|
const lowerKey = lowerKeywords[i];
|
||||||
|
const len = lowerKey.length;
|
||||||
|
const matches = [];
|
||||||
|
let index = lowerText.indexOf(lowerKey);
|
||||||
|
while (index !== -1) {
|
||||||
|
matches.push(index);
|
||||||
|
index = lowerText.indexOf(lowerKey, index + len);
|
||||||
|
}
|
||||||
|
if (!matches.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const distances = matches.map(start => {
|
||||||
|
const end = start + len
|
||||||
|
const str = text.slice(start, end);
|
||||||
|
let distance = leven(key, str);
|
||||||
|
const prev = text.slice(start - 1, start);
|
||||||
|
if (prev) {
|
||||||
|
if (/[A-Za-z]/.test(prev)) {
|
||||||
|
distance += 1;
|
||||||
|
} else if (/[0-9]/.test(prev)) {
|
||||||
|
distance += 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const next = text.slice(end, end + 1);
|
||||||
|
if (next) {
|
||||||
|
if (/[A-Za-z]/.test(next)) {
|
||||||
|
distance += 0.8;
|
||||||
|
} else if (/[0-9]/.test(next)) {
|
||||||
|
distance += 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return distance;
|
||||||
|
});
|
||||||
|
// console.log(rowItem.title, distances)
|
||||||
|
return {
|
||||||
|
// min
|
||||||
|
distance: Math.min.apply(null, distances)
|
||||||
|
}
|
||||||
|
}).filter(it => it);
|
||||||
|
if (matchedItems.length < keywords.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
// avg
|
||||||
|
distance: matchedItems.map(it => it.distance).reduce((p, v) => p + v, 0) / matchedItems.length
|
||||||
|
}
|
||||||
|
}).filter(it => it);
|
||||||
|
// by distance
|
||||||
|
let distance = Math.min.apply(null, matchedList.map(it => it.distance));
|
||||||
|
// by matched count
|
||||||
|
distance += 1 - matchedList.length / searchableColumns.length;
|
||||||
|
// by stars
|
||||||
|
const stars = TG.Util.toNum(rowItem.stars);
|
||||||
|
distance += 1 - stars / 10000;
|
||||||
|
// score
|
||||||
|
return 1 / distance;
|
||||||
|
}
|
||||||
|
|
||||||
hasAlternatives() {
|
hasAlternatives() {
|
||||||
return this.filter === ShowMode.ALTERNATIVES
|
return this.filter === ShowMode.ALTERNATIVES
|
||||||
}
|
}
|
||||||
@ -756,6 +857,7 @@ export class CustomNodesManager {
|
|||||||
id: "nodes",
|
id: "nodes",
|
||||||
name: "Nodes",
|
name: "Nodes",
|
||||||
width: 100,
|
width: 100,
|
||||||
|
sortAsc: false,
|
||||||
formatter: (v, rowItem, columnItem) => {
|
formatter: (v, rowItem, columnItem) => {
|
||||||
if (!rowItem.nodes) {
|
if (!rowItem.nodes) {
|
||||||
return '';
|
return '';
|
||||||
@ -796,6 +898,7 @@ export class CustomNodesManager {
|
|||||||
id: 'stars',
|
id: 'stars',
|
||||||
name: '★',
|
name: '★',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
sortAsc: false,
|
||||||
classMap: "cn-pack-stars",
|
classMap: "cn-pack-stars",
|
||||||
formatter: (stars) => {
|
formatter: (stars) => {
|
||||||
if (stars < 0) {
|
if (stars < 0) {
|
||||||
@ -811,6 +914,7 @@ export class CustomNodesManager {
|
|||||||
name: 'Last Update',
|
name: 'Last Update',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
type: 'date',
|
type: 'date',
|
||||||
|
sortAsc: false,
|
||||||
width: 100,
|
width: 100,
|
||||||
classMap: "cn-pack-last-update",
|
classMap: "cn-pack-last-update",
|
||||||
formatter: (last_update) => {
|
formatter: (last_update) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user