mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat(dashboard): fuzzy settings search, several searches at once
Words in a search match in any order, each one as part of a word, as its letters in order (dbnc finds debounce) or with one typo or swapped pair. Commas or | separate searches: "rent, ugc icon" shows everything matching either. Underscores and dots count as spaces, so "icon border" finds ugc_icon_border. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -94,7 +94,57 @@
|
||||
if (state.filter === 'changed' && !isChanged(s) && !isPending(s)) return false;
|
||||
if (state.filter === 'restart' && !s.restart) return false;
|
||||
if (!state.query) return true;
|
||||
return (s.name + ' ' + s.title + ' ' + s.description + ' ' + s.section + ' ' + s.file).toLowerCase().indexOf(state.query) !== -1;
|
||||
return queryMatches(s.name + ' ' + s.title + ' ' + s.description + ' ' + s.section + ' ' + s.file);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- fuzzy search
|
||||
// "rent, ugc icon": commas (or |) separate searches, any of which may match; within one search every word must
|
||||
// match somewhere, in any order. A word matches a word of the setting when it is part of it, when its letters
|
||||
// appear in it in order (e.g. "dbnc" -> "debounce"), or with one typo (words of 4+ letters).
|
||||
|
||||
function searchGroups(query) {
|
||||
return query.split(/[,|]/).map(function (group) {
|
||||
return group.split(/[\s_.\-/]+/).filter(Boolean);
|
||||
}).filter(function (words) { return words.length; });
|
||||
}
|
||||
|
||||
function isSubsequence(needle, word) {
|
||||
var i = 0;
|
||||
for (var j = 0; j < word.length && i < needle.length; j++) if (word[j] === needle[i]) i++;
|
||||
return i === needle.length;
|
||||
}
|
||||
|
||||
// At most one insertion, deletion or substitution between needle and the start of word (so "unsed" finds "unused").
|
||||
function withinOneEdit(needle, word) {
|
||||
var candidates = [word.slice(0, needle.length - 1), word.slice(0, needle.length), word.slice(0, needle.length + 1)];
|
||||
return candidates.some(function (c) {
|
||||
var a = needle, b = c, edits = 0, i = 0, j = 0;
|
||||
while (i < a.length && j < b.length) {
|
||||
if (a[i] === b[j]) { i++; j++; continue; }
|
||||
if (++edits > 1) return false;
|
||||
if (a[i] === b[j + 1] && a[i + 1] === b[j]) { i += 2; j += 2; } // two letters swapped
|
||||
else if (a.length > b.length) i++;
|
||||
else if (b.length > a.length) j++;
|
||||
else { i++; j++; }
|
||||
}
|
||||
return edits + (a.length - i) + (b.length - j) <= 1;
|
||||
});
|
||||
}
|
||||
|
||||
function wordMatches(needle, text, words) {
|
||||
if (text.indexOf(needle) !== -1) return true;
|
||||
return words.some(function (w) {
|
||||
if (needle.length >= 3 && needle[0] === w[0] && isSubsequence(needle, w)) return true;
|
||||
return needle.length >= 4 && withinOneEdit(needle, w);
|
||||
});
|
||||
}
|
||||
|
||||
function queryMatches(haystack) {
|
||||
var text = haystack.toLowerCase().replace(/[_.\-/]+/g, ' ');
|
||||
var words = text.split(/\s+/).filter(Boolean);
|
||||
return searchGroups(state.query).some(function (group) {
|
||||
return group.every(function (needle) { return wordMatches(needle, text, words); });
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- controls, one per setting type
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<div class="settings-toolbar d-flex flex-wrap align-items-center gap-2 mb-3">
|
||||
<input type="search" class="form-control form-control-sm" id="settingsSearch" placeholder="Search every setting by name or description" aria-label="Search settings">
|
||||
<input type="search" class="form-control form-control-sm" id="settingsSearch" placeholder="Search settings: words in any order, commas for several (rent, ugc icon)" aria-label="Search settings">
|
||||
<select class="form-select form-select-sm w-auto" id="settingsFilter" aria-label="Show">
|
||||
<option value="all">All settings</option>
|
||||
<option value="changed">Changed from default</option>
|
||||
|
||||
Reference in New Issue
Block a user