From f08abec68a4c0730b0bf8929b20c44727fb7e5e4 Mon Sep 17 00:00:00 2001 From: dirtydishes Date: Tue, 20 Jan 2026 12:57:38 -0500 Subject: [PATCH] Open classifier hit drawer when alert missing --- apps/web/app/page.tsx | 228 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 206 insertions(+), 22 deletions(-) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index e6b1430..579ad2c 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -2388,6 +2388,114 @@ const AlertDrawer = ({ alert, flowPacket, evidence, onClose }: AlertDrawerProps) ); }; +type ClassifierHitDrawerProps = { + hit: ClassifierHitEvent; + flowPacket: FlowPacket | null; + evidence: EvidenceItem[]; + onClose: () => void; +}; + +const ClassifierHitDrawer = ({ hit, flowPacket, evidence, onClose }: ClassifierHitDrawerProps) => { + const direction = normalizeDirection(hit.direction); + const evidencePrints = evidence.filter((item) => item.kind === "print"); + const unknownCount = evidence.filter((item) => item.kind === "unknown").length; + + return ( + + ); +}; + type DarkDrawerProps = { event: InferredDarkEvent; evidence: DarkEvidenceItem[]; @@ -2513,6 +2621,7 @@ export default function HomePage() { const [replaySource, setReplaySource] = useState(null); const [selectedAlert, setSelectedAlert] = useState(null); const [selectedDarkEvent, setSelectedDarkEvent] = useState(null); + const [selectedClassifierHit, setSelectedClassifierHit] = useState(null); const [filterInput, setFilterInput] = useState(""); const [chartIntervalMs, setChartIntervalMs] = useState(CANDLE_INTERVALS[0].ms); @@ -2779,6 +2888,7 @@ export default function HomePage() { setSelectedAlert(null); } setSelectedDarkEvent(null); + setSelectedClassifierHit(null); }, [mode]); const extractPacketContract = useCallback((packet: FlowPacket): string => { @@ -2806,6 +2916,43 @@ export default function HomePage() { return traceId.slice(idx); }, []); + const selectedClassifierPacketId = useMemo(() => { + if (!selectedClassifierHit) { + return null; + } + return extractPacketIdFromClassifierHitTrace(selectedClassifierHit.trace_id); + }, [extractPacketIdFromClassifierHitTrace, selectedClassifierHit]); + + const selectedClassifierFlowPacket = useMemo(() => { + if (!selectedClassifierPacketId) { + return null; + } + return flowPacketMap.get(selectedClassifierPacketId) ?? null; + }, [flowPacketMap, selectedClassifierPacketId]); + + const selectedClassifierEvidence = useMemo((): EvidenceItem[] => { + if (!selectedClassifierHit) { + return []; + } + + if (!selectedClassifierPacketId) { + return []; + } + + const packet = flowPacketMap.get(selectedClassifierPacketId); + if (!packet) { + return []; + } + + return packet.members.map((id) => { + const print = optionPrintMap.get(id); + if (print) { + return { kind: "print", id, print }; + } + return { kind: "unknown", id }; + }); + }, [flowPacketMap, optionPrintMap, selectedClassifierHit, selectedClassifierPacketId]); + const inferAlertUnderlying = useCallback( (alert: AlertEvent): string | null => { const fromTrace = extractUnderlyingFromTrace(alert.trace_id); @@ -2924,29 +3071,50 @@ export default function HomePage() { }); }, [chartTicker, inferredDark.items, equityJoinMap, equityPrintMap]); - const handleClassifierMarkerClick = useCallback( - (hit: ClassifierHitEvent) => { + const findAlertForClassifierHit = useCallback( + (hit: ClassifierHitEvent): AlertEvent | null => { const packetId = extractPacketIdFromClassifierHitTrace(hit.trace_id); if (!packetId) { - return; + return null; } const desiredTrace = `alert:${packetId}`; - const alert = alerts.items.find( - (item) => item.trace_id === desiredTrace || item.evidence_refs[0] === packetId + return ( + alerts.items.find( + (item) => item.trace_id === desiredTrace || item.evidence_refs[0] === packetId + ) ?? null ); - if (!alert) { - return; - } - - setSelectedDarkEvent(null); - setSelectedAlert(alert); }, [alerts.items, extractPacketIdFromClassifierHitTrace] ); + const openFromClassifierHit = useCallback( + (hit: ClassifierHitEvent) => { + const alert = findAlertForClassifierHit(hit); + if (alert) { + setSelectedClassifierHit(null); + setSelectedDarkEvent(null); + setSelectedAlert(alert); + return; + } + + setSelectedAlert(null); + setSelectedDarkEvent(null); + setSelectedClassifierHit(hit); + }, + [findAlertForClassifierHit] + ); + + const handleClassifierMarkerClick = useCallback( + (hit: ClassifierHitEvent) => { + openFromClassifierHit(hit); + }, + [openFromClassifierHit] + ); + const handleDarkMarkerClick = useCallback((event: InferredDarkEvent) => { setSelectedAlert(null); + setSelectedClassifierHit(null); setSelectedDarkEvent(event); }, []); @@ -3401,6 +3569,7 @@ export default function HomePage() { type="button" onClick={() => { setSelectedDarkEvent(null); + setSelectedClassifierHit(null); setSelectedAlert(alert); }} > @@ -3468,7 +3637,12 @@ export default function HomePage() { filteredClassifierHits.map((hit) => { const direction = normalizeDirection(hit.direction); return ( -
+ ); }) )} @@ -3528,15 +3702,16 @@ export default function HomePage() { const underlying = inferDarkUnderlying(event, equityPrintMap, equityJoinMap); const evidenceCount = event.evidence_refs.length; return ( -