improve flow indication in table
All checks were successful
Build and Deploy MITM Webserver / traffic_target (push) Successful in 1s
Build and Deploy MITM Webserver / build (push) Successful in 13s

This commit is contained in:
2026-04-02 21:42:07 +02:00
parent 74b961e152
commit 8153dc283c

View File

@@ -152,6 +152,8 @@ type PacketTableRow = PacketRow & {
key: string; key: string;
__kind: 'packet' | 'flow'; __kind: 'packet' | 'flow';
flow_packet_count?: number; flow_packet_count?: number;
flow_packet_index?: number;
flow_has_siblings?: boolean;
children?: PacketTableRow[]; children?: PacketTableRow[];
}; };
@@ -1103,11 +1105,14 @@ export default function PacketViewer(): ReactElement {
} }
const latestPacket = [...flowPackets].sort(sortPacketsByTimestampDesc)[0]; const latestPacket = [...flowPackets].sort(sortPacketsByTimestampDesc)[0];
const children = sortedPackets.map((packet) => ({ const children = sortedPackets.map((packet, index) => ({
...packet, ...packet,
flow_id: flowId, flow_id: flowId,
key: packetKey(packet), key: packetKey(packet),
__kind: 'packet' as const, __kind: 'packet' as const,
flow_packet_count: sortedPackets.length,
flow_packet_index: index + 1,
flow_has_siblings: sortedPackets.length > 1,
})); }));
flowRows.push({ flowRows.push({
...latestPacket, ...latestPacket,
@@ -1127,21 +1132,45 @@ export default function PacketViewer(): ReactElement {
{ {
title: 'Group', title: 'Group',
key: 'group', key: 'group',
width: 130, width: 180,
render: (_: any, rec: PacketTableRow) => render: (_: any, rec: PacketTableRow) =>
rec.__kind === 'flow' ? ( rec.__kind === 'flow' ? (
<Space direction="vertical" size={0}> <div className="packet-group-cell packet-group-cell-flow">
<Tag color="blue" style={{ width: 'fit-content', marginInlineEnd: 0 }}> <div className="packet-group-title">
<Tag color="blue" style={{ marginInlineEnd: 0 }}>
Flow Flow
</Tag> </Tag>
<Text type="secondary" style={{ fontSize: 12 }}> <span className="packet-group-count">{rec.flow_packet_count ?? 0} packets</span>
{rec.flow_packet_count ?? 0} packets </div>
<Text type="secondary" className="packet-group-subtitle">
{rec.flow_id ?? 'Grouped flow'}
</Text> </Text>
</Space> </div>
) : ( ) : (
<Text type="secondary" style={{ paddingLeft: 18 }}> <div className={`packet-group-cell ${rec.flow_has_siblings ? 'packet-group-cell-child' : 'packet-group-cell-single'}`}>
Packet {rec.flow_has_siblings ? (
<>
<div className="packet-child-rail" aria-hidden="true" />
<div className="packet-child-content">
<Tag color="default" style={{ marginInlineEnd: 0 }}>
Packet {rec.flow_packet_index ?? 1}/{rec.flow_packet_count ?? 1}
</Tag>
<Text type="secondary" className="packet-group-subtitle">
In expanded flow
</Text> </Text>
</div>
</>
) : (
<>
<Tag color="default" style={{ marginInlineEnd: 0 }}>
Single Packet
</Tag>
<Text type="secondary" className="packet-group-subtitle">
Not grouped into a flow
</Text>
</>
)}
</div>
), ),
}, },
{ {
@@ -1332,16 +1361,21 @@ export default function PacketViewer(): ReactElement {
{` {`
.packet-viewer .ant-table-tbody > tr.packet-flow-row > td { .packet-viewer .ant-table-tbody > tr.packet-flow-row > td {
background: #eef6ff; background: #eef6ff;
border-top: 1px solid #cfe2ff; border-top: 1px solid #c8dbff;
border-bottom: 1px solid #cfe2ff; border-bottom: 1px solid #c8dbff;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), inset 0 -1px 0 rgba(165, 194, 255, 0.24);
} }
.packet-viewer .ant-table-tbody > tr.packet-child-row > td { .packet-viewer .ant-table-tbody > tr.packet-child-row > td {
background: #fafcff; background: #fbfdff;
} }
.packet-viewer .ant-table-tbody > tr.packet-child-row > td:first-child { .packet-viewer .ant-table-tbody > tr.packet-standalone-row > td {
border-left: 3px solid #d6e4ff; background: #ffffff;
}
.packet-viewer .ant-table-tbody > tr.packet-child-row > td {
border-top-color: #edf2fb;
} }
.packet-viewer .ant-table-row-expand-icon { .packet-viewer .ant-table-row-expand-icon {
@@ -1355,6 +1389,102 @@ export default function PacketViewer(): ReactElement {
font-weight: 600; font-weight: 600;
} }
.packet-viewer .packet-group-cell {
display: flex;
flex-direction: column;
gap: 4px;
min-height: 40px;
justify-content: center;
}
.packet-viewer .packet-group-cell-flow {
gap: 6px;
}
.packet-viewer .packet-group-title {
display: inline-flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.packet-viewer .packet-group-count {
font-size: 12px;
font-weight: 600;
color: #1d4f91;
background: rgba(214, 232, 255, 0.85);
border: 1px solid rgba(148, 186, 242, 0.65);
border-radius: 999px;
padding: 1px 8px;
}
.packet-viewer .packet-group-subtitle {
font-size: 12px;
line-height: 1.3;
word-break: break-word;
}
.packet-viewer .packet-group-cell-child {
position: relative;
flex-direction: row;
align-items: stretch;
gap: 10px;
padding-left: 2px;
}
.packet-viewer .packet-child-rail {
width: 16px;
flex: 0 0 16px;
position: relative;
}
.packet-viewer .packet-child-rail::before {
content: '';
position: absolute;
left: 7px;
top: -12px;
bottom: -12px;
width: 2px;
background: linear-gradient(180deg, #c7d9fb 0%, #dbe8ff 100%);
border-radius: 999px;
}
.packet-viewer .packet-child-rail::after {
content: '';
position: absolute;
left: 7px;
top: 50%;
width: 12px;
height: 2px;
transform: translateY(-50%);
background: #c7d9fb;
border-radius: 999px;
}
.packet-viewer .packet-child-content {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
justify-content: center;
}
.packet-viewer .packet-group-cell-single {
gap: 6px;
}
.packet-viewer .ant-table-tbody > tr.packet-flow-row:hover > td {
background: #e7f1ff;
}
.packet-viewer .ant-table-tbody > tr.packet-child-row:hover > td {
background: #f4f8ff;
}
.packet-viewer .ant-table-tbody > tr.packet-standalone-row:hover > td {
background: #fafcff;
}
.packet-viewer .packet-mode-toggle.ant-segmented { .packet-viewer .packet-mode-toggle.ant-segmented {
background: ${token.colorFillTertiary}; background: ${token.colorFillTertiary};
} }
@@ -1464,7 +1594,13 @@ export default function PacketViewer(): ReactElement {
<span style={{ display: 'inline-block', width: 24 }} /> <span style={{ display: 'inline-block', width: 24 }} />
), ),
}} }}
rowClassName={(record: PacketTableRow) => (record.__kind === 'flow' ? 'packet-flow-row' : 'packet-child-row')} rowClassName={(record: PacketTableRow) =>
record.__kind === 'flow'
? 'packet-flow-row'
: record.flow_has_siblings
? 'packet-child-row'
: 'packet-standalone-row'
}
/> />
)} )}
</Card> </Card>