diff --git a/src/plugins/textReplace/index.tsx b/src/plugins/textReplace/index.tsx index 68938e8dc..e0bdfaa5b 100644 --- a/src/plugins/textReplace/index.tsx +++ b/src/plugins/textReplace/index.tsx @@ -35,7 +35,14 @@ import { React, Select, TextInput, UserStore, useState } from "@webpack/common"; const cl = classNameFactory("vc-textReplace-"); -type Rule = Record<"find" | "replace" | "onlyIfIncludes" | "scope" | "id", string>; +interface Rule { + name?: string; + find: string; + replace: string; + onlyIfIncludes: string; + scope: string; + id: string; +} interface TextReplaceProps { title: string; @@ -44,7 +51,13 @@ interface TextReplaceProps { isRegex?: boolean; } +interface RuleWithIndex { + rule: Rule; + index: number; +} + const makeEmptyRule: () => Rule = () => ({ + name: "", find: "", replace: "", onlyIfIncludes: "", @@ -151,7 +164,23 @@ function TextRow({ label, description, value, onChange }: { label: string; descr const isEmptyRule = (rule: Rule) => !rule.find; +function matchesRuleSearch(rule: Rule, query: string) { + if (!query) return true; + + const normalizedQuery = query.trim().toLowerCase(); + return [rule.name ?? "", rule.find, rule.replace, rule.onlyIfIncludes] + .some(value => value.toLowerCase().includes(normalizedQuery)); +} + +function normalizeRule(rule: Rule) { + rule.name ??= ""; + rule.scope ??= "myMessages"; + rule.id ??= crypto.randomUUID(); +} + function TextReplace({ title, description, rulesArray, isRegex = false }: TextReplaceProps) { + const [searchQuery, setSearchQuery] = useState(""); + function onClickRemove(index: number) { rulesArray.splice(index, 1); } @@ -160,7 +189,7 @@ function TextReplace({ title, description, rulesArray, isRegex = false }: TextRe rulesArray[index][key] = e; // If a rule is empty after editing and is not the last rule, remove it - if (rulesArray[index].find === "" && rulesArray[index].replace === "" && rulesArray[index].onlyIfIncludes === "" && index !== rulesArray.length - 1) { + if (rulesArray[index].name === "" && rulesArray[index].find === "" && rulesArray[index].replace === "" && rulesArray[index].onlyIfIncludes === "" && index !== rulesArray.length - 1) { rulesArray.splice(index, 1); } } @@ -171,19 +200,41 @@ function TextReplace({ title, description, rulesArray, isRegex = false }: TextRe { label: "Apply to all messages", value: "allMessages" } ]; + const filteredRules = rulesArray.reduce((acc: RuleWithIndex[], rule, index) => { + if (matchesRuleSearch(rule, searchQuery)) { + acc.push({ rule, index }); + } + + return acc; + }, []); + return ( <>
{title} {description} +
- {rulesArray.map((rule, index) => + {!filteredRules.length && searchQuery && ( + No rules match your search criteria. + )} + {filteredRules.map(({ rule, index }) => ( <>
+ onChange(e, index, "name")} + /> onChange(e, index, "onlyIfIncludes")} /> @@ -223,15 +274,20 @@ function TextReplace({ title, description, rulesArray, isRegex = false }: TextRe )} > - {isEmptyRule(rule) - ? `Empty Rule ${index + 1}` - : `Rule ${index + 1} - ${rule.find}` + {rule.name + ? rule.name + : isEmptyRule(rule) + ? `Empty Rule ${index + 1}` + : `Rule ${index + 1} - ${rule.find}` } )}