From 809c3db93d991366c3746c5a8ba74780ba16c6b2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 23 Mar 2026 16:11:11 +0000 Subject: [PATCH 1/2] fix(app): fully clear heatmap visual selection Co-authored-by: Alex Fedotyev --- packages/app/src/components/DBDeltaChart.tsx | 18 +++++++++++++++ .../app/src/components/DBHeatmapChart.tsx | 22 ++++++++++++++++++- .../Search/DBSearchHeatmapChart.tsx | 12 ++++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/packages/app/src/components/DBDeltaChart.tsx b/packages/app/src/components/DBDeltaChart.tsx index 61865a360..ba8899c72 100644 --- a/packages/app/src/components/DBDeltaChart.tsx +++ b/packages/app/src/components/DBDeltaChart.tsx @@ -5,6 +5,7 @@ import { Filter, } from '@hyperdx/common-utils/dist/types'; import { + ActionIcon, Box, Code, Container, @@ -12,8 +13,10 @@ import { Flex, Pagination, Text, + Tooltip, } from '@mantine/core'; import { useElementSize } from '@mantine/hooks'; +import { IconX } from '@tabler/icons-react'; import { isAggregateFunction } from '@/ChartUtils'; import { useQueriedChartConfig } from '@/hooks/useChartConfig'; @@ -52,6 +55,7 @@ export default function DBDeltaChart({ yMin: rawYMin, yMax: rawYMax, onAddFilter, + onClearSelection, spanIdExpression, legendPrefix, }: { @@ -62,6 +66,7 @@ export default function DBDeltaChart({ yMin?: number | null; yMax?: number | null; onAddFilter?: AddFilterFn; + onClearSelection?: () => void; spanIdExpression?: string; legendPrefix?: React.ReactNode; }) { @@ -516,6 +521,19 @@ export default function DBDeltaChart({ Background + {onClearSelection && ( + + + + + + )} ) : ( <> diff --git a/packages/app/src/components/DBHeatmapChart.tsx b/packages/app/src/components/DBHeatmapChart.tsx index bad724eb8..f78769223 100644 --- a/packages/app/src/components/DBHeatmapChart.tsx +++ b/packages/app/src/components/DBHeatmapChart.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import dynamic from 'next/dynamic'; import type { Plugin } from 'uplot'; import uPlot from 'uplot'; @@ -341,6 +341,7 @@ function HeatmapContainer({ config, enabled = true, onFilter, + clearSelectionVersion = 0, title, toolbarPrefix, toolbarSuffix, @@ -349,6 +350,7 @@ function HeatmapContainer({ config: HeatmapChartConfig; enabled?: boolean; onFilter?: (xMin: number, xMax: number, yMin: number, yMax: number) => void; + clearSelectionVersion?: number; title?: React.ReactNode; toolbarPrefix?: React.ReactNode[]; toolbarSuffix?: React.ReactNode[]; @@ -661,6 +663,7 @@ function HeatmapContainer({ key={JSON.stringify(config)} data={[time, bucket, count]} numberFormat={config.numberFormat} + clearSelectionVersion={clearSelectionVersion} onFilter={ onFilter ? (xMin, xMax, yMin, yMax) => { @@ -791,12 +794,14 @@ function Heatmap({ data, numberFormat, onFilter, + clearSelectionVersion = 0, scaleType = 'linear', palette, }: { data: Mode2DataArray; numberFormat?: NumberFormat; onFilter?: (xMin: number, xMax: number, yMin: number, yMax: number) => void; + clearSelectionVersion?: number; scaleType?: HeatmapScaleType; palette: string[]; }) { @@ -834,9 +839,18 @@ function Heatmap({ // Gate tooltip display on actual mouse interaction. uPlot fires setCursor // on init (before user hovers), which would show the tooltip on page load. const mouseInsideRef = useRef(false); + const uplotRef = useRef(null); const { ref, width, height } = useElementSize(); + useEffect(() => { + setSelectingInfo(undefined); + if (uplotRef.current != null) { + // Clear persisted uPlot drag rectangle when parent resets selection. + uplotRef.current.setSelect({ left: 0, top: 0, width: 0, height: 0 }, false); + } + }, [clearSelectionVersion]); + const tickFormatter = useCallback( (value: number) => { // y-values are stored in log space for log scale; exponentiate back @@ -1048,6 +1062,12 @@ function Heatmap({ // @ts-expect-error TODO: uPlot types are wrong for mode 2 data data={[[], data]} resetScales={true} + onCreate={chart => { + uplotRef.current = chart; + }} + onDelete={() => { + uplotRef.current = null; + }} /> {highlightedPoint != null && ( <> diff --git a/packages/app/src/components/Search/DBSearchHeatmapChart.tsx b/packages/app/src/components/Search/DBSearchHeatmapChart.tsx index 9c5db111d..97bc1d403 100644 --- a/packages/app/src/components/Search/DBSearchHeatmapChart.tsx +++ b/packages/app/src/components/Search/DBSearchHeatmapChart.tsx @@ -69,6 +69,7 @@ export function DBSearchHeatmapChart({ yMax: parseAsFloat, }); const [container, setContainer] = useState(null); + const [clearSelectionVersion, setClearSelectionVersion] = useState(0); const scaleType = (fields.scaleType ?? 'log') as HeatmapScaleType; const setScaleType = useCallback( (v: HeatmapScaleType) => { @@ -80,16 +81,21 @@ export function DBSearchHeatmapChart({ const { colorScheme } = useMantineColorScheme(); const palette = colorScheme === 'light' ? lightPalette : darkPalette; + const clearSelection = useCallback(() => { + setFields({ xMin: null, xMax: null, yMin: null, yMax: null }); + setClearSelectionVersion(version => version + 1); + }, [setFields]); + // After applying a filter, clear the heatmap selection so the delta chart // resets instead of staying in comparison mode. const handleAddFilterAndClearSelection = useCallback< NonNullable >( (property, value, action) => { - setFields({ xMin: null, xMax: null, yMin: null, yMax: null }); + clearSelection(); onAddFilter?.(property, value, action); }, - [onAddFilter, setFields], + [clearSelection, onAddFilter], ); return ( @@ -125,6 +131,7 @@ export function DBSearchHeatmapChart({ : undefined, }} enabled={isReady} + clearSelectionVersion={clearSelectionVersion} scaleType={scaleType} onFilter={(xMin, xMax, yMin, yMax) => { setFields({ xMin, xMax, yMin, yMax }); @@ -180,6 +187,7 @@ export function DBSearchHeatmapChart({ onAddFilter={ onAddFilter ? handleAddFilterAndClearSelection : undefined } + onClearSelection={clearSelection} spanIdExpression={source.spanIdExpression} legendPrefix={} /> From 3e7030ed0d03e3f800ac37d1dbb95faae1f9b074 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 23 Mar 2026 16:22:32 +0000 Subject: [PATCH 2/2] fix(app): format heatmap selection reset call Co-authored-by: Alex Fedotyev --- packages/app/src/components/DBHeatmapChart.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/DBHeatmapChart.tsx b/packages/app/src/components/DBHeatmapChart.tsx index f78769223..256aa90b5 100644 --- a/packages/app/src/components/DBHeatmapChart.tsx +++ b/packages/app/src/components/DBHeatmapChart.tsx @@ -847,7 +847,10 @@ function Heatmap({ setSelectingInfo(undefined); if (uplotRef.current != null) { // Clear persisted uPlot drag rectangle when parent resets selection. - uplotRef.current.setSelect({ left: 0, top: 0, width: 0, height: 0 }, false); + uplotRef.current.setSelect( + { left: 0, top: 0, width: 0, height: 0 }, + false, + ); } }, [clearSelectionVersion]);