mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 06:03:45 +08:00
feat: add logger highlighting, support regular and case matching
This commit is contained in:
parent
2a7b22c96f
commit
95123aceb5
@ -7,20 +7,19 @@ import matchCaseIcon from "@/assets/image/component/match_case.svg?react";
|
||||
import matchWholeWordIcon from "@/assets/image/component/match_whole_word.svg?react";
|
||||
import useRegularExpressionIcon from "@/assets/image/component/use_regular_expression.svg?react";
|
||||
|
||||
export type SearchState = {
|
||||
text: string;
|
||||
matchCase: boolean;
|
||||
matchWholeWord: boolean;
|
||||
useRegularExpression: boolean;
|
||||
};
|
||||
|
||||
type SearchProps = {
|
||||
placeholder?: string;
|
||||
matchCase?: boolean;
|
||||
matchWholeWord?: boolean;
|
||||
useRegularExpression?: boolean;
|
||||
onSearch: (
|
||||
match: (content: string) => boolean,
|
||||
state: {
|
||||
text: string;
|
||||
matchCase: boolean;
|
||||
matchWholeWord: boolean;
|
||||
useRegularExpression: boolean;
|
||||
}
|
||||
) => void;
|
||||
onSearch: (match: (content: string) => boolean, state: SearchState) => void;
|
||||
};
|
||||
|
||||
export const BaseSearchBox = styled((props: SearchProps) => {
|
||||
@ -28,10 +27,10 @@ export const BaseSearchBox = styled((props: SearchProps) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [matchCase, setMatchCase] = useState(props.matchCase ?? false);
|
||||
const [matchWholeWord, setMatchWholeWord] = useState(
|
||||
props.matchWholeWord ?? false
|
||||
props.matchWholeWord ?? false,
|
||||
);
|
||||
const [useRegularExpression, setUseRegularExpression] = useState(
|
||||
props.useRegularExpression ?? false
|
||||
props.useRegularExpression ?? false,
|
||||
);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
@ -60,7 +59,7 @@ export const BaseSearchBox = styled((props: SearchProps) => {
|
||||
matchCase,
|
||||
matchWholeWord,
|
||||
useRegularExpression,
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { styled, Box } from "@mui/material";
|
||||
import { SearchState } from "@/components/base/base-search-box";
|
||||
|
||||
const Item = styled(Box)(({ theme: { palette, typography } }) => ({
|
||||
padding: "8px 0",
|
||||
@ -41,24 +42,41 @@ const Item = styled(Box)(({ theme: { palette, typography } }) => ({
|
||||
|
||||
interface Props {
|
||||
value: ILogItem;
|
||||
searchText?: string;
|
||||
searchState?: SearchState;
|
||||
}
|
||||
|
||||
const LogItem = ({ value, searchText }: Props) => {
|
||||
const LogItem = ({ value, searchState }: Props) => {
|
||||
const renderHighlightText = (text: string) => {
|
||||
if (!searchText?.trim()) return text;
|
||||
if (!searchState?.text.trim()) return text;
|
||||
|
||||
try {
|
||||
const parts = text.split(new RegExp(`(${searchText})`, "gi"));
|
||||
return parts.map((part, index) =>
|
||||
part.toLowerCase() === searchText.toLowerCase() ? (
|
||||
const searchText = searchState.text;
|
||||
let pattern: string;
|
||||
|
||||
if (searchState.useRegularExpression) {
|
||||
try {
|
||||
new RegExp(searchText);
|
||||
pattern = searchText;
|
||||
} catch {
|
||||
pattern = searchText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
} else {
|
||||
const escaped = searchText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
pattern = searchState.matchWholeWord ? `\\b${escaped}\\b` : escaped;
|
||||
}
|
||||
|
||||
const flags = searchState.matchCase ? "g" : "gi";
|
||||
const parts = text.split(new RegExp(`(${pattern})`, flags));
|
||||
|
||||
return parts.map((part, index) => {
|
||||
return index % 2 === 1 ? (
|
||||
<span key={index} className="highlight">
|
||||
{part}
|
||||
</span>
|
||||
) : (
|
||||
part
|
||||
),
|
||||
);
|
||||
);
|
||||
});
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import LogItem from "@/components/log/log-item";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { BaseSearchBox } from "@/components/base/base-search-box";
|
||||
import { BaseStyledSelect } from "@/components/base/base-styled-select";
|
||||
import { SearchState } from "@/components/base/base-search-box";
|
||||
|
||||
const LogPage = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -27,7 +28,7 @@ const LogPage = () => {
|
||||
);
|
||||
const [match, setMatch] = useState(() => (_: string) => true);
|
||||
const logData = useLogData(logLevel);
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [searchState, setSearchState] = useState<SearchState>();
|
||||
|
||||
const filterLogs = useMemo(() => {
|
||||
return logData
|
||||
@ -96,7 +97,7 @@ const LogPage = () => {
|
||||
<BaseSearchBox
|
||||
onSearch={(matcher, state) => {
|
||||
setMatch(() => matcher);
|
||||
setSearchText(state.text);
|
||||
setSearchState(state);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@ -114,7 +115,7 @@ const LogPage = () => {
|
||||
initialTopMostItemIndex={999}
|
||||
data={filterLogs}
|
||||
itemContent={(index, item) => (
|
||||
<LogItem value={item} searchText={searchText} />
|
||||
<LogItem value={item} searchState={searchState} />
|
||||
)}
|
||||
followOutput={"smooth"}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user