Removed observations dropdown from main packet list, moved completely to packet analyzer

This commit is contained in:
MrAlders0n
2026-05-29 17:39:31 -04:00
parent b63e132433
commit 71c25ecd4c
5 changed files with 19 additions and 65 deletions
+1 -1
View File
@@ -96,7 +96,7 @@ function AppInner() {
}, []);
const tabContent: Record<string, React.ReactNode> = {
Packets: <PacketList wsManager={wsManager} onAnalyze={handleAnalyze} selectedObservationId={selectedObservationId} onSelectObservation={setSelectedObservationId} />,
Packets: <PacketList wsManager={wsManager} onAnalyze={handleAnalyze} />,
Nodes: <NodeTable wsManager={wsManager} />,
Observers: <ObserverTable wsManager={wsManager} />,
Channels: <ChannelList wsManager={wsManager} onAnalyze={handleAnalyze} />,
@@ -149,7 +149,7 @@ export function PacketAnalyzerDrawer({ detail, selectedObservationId, open, onTo
</DrawerSection>
)}
{detail.observations.length > 1 && (
{detail.observations.length >= 1 && (
<DrawerSection title={`Observations (${detail.observations.length})`} collapsible defaultOpen={false}>
<div className="flex flex-col gap-1">
{detail.observations.map((obs) => (
+10 -27
View File
@@ -1,12 +1,10 @@
import { useState, useCallback, useRef, useMemo } from "react";
import { useSearchParams } from "react-router-dom";
import { useQuery, keepPreviousData } from "@tanstack/react-query";
import { usePackets } from "./usePackets";
import { usePacketFilters, matchesFilters } from "./usePacketFilters";
import { useWsPacketHandler, useWsLaggedHandler } from "../../hooks/useWsHandlers";
import { PacketVirtualList } from "./PacketVirtualList";
import { FilterBar } from "../../components/FilterBar";
import { getPacketDetail } from "../../api/client";
import { PAYLOAD_TYPE_NAMES, ROUTE_TYPE_NAMES } from "../../types/enums";
import type { WsManager } from "../../api/ws-manager";
@@ -25,13 +23,11 @@ const ROUTE_OPTIONS = Object.entries(ROUTE_TYPE_NAMES).map(([value, label]) => (
interface PacketListProps {
wsManager: WsManager;
onAnalyze: (hash: string | null) => void;
selectedObservationId: number | null;
onSelectObservation: (id: number | null) => void;
}
// main packet view: filters, banner, virtual list
export function PacketList({ wsManager, onAnalyze, selectedObservationId, onSelectObservation }: PacketListProps) {
export function PacketList({ wsManager, onAnalyze }: PacketListProps) {
const [searchParams, setSearchParams] = useSearchParams();
const { filters, setFilter, setSearch, setSearchField, clearFilters } = usePacketFilters();
const {
@@ -59,26 +55,16 @@ export function PacketList({ wsManager, onAnalyze, selectedObservationId, onSele
const [expandedHash, setExpandedHash] = useState<string | null>(() => searchParams.get("hash"));
const { data: expandedDetail } = useQuery({
queryKey: ["packet-detail", expandedHash],
queryFn: () => getPacketDetail(expandedHash!),
enabled: !!expandedHash,
staleTime: Infinity,
placeholderData: keepPreviousData,
});
const handleToggleExpand = useCallback((hash: string) => {
setExpandedHash((prev) => {
const next = prev === hash ? null : hash;
onAnalyze(next);
setSearchParams((p) => {
const n = new URLSearchParams(p);
if (next) n.set("hash", next); else n.delete("hash");
return n;
}, { replace: true });
return next;
});
}, [setSearchParams, onAnalyze]);
const next = expandedHash === hash ? null : hash;
setExpandedHash(next);
onAnalyze(next);
setSearchParams((p) => {
const n = new URLSearchParams(p);
if (next) n.set("hash", next); else n.delete("hash");
return n;
}, { replace: true });
}, [expandedHash, setSearchParams, onAnalyze]);
useWsPacketHandler(wsManager, handlePacketObservation);
useWsLaggedHandler(wsManager, handleLagged);
@@ -143,10 +129,7 @@ export function PacketList({ wsManager, onAnalyze, selectedObservationId, onSele
onScrollAwayFromTop={handleScrolledAway}
scrollToTopRef={scrollToTopRef}
expandedHash={expandedHash}
expandedDetail={expandedDetail}
onToggleExpand={handleToggleExpand}
selectedObservationId={selectedObservationId}
onSelectObservation={onSelectObservation}
/>
</div>
</div>
+6 -26
View File
@@ -1,33 +1,29 @@
import { formatHex, formatTimestamp } from "../../lib/formatters";
import type { PacketSummary, PacketDetail } from "../../types/api";
import type { PacketSummary } from "../../types/api";
import { Badge } from "../../components/Badge";
import { payloadTypeVariant } from "../../components/badge-utils";
import { ObservationCard } from "./ObservationCard";
interface PacketRowProps {
packet: PacketSummary;
expanded: boolean;
detail?: PacketDetail;
isFresh?: boolean;
onToggle: () => void;
selectedObservationId?: number | null;
onSelectObservation?: (id: number) => void;
}
// expandable packet card with observations
// selectable packet card; observations live in the analyzer drawer
export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selectedObservationId, onSelectObservation }: PacketRowProps) {
export function PacketRow({ packet, expanded, isFresh, onToggle }: PacketRowProps) {
return (
<div
className={`group bg-bg-surface border rounded-md px-3.5 py-2.5 cursor-pointer ${
expanded
? "border-secondary bg-bg-raised"
? "border-primary bg-primary/10"
: isFresh
? "packet-fresh"
: "border-border hover:border-text-dim/30 hover:bg-bg-raised/50"
}`}
onClick={() => onToggle()}
aria-expanded={expanded}
aria-pressed={expanded}
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
@@ -37,9 +33,6 @@ export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selecte
}}
>
<div className="flex items-center gap-2.5">
<span className="text-text-muted group-hover:text-text-normal text-[11px] w-3.5 font-mono transition-colors" aria-hidden>
{expanded ? "▾" : "▸"}
</span>
<span className="font-mono text-xs font-semibold text-primary tracking-wider">
{formatHex(packet.packetHash)}
</span>
@@ -58,7 +51,7 @@ export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selecte
</span>
</div>
<div className="flex items-center gap-2 mt-1 pl-6 text-[11px] text-text-dim">
<div className="flex items-center gap-2 mt-1 text-[11px] text-text-dim">
<span className="font-mono text-[11px] text-text-muted uppercase tracking-wider bg-text-muted/8 px-1.5 py-px rounded-sm">
{packet.routeTypeName || "Unknown"}
</span>
@@ -75,19 +68,6 @@ export function PacketRow({ packet, expanded, detail, isFresh, onToggle, selecte
</>
)}
</div>
{expanded && detail && (
<div className="mt-2 pl-6 flex flex-col gap-1" onClick={(e) => e.stopPropagation()}>
{detail.observations.map((obs) => (
<ObservationCard
key={obs.id}
observation={obs}
selected={selectedObservationId === obs.id}
onClick={onSelectObservation ? () => onSelectObservation(obs.id) : undefined}
/>
))}
</div>
)}
</div>
);
}
+1 -10
View File
@@ -1,6 +1,6 @@
import { useRef, useState, useCallback, useEffect, useLayoutEffect } from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
import type { PacketSummary, PacketDetail } from "../../types/api";
import type { PacketSummary } from "../../types/api";
import { PacketRow } from "./PacketRow";
import { LIVE_BUFFER_CAP, SCROLL_TOP_THRESHOLD_PX, SCROLL_BOTTOM_THRESHOLD_PX } from "../../lib/constants";
@@ -12,10 +12,7 @@ interface PacketVirtualListProps {
onScrollAwayFromTop: (isAway: boolean) => void;
scrollToTopRef?: React.MutableRefObject<(() => void) | null>;
expandedHash: string | null;
expandedDetail?: PacketDetail;
onToggleExpand: (hash: string) => void;
selectedObservationId: number | null;
onSelectObservation: (id: number) => void;
}
// virtualized scroll list with fresh-item highlighting and infinite load
@@ -28,10 +25,7 @@ export function PacketVirtualList({
onScrollAwayFromTop,
scrollToTopRef,
expandedHash,
expandedDetail,
onToggleExpand,
selectedObservationId,
onSelectObservation,
}: PacketVirtualListProps) {
const parentRef = useRef<HTMLDivElement>(null);
const knownHashes = useRef<Set<string>>(new Set());
@@ -177,11 +171,8 @@ export function PacketVirtualList({
<PacketRow
packet={packet}
expanded={expandedHash === packet.packetHash}
detail={expandedHash === packet.packetHash ? expandedDetail : undefined}
isFresh={freshHashes.has(packet.packetHash)}
onToggle={() => onToggleExpand(packet.packetHash)}
selectedObservationId={expandedHash === packet.packetHash ? selectedObservationId : null}
onSelectObservation={onSelectObservation}
/>
</div>
</div>