From 35207a70eafab74eb8053905658fb4b50c0f95d5 Mon Sep 17 00:00:00 2001 From: justme Date: Wed, 21 Oct 2026 16:31:22 +0800 Subject: [PATCH] web: group the line card rows by direction so each agent pair is named once --- web/display/src/LineStrips.tsx | 80 ++++++++++++---------------------- web/display/src/display.css | 42 ++++++++++-------- 2 files changed, 51 insertions(+), 71 deletions(-) diff --git a/web/display/src/LineStrips.tsx b/web/display/src/LineStrips.tsx index 2067878..d5f7ec6 100644 --- a/web/display/src/LineStrips.tsx +++ b/web/display/src/LineStrips.tsx @@ -62,68 +62,42 @@ export function LineStrips({ lineId, dialer, listener, range, m }: Props) { const lossDown = col('loss.down') const retransUp = col('tcp.retrans.up') const retransDown = col('tcp.retrans.down') - const up = m['loss.up'] - const down = m['loss.down'] - const upText = up === null || up === undefined ? '丢包 —' : `${fmtPct(up)} 丢包` - const downText = down === null || down === undefined ? '丢包 —' : `${fmtPct(down)} 丢包` - const retransUpValue = m['tcp.retrans.up'] - const retransDownValue = m['tcp.retrans.down'] + const lossText = (v: number | null | undefined) => (v === null || v === undefined ? '丢包 —' : `${fmtPct(v)} 丢包`) const retransText = (v: number | null | undefined) => (v === null || v === undefined ? '重传 —' : `${fmtPct(v)} 重传`) + const cellsOf = (values: (number | null)[]) => (s ? values : Array.from({ length: common.cells }, () => null)) + // 一个方向下的一行:协议名、数值徽标和质量点阵。 + const row = (id: string, proto: string, label: string, value: number | null | undefined, text: string, values: (number | null)[]) => ( +
+
+ {proto} + {text} +
+
+ {cellsOf(values).map((v, i) => ( + + ))} +
+
+ ) return (
setHover(null)}>
-
- QUIC RTT ⇄{fmtMs(m['rtt.p50'])}悬停看 p95 / p99 +
+ RTT ⇄ + QUIC {fmtMs(m['rtt.p50'])} + TCP {fmtMs(m['tcp.rtt.p50'])} + 悬停看 p95 / p99 {series.error ? 数据未更新 : null}
-
- {dialer} → {listener} - {upText} -
-
- {(s ? lossUp : Array.from({ length: common.cells }, () => null)).map((value, i) => ( - - ))} -
+
{dialer} → {listener}
+ {row('up-quic', 'QUIC', '去程丢包', m['loss.up'], lossText(m['loss.up']), lossUp)} + {row('up-tcp', 'TCP', '去程重传', m['tcp.retrans.up'], retransText(m['tcp.retrans.up']), retransUp)}
-
- {listener} → {dialer} - {downText} -
-
- {(s ? lossDown : Array.from({ length: common.cells }, () => null)).map((value, i) => ( - - ))} -
-
-
-
- TCP RTT ⇄{fmtMs(m['tcp.rtt.p50'])} -
-
-
-
- {dialer} → {listener} - {retransText(retransUpValue)} -
-
- {(s ? retransUp : Array.from({ length: common.cells }, () => null)).map((value, i) => ( - - ))} -
-
-
-
- {listener} → {dialer} - {retransText(retransDownValue)} -
-
- {(s ? retransDown : Array.from({ length: common.cells }, () => null)).map((value, i) => ( - - ))} -
+
{listener} → {dialer}
+ {row('down-quic', 'QUIC', '回程丢包', m['loss.down'], lossText(m['loss.down']), lossDown)} + {row('down-tcp', 'TCP', '回程重传', m['tcp.retrans.down'], retransText(m['tcp.retrans.down']), retransDown)}
diff --git a/web/display/src/display.css b/web/display/src/display.css index 440fcb4..46ab000 100644 --- a/web/display/src/display.css +++ b/web/display/src/display.css @@ -1388,15 +1388,13 @@ } .line-mini-quic-head > span, -.line-mini-direction-head > span:first-child, -.line-mini-tcp-head > span:first-child { +.line-mini-direction-head > span:first-child { color: var(--app-ink-muted); font-size: 10px; font-weight: 600; } -.line-mini-quic-head > strong, -.line-mini-tcp-head > strong { +.line-mini-quic-head strong { color: var(--app-ink); font-size: 14px; font-weight: 700; @@ -1410,15 +1408,13 @@ white-space: nowrap; } -.line-mini-direction, -.line-mini-tcp { +.line-mini-direction { display: grid; gap: 3px; min-width: 0; } -.line-mini-direction-head, -.line-mini-tcp-head { +.line-mini-direction-head { display: flex; align-items: baseline; gap: 6px; @@ -1426,33 +1422,43 @@ line-height: 1.25; } -.line-mini-direction-head > span:first-child, -.line-mini-tcp-head > span:first-child { +.line-mini-direction-head > span:first-child { font-weight: 500; white-space: nowrap; } -.line-mini-direction-head .line-mini-badge, -.line-mini-tcp-head .line-mini-badge { +.line-mini-direction-head .line-mini-badge { margin-left: auto; } -.line-mini-direction-head > .line-mini-direction-name { +.line-mini-route { min-width: 0; overflow: hidden; + color: var(--app-ink); + font-size: 11px; + font-weight: 600; text-overflow: ellipsis; + white-space: nowrap; } -.line-mini-tcp { +.line-mini-row { + display: grid; + gap: 3px; + min-width: 0; +} + +.line-mini-rtt { + display: inline-flex; + align-items: baseline; + gap: 4px; +} + +.line-mini-direction + .line-mini-direction { margin-top: 2px; padding-top: 5px; border-top: 1px solid var(--app-outline-soft); } -.line-mini-tcp-head > strong { - font-size: 12px; -} - .line-mini-badge { display: inline-flex; align-items: center;