diff --git a/webapp/src/components/Alerts.tsx b/webapp/src/components/Alerts.tsx index 260bc45..c77ab0c 100644 --- a/webapp/src/components/Alerts.tsx +++ b/webapp/src/components/Alerts.tsx @@ -17,6 +17,7 @@ import { useSectionCollapsed, } from './CollapsibleSection' import { HelpIcon } from './HelpIcon' +import { RegexHelpIcon } from './RegexHelpIcon' import { queryInputClassName } from './FilterBar' type ConditionKind = AlertCondition['kind'] @@ -341,7 +342,10 @@ export function Alerts() { {form.kind === 'newvalue' && (
- +
+ {collapseToggle}
- +
- + ...)', meaning: 'Named capturing group' }, + { tokens: '(?:...)', meaning: 'Non-capturing group' }, + { tokens: '(?i) (?m) (?s)', meaning: 'Inline flags: case-insensitive, multi-line, dot-matches-newline' }, +] + +// "@" icon next to regex inputs; shows a compact cheat sheet for the regex flavor the server actually +// uses (Rust `regex` crate — no lookaround/backreferences), modeled on HelpIcon's button styling. +export function RegexHelpIcon({ label }: { label?: string }) { + const [open, setOpen] = useState(false) + const ref = useRef(null) + + useEffect(() => { + if (!open) return + function onClickOutside(e: MouseEvent) { + if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false) + } + document.addEventListener('mousedown', onClickOutside) + return () => document.removeEventListener('mousedown', onClickOutside) + }, [open]) + + return ( +
setOpen(true)} onMouseLeave={() => setOpen(false)}> + + {open && ( +
+

Regex syntax (Rust regex crate)

+ + + {CHEAT_SHEET.map(({ tokens, meaning }) => ( + + + + + ))} + +
{tokens}{meaning}
+

No lookaround or backreferences (not supported by the Rust regex crate).

+
+ )} +
+ ) +}