fix(packets): fold summaries into the endpoints cell (#73)

This commit is contained in:
MrAlders0n authored and GitHub committed 2026-09-25 11:37:57 -04:00
1 parent 9cd2e8f6db
commit 54b5093ac0
5 files changed
+39 -11

No files matched your search

+15 -6
View File
@@ -1,5 +1,5 @@
import type { PacketSummary } from "../../types/api";
import type { PathConfidence } from "../../types/enums";
import { PayloadType, type PathConfidence } from "../../types/enums";
import { buildPathSummary, type PathChip } from "./path-summary";
// Same three-state vocabulary PathData uses in the analyzer.
@@ -21,7 +21,7 @@ function Chip({ chip }: { chip: PathChip }) {
);
}
return (
<span className={`font-mono text-[10px] px-1.5 py-px rounded-sm truncate ${CONFIDENCE_CLASSES[chip.confidence]}`}>
<span className={`font-mono text-[10px] px-1.5 py-px rounded-sm truncate ${CONFIDENCE_CLASSES[chip.confidence]}`} title={chip.label}>
{chip.label}
</span>
);
@@ -30,12 +30,21 @@ function Chip({ chip }: { chip: PathChip }) {
const Na = () => <span className="text-text-dim">n/a</span>;
// The packet's logical endpoints. beacon-server resolves these on the WS feed only and leaves them
// nil on the REST list, so scrollback rows read n/a — as do payload types with no addressed
// endpoint at all (GRP_TXT/GRP_DATA/TRACE).
// nil on the REST list; there the server's summary (advert name, trace tag, ack checksum) stands in.
export function PacketEndpoints({ packet }: { packet: PacketSummary }) {
const { source, destination } = buildPathSummary(packet);
// One n/a for the pair reads better than "n/a → n/a" on every historical row.
if (!source && !destination) return <Na />;
// An advert is the node announcing itself, so its name is the whole story — no destination.
if (packet.payloadType === PayloadType.ADVERT) {
const self = source ?? (packet.summary ? { kind: "node" as const, label: packet.summary, confidence: "high" as const } : null);
return self ? <Chip chip={self} /> : <Na />;
}
if (!source && !destination) {
return packet.summary
? <span className="block truncate font-mono text-[10px] text-text-muted tracking-wider" title={packet.summary}>{packet.summary}</span>
: <Na />;
}
return (
<span className="flex items-center gap-x-1 min-w-0">
+1 -1
View File
@@ -54,7 +54,7 @@ export function PacketRow({ packet, expanded, isFresh, onToggle }: PacketRowProp
</Tooltip>
</div>
{packet.summary && <div className="mt-1 truncate text-[11px] text-text-bright" title={packet.summary}>{packet.summary}</div>}
{packet.summary && <div className="mt-1 truncate font-mono text-[10px] text-text-muted tracking-wider" title={packet.summary}>{packet.summary}</div>}
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 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">
-1
View File
@@ -58,7 +58,6 @@ export function PacketTableRow({ packet, expanded, isFresh, onToggle }: PacketTa
<span className="font-mono text-text-muted">{pathLength?.hopCount ?? na}</span>
<span className="font-mono text-text-muted">{pathLength?.hashSize ?? na}</span>
<span className="min-w-0 overflow-hidden">
{packet.summary && <span className="block truncate text-text-bright" title={packet.summary}>{packet.summary}</span>}
<PacketEndpoints packet={packet} />
</span>
<span className="font-mono font-bold text-primary tracking-wider">
@@ -64,4 +64,24 @@ describe("PacketEndpoints", () => {
}))} />);
expect(screen.getByText("?")).toBeInTheDocument();
});
it("shows an advert as its single source node with no destination", () => {
const advert = { ...pkt(obs({ resolvedSource: { confidence: "high", nodes: [{ id: "s", publicKey: "aa", name: "Fuzz HQ" }] } })), payloadType: 4, summary: "Fuzz HQ" };
render(<PacketEndpoints packet={advert} />);
expect(screen.getAllByText("Fuzz HQ")).toHaveLength(1);
expect(screen.queryByText("→")).not.toBeInTheDocument();
expect(screen.queryByText("n/a")).not.toBeInTheDocument();
});
it("falls back to the summary name for an advert with no resolved source", () => {
render(<PacketEndpoints packet={{ ...pkt(obs()), payloadType: 4, summary: "Fuzz HQ" }} />);
expect(screen.getByText("Fuzz HQ").className).toContain("text-green");
expect(screen.queryByText("n/a")).not.toBeInTheDocument();
});
it("replaces n/a with the summary when there are no endpoints", () => {
render(<PacketEndpoints packet={{ ...pkt(obs()), payloadType: 9, summary: "TRACE 2ca2a79c" }} />);
expect(screen.getByText("TRACE 2ca2a79c").className).toContain("text-text-muted");
expect(screen.queryByText("n/a")).not.toBeInTheDocument();
});
});
@@ -23,9 +23,9 @@ const observer = (
};
describe("PacketTableRow", () => {
it("keeps endpoints visible alongside a payload summary", () => {
render(<PacketTableRow packet={pkt({ summary: "Packet summary", latestObserver: observer({ resolvedSource: node("Source node"), resolvedDestination: node("Destination node") }) })} expanded={false} onToggle={() => {}} />);
expect(screen.getByText("Packet summary")).toBeInTheDocument();
it("prefers endpoints over a payload summary", () => {
render(<PacketTableRow packet={pkt({ payloadType: 2, summary: "Packet summary", latestObserver: observer({ resolvedSource: node("Source node"), resolvedDestination: node("Destination node") }) })} expanded={false} onToggle={() => {}} />);
expect(screen.queryByText("Packet summary")).not.toBeInTheDocument();
expect(screen.getByText("Source node")).toBeInTheDocument();
expect(screen.getByText("Destination node")).toBeInTheDocument();
});