From 8153dc283cc302abca0c1f4cad77caea57b26c7c Mon Sep 17 00:00:00 2001 From: malmert Date: Thu, 2 Apr 2026 21:42:07 +0200 Subject: [PATCH] improve flow indication in table --- frontend/src/components/PacketViewer.tsx | 172 ++++++++++++++++++++--- 1 file changed, 154 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/PacketViewer.tsx b/frontend/src/components/PacketViewer.tsx index 6e43421..593353b 100644 --- a/frontend/src/components/PacketViewer.tsx +++ b/frontend/src/components/PacketViewer.tsx @@ -152,6 +152,8 @@ type PacketTableRow = PacketRow & { key: string; __kind: 'packet' | 'flow'; flow_packet_count?: number; + flow_packet_index?: number; + flow_has_siblings?: boolean; children?: PacketTableRow[]; }; @@ -1103,11 +1105,14 @@ export default function PacketViewer(): ReactElement { } const latestPacket = [...flowPackets].sort(sortPacketsByTimestampDesc)[0]; - const children = sortedPackets.map((packet) => ({ + const children = sortedPackets.map((packet, index) => ({ ...packet, flow_id: flowId, key: packetKey(packet), __kind: 'packet' as const, + flow_packet_count: sortedPackets.length, + flow_packet_index: index + 1, + flow_has_siblings: sortedPackets.length > 1, })); flowRows.push({ ...latestPacket, @@ -1127,21 +1132,45 @@ export default function PacketViewer(): ReactElement { { title: 'Group', key: 'group', - width: 130, + width: 180, render: (_: any, rec: PacketTableRow) => rec.__kind === 'flow' ? ( - - - Flow - - - {rec.flow_packet_count ?? 0} packets +
+
+ + Flow + + {rec.flow_packet_count ?? 0} packets +
+ + {rec.flow_id ?? 'Grouped flow'} - +
) : ( - - Packet - +
+ {rec.flow_has_siblings ? ( + <> + ), }, { @@ -1332,16 +1361,21 @@ export default function PacketViewer(): ReactElement { {` .packet-viewer .ant-table-tbody > tr.packet-flow-row > td { background: #eef6ff; - border-top: 1px solid #cfe2ff; - border-bottom: 1px solid #cfe2ff; + border-top: 1px solid #c8dbff; + 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 { - background: #fafcff; + background: #fbfdff; } - .packet-viewer .ant-table-tbody > tr.packet-child-row > td:first-child { - border-left: 3px solid #d6e4ff; + .packet-viewer .ant-table-tbody > tr.packet-standalone-row > td { + background: #ffffff; + } + + .packet-viewer .ant-table-tbody > tr.packet-child-row > td { + border-top-color: #edf2fb; } .packet-viewer .ant-table-row-expand-icon { @@ -1355,6 +1389,102 @@ export default function PacketViewer(): ReactElement { 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 { background: ${token.colorFillTertiary}; } @@ -1464,7 +1594,13 @@ export default function PacketViewer(): ReactElement { ), }} - 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' + } /> )}