feat(proxy): unify filter search box and sync filter options (#5819)

* feat(proxy): unify filter search box and sync filter options

* docs: fix JSDoc placement

* refactor(search): unify string matcher and stabilize regex filtering

* fix(search): treat invalid regex as matching nothing

* docs: update changelog to include advanced filter search feature for proxy page

---------

Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
This commit is contained in:
Sline
2025-12-19 17:12:36 +08:00
committed by GitHub
co-authored by Tunglies
parent aa72fa9a42
commit b4e25951b4
7 changed files with 240 additions and 140 deletions
+59
View File
@@ -0,0 +1,59 @@
export type SearchMatcherOptions = {
matchCase?: boolean;
matchWholeWord?: boolean;
useRegularExpression?: boolean;
};
export type CompileStringMatcherResult = {
matcher: (content: string) => boolean;
isValid: boolean;
};
export const escapeRegex = (value: string) => {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
export const buildRegex = (pattern: string, flags = "") => {
try {
return new RegExp(pattern, flags);
} catch {
return null;
}
};
export const compileStringMatcher = (
query: string,
options: SearchMatcherOptions = {},
): CompileStringMatcherResult => {
if (!query) return { matcher: () => true, isValid: true };
const matchCase = options.matchCase ?? false;
const matchWholeWord = options.matchWholeWord ?? false;
const useRegularExpression = options.useRegularExpression ?? false;
const flags = matchCase ? "" : "i";
if (useRegularExpression) {
const regex = buildRegex(query, flags);
if (!regex) return { matcher: () => false, isValid: false };
return { matcher: (content: string) => regex.test(content), isValid: true };
}
if (matchWholeWord) {
const regex = buildRegex(`\\b${escapeRegex(query)}\\b`, flags);
if (!regex) return { matcher: () => false, isValid: false };
return { matcher: (content: string) => regex.test(content), isValid: true };
}
if (matchCase) {
return {
matcher: (content: string) => content.includes(query),
isValid: true,
};
}
const target = query.toLowerCase();
return {
matcher: (content: string) => content.toLowerCase().includes(target),
isValid: true,
};
};