Fix regex help icon click-toggle and add PromQL by()/sum(rate()) grouping

RegexHelpIcon previously used a single `open` boolean driven by both hover
and click, so clicking to pin the popover open was undone by the next
mouseleave. Split into separate `pinned` (click) and `hovering` (mouse-over)
states so a click-toggle actually persists until clicked again.

The seeded `log_category` dashboard's line panel plotted the bare metric
name, since `sum()`/`rate()` always collapsed all label combinations into
one series and PromQL syntax had no way to preserve a label dimension
through an aggregation. Added `by (label, ...)` grouping and nested
`sum(rate(metric[range]))`/`avg(rate(...))` parsing, and made `rate()`
compute a real windowed time series instead of a single trailing point, so
a query like `sum(rate(log_category[1m])) by (category)` now renders one
line per category.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
Hugh Kaznowski
2026-09-05 20:17:50 +01:00
co-authored by Claude Sonnet 5
parent d6b7f7dfb6
commit 5aa7c1bae9
4 changed files with 330 additions and 34 deletions
+15 -6
View File
@@ -23,23 +23,32 @@ const CHEAT_SHEET: { tokens: string; meaning: string }[] = [
// "@" 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)
// Click pins the popover open until clicked again; hovering shows it only while the mouse
// stays over the icon/box, independent of the pinned state.
const [pinned, setPinned] = useState(false)
const [hovering, setHovering] = useState(false)
const open = pinned || hovering
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!open) return
if (!pinned) return
function onClickOutside(e: MouseEvent) {
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)
if (ref.current && !ref.current.contains(e.target as Node)) setPinned(false)
}
document.addEventListener('mousedown', onClickOutside)
return () => document.removeEventListener('mousedown', onClickOutside)
}, [open])
}, [pinned])
return (
<div ref={ref} className="relative inline-flex" onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
<div
ref={ref}
className="relative inline-flex"
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
<button
type="button"
onClick={() => setOpen((v) => !v)}
onClick={() => setPinned((v) => !v)}
title="Regex syntax cheat sheet"
aria-label={label ?? 'Regex syntax cheat sheet'}
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full border border-border-soft bg-surface-2 text-xs leading-none text-text-muted hover:border-border-strong hover:text-text-secondary"