port highlight enable toggle [No GBP] (#19375)

* port highlight enable toggle

* .

* .
This commit is contained in:
Kashargul
2026-04-13 02:54:39 -04:00
committed by GitHub
parent 9f782c77bb
commit a5dec7dae0
7 changed files with 98 additions and 42 deletions
+28 -24
View File
@@ -176,6 +176,7 @@ class ChatRenderer {
highlightWholeMessage: boolean;
highlightBlacklist: boolean;
blacklistregex: RegExp;
enabled: boolean;
}[]
| null;
databaseBackendEnabled: boolean;
@@ -288,6 +289,7 @@ class ChatRenderer {
const highlightWholeMessage = setting.highlightWholeMessage;
const matchWord = setting.matchWord;
const matchCase = setting.matchCase;
const enabled = setting.enabled;
const allowedRegex = /^[a-zа-яё0-9_\-$/^[\s\]\\]+$/gi;
const regexEscapeCharacters = /[!#$%^&*)(+=.<>{}[\]:;'"|~`_\-\\/]/g;
// Reset lastIndex so it does not mess up the next word
@@ -397,6 +399,7 @@ class ChatRenderer {
this.highlightParsers = [];
}
this.highlightParsers.push({
enabled,
highlightWords,
highlightRegex,
highlightColor,
@@ -640,31 +643,32 @@ class ChatRenderer {
// Highlight text
if (!message.avoidHighlighting && this.highlightParsers) {
this.highlightParsers.map((parser) => {
const ourUser = node.getElementsByClassName('name');
const isEmote = node.getElementsByClassName('emote');
if (
!(
parser.highlightBlacklist &&
parser.blacklistregex &&
((ourUser.length > 0 &&
parser.blacklistregex.test(ourUser[0].textContent)) ||
(isEmote.length > 0 &&
parser.blacklistregex.test(isEmote[0].textContent)))
)
) {
const highlighted = highlightNode(
node,
parser.highlightRegex,
parser.highlightWords,
(text) => createHighlightNode(text, parser.highlightColor),
);
if (highlighted && parser.highlightWholeMessage) {
node.className += ' ChatMessage--highlighted';
this.highlightParsers
.filter((parser) => parser.enabled)
.forEach((parser) => {
const ourUser = node.getElementsByClassName('name');
const isEmote = node.getElementsByClassName('emote');
if (
!(
parser.highlightBlacklist &&
parser.blacklistregex &&
((ourUser.length > 0 &&
parser.blacklistregex.test(ourUser[0].textContent)) ||
(isEmote.length > 0 &&
parser.blacklistregex.test(isEmote[0].textContent)))
)
) {
const highlighted = highlightNode(
node,
parser.highlightRegex,
parser.highlightWords,
(text) => createHighlightNode(text, parser.highlightColor),
);
if (highlighted && parser.highlightWholeMessage) {
node.className += ' ChatMessage--highlighted';
}
}
}
return undefined;
});
});
}
// Linkify text
const linkifyNodes = node.querySelectorAll('.linkify');
@@ -1,4 +1,5 @@
import { useAtomValue } from 'jotai';
import { useMemo } from 'react';
import {
Box,
Button,
@@ -14,7 +15,7 @@ import { settingsAtom } from '../atoms';
import { MAX_HIGHLIGHT_SETTINGS } from '../constants';
import { useHighlights } from '../use-highlights';
export const TextHighlightSettings = (props) => {
export function TextHighlightSettings(props) {
const {
highlights: { highlightSettings },
addHighlight,
@@ -59,9 +60,25 @@ export const TextHighlightSettings = (props) => {
</Box>
</Section>
);
};
}
const TextHighlightSetting = (props) => {
const oneCharacterRegex = /^(\[.*\]|\\.|.)$/;
function extractRegex(highlight: string): string | null {
if (
highlight.charAt(0) !== '/' ||
highlight.charAt(highlight.length - 1) !== '/'
) {
return null;
}
const expr = highlight.substring(1, highlight.length - 1);
if (oneCharacterRegex.test(expr)) {
return null;
}
return expr;
}
function TextHighlightSetting(props) {
const { id, ...rest } = props;
const {
highlights: { highlightSettingById },
@@ -69,6 +86,7 @@ const TextHighlightSetting = (props) => {
removeHighlight,
} = useHighlights();
const {
enabled,
highlightColor,
highlightText,
blacklistText,
@@ -77,6 +95,22 @@ const TextHighlightSetting = (props) => {
matchWord,
matchCase,
} = highlightSettingById[id];
const highlightRegex = useMemo(
() => extractRegex(highlightText),
[highlightText],
);
const isRegexValid = useMemo(() => {
if (!highlightRegex) return true;
try {
new RegExp(highlightRegex, 'g');
return true;
} catch {
return false;
}
}, [highlightRegex]);
return (
<Stack.Item {...rest}>
<Stack mb={1} color="label" align="baseline">
@@ -104,11 +138,26 @@ const TextHighlightSetting = (props) => {
</Button.Confirm>
</Stack.Item>
)}
<Stack.Item>
<Button.Checkbox
checked={!!enabled}
mr="5px"
onClick={() =>
updateHighlight({
id,
enabled: !enabled,
})
}
>
Enabled
</Button.Checkbox>
</Stack.Item>
<Stack.Item grow />
<Stack.Item>
<Button.Checkbox
checked={highlightBlacklist}
tooltip="If this option is selected, you can blacklist senders not to highlight their messages."
disabled={!!highlightRegex}
mr="5px"
onClick={() =>
updateHighlight({
@@ -185,6 +234,7 @@ const TextHighlightSetting = (props) => {
height="3em"
value={highlightText}
placeholder="Put words to highlight here. Separate terms with commas, i.e. (term1, term2, term3)"
style={{ border: isRegexValid ? '' : '1px solid red' }}
onBlur={(value) =>
updateHighlight({
id,
@@ -208,4 +258,4 @@ const TextHighlightSetting = (props) => {
)}
</Stack.Item>
);
};
}
@@ -46,6 +46,7 @@ export const defaultHighlightSetting: HighlightSetting = {
highlightWholeMessage: true,
matchWord: false,
matchCase: false,
enabled: true,
};
export const defaultHighlights: HighlightState = {
@@ -60,6 +60,17 @@ function migrateHighlights(next: HighlightState): HighlightState {
draft.highlightText ?? defaultHighlightSetting.highlightText;
}
// Ensure that all highlights have the "enabled" var,
// setting it to true if it doesn't exist.
for (const id in draft.highlightSettingById) {
if (
draft.highlightSettingById[id] &&
draft.highlightSettingById[id].enabled === undefined
) {
draft.highlightSettingById[id].enabled = true;
}
}
return draft;
}
@@ -47,6 +47,7 @@ export type HighlightSetting = {
id: string;
matchCase: boolean;
matchWord: boolean;
enabled: boolean;
};
export type HighlightState = {
@@ -40,14 +40,13 @@ export const setGradientSpace = (
space: number,
) => {
let found = false;
gradient?.map((entry) => {
gradient?.forEach((entry) => {
if (typeof entry === 'object') {
if (Object.keys(entry)[0] === 'space') {
entry.space = space;
found = true;
}
}
return undefined;
});
if (!found) {
gradient.push({ space: space });
@@ -56,18 +56,8 @@ export const VoreContentsPanel = (props: {
}, [contents]);
function bellyValueToName(value: string) {
const bellyName = bellyDropdownNames
?.map((entry) => {
if (entry.value === value) {
return entry.displayText;
}
return undefined;
})
.filter((value) => value !== undefined);
if (Array.isArray(bellyName) && bellyName.length) {
return bellyName[0];
}
return '';
const entry = bellyDropdownNames?.find((entry) => entry.value === value);
return entry ? entry.displayText : '';
}
const contentSearch = createSearch<ContentData>(