Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 统一换行符,保证跨平台构建产物字节一致。
# 背景:Windows 上 core.autocrlf=true 会把文本文件 checkout 成 CRLF,
# 前端构建(Vite)按内容哈希命名产物,换行符差异会导致同一文件哈希漂移
# (例如 workbuddy-mark SVG 在 Windows 构建后文件名变化)。
# 这里显式声明 eol=lf,优先级高于 core.autocrlf,任何平台 checkout 都是 LF。

# 默认:自动检测文本,仓库内与工作区统一 LF
* text=auto eol=lf

# 明确二进制类型,禁止任何换行符转换
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
*.otf binary
*.gz binary
*.tgz binary
*.zip binary
*.pdf binary
*.wasm binary

# SVG 是文本,保持 LF(与上面 * 规则一致,显式写出更直观)
*.svg text eol=lf

# Go / 脚本 / 配置类常见文本
*.go text eol=lf
*.mjs text eol=lf
*.js text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.css text eol=lf
*.html text eol=lf
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.sh text eol=lf

# Windows 批处理必须保持 CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
1 change: 1 addition & 0 deletions .opencode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
images/
7 changes: 7 additions & 0 deletions changelog/unreleased/traffic-chart-error-series.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
### English

- Fix the Overview traffic chart so the success and failure lines each plot their own counts instead of both tracking total requests, and make the failure line cover every non-success outcome (error, incomplete, canceled) so no traffic disappears from the curve.

### 中文

- 修复概览页流量折线图:成功与失败各自显示自己的计数,不再都跟着总请求数;失败折线覆盖全部未成功请求(失败、未完成、取消),曲线不再丢数据。
41 changes: 28 additions & 13 deletions frontend/src/components/overview/TrafficChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function niceMax(peak: number) {
return nice * base
}

type ChartRow = RequestStatsPoint & { okCount: number }
type ChartRow = RequestStatsPoint & { okCount: number; errorCount: number }

function TrafficTooltip({
active,
Expand Down Expand Up @@ -60,7 +60,8 @@ function TrafficTooltip({
</div>
<div className="flex items-center justify-between gap-6">
<span className="inline-flex items-center gap-1.5"><span className="size-1.5 rounded-full bg-danger" />{errorLabel}</span>
<span className="mono font-medium">{point.error}</span>
{/* 与红色折线同口径:未成功数(error + incomplete + canceled) */}
<span className="mono font-medium">{point.errorCount}</span>
</div>
<div className="flex items-center justify-between gap-6 border-t border-separator pt-1">
<span className="text-muted">total</span>
Expand All @@ -85,10 +86,23 @@ export function TrafficChart({
errorLabel: string
}) {
const rootRef = useRef<HTMLDivElement>(null)
const data = useMemo<ChartRow[]>(() => series.map((point) => ({
...point,
okCount: Math.max(0, point.ok ?? (point.requests - point.error)),
})), [series])
// 修复:成功/失败折线必须各自取自己的计数。
// 之前红色折线/面积用的是 requests(总请求数),与成功线在几乎无报错时重合,
// 看起来就像"成功和失败数据一模一样"。这里显式派生两个序列:
// - okCount: 成功数(后端 ok 字段;缺省时用 requests - error 兜底)
// - errorCount: 未成功数 = requests - okCount(含 error/incomplete/canceled)。
// 不能只用后端 error 字段:canceled(客户端断开)等状态会被顶掉,
// 导致 tooltip 出现 total 37 / 成功 1 / 失败 0、红线却贴底的缺口。
// 与概览卡片"成功率 = ok/total"的口径保持一致,红线垫平剩余部分。
const data = useMemo<ChartRow[]>(() => series.map((point) => {
const okCount = Math.max(0, point.ok ?? (point.requests - point.error))
const errorCount = Math.min(Math.max(0, point.requests - okCount), Math.max(0, point.requests))
return {
...point,
okCount,
errorCount,
}
}), [series])
const first = data[0]
const last = data[data.length - 1]
const span = first && last ? Date.parse(last.at) - Date.parse(first.at) : 0
Expand Down Expand Up @@ -200,25 +214,26 @@ export function TrafficChart({
cursor={{ stroke: 'var(--border)', strokeDasharray: '3 3' }}
content={<TrafficTooltip daily={daily} lang={lang} okLabel={okLabel} errorLabel={errorLabel} />}
/>
{/* 面积:先画成功(较大,作为底层),再画失败(叠加在底部,避免被完全遮住) */}
<Area
type="monotone"
dataKey="requests"
dataKey="okCount"
stroke="none"
fill="url(#traffic-error)"
fill="url(#traffic-ok)"
isAnimationActive={false}
activeDot={false}
dot={false}
className="traffic-error-area"
className="traffic-ok-area"
/>
<Area
type="monotone"
dataKey="okCount"
dataKey="errorCount"
stroke="none"
fill="url(#traffic-ok)"
fill="url(#traffic-error)"
isAnimationActive={false}
activeDot={false}
dot={false}
className="traffic-ok-area"
className="traffic-error-area"
/>
<Line
type="monotone"
Expand All @@ -232,7 +247,7 @@ export function TrafficChart({
/>
<Line
type="monotone"
dataKey="requests"
dataKey="errorCount"
stroke="var(--danger)"
strokeWidth={1.25}
strokeOpacity={0.7}
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions internal/webui/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet" />
<script type="module" crossorigin src="/assets/index-Cp1DNWCo.js"></script>
<script type="module" crossorigin src="/assets/index-BLZdbUud.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BEwWCUnU.css">
</head>
<body>
<div id="root"></div>
<div id="root"></div>
</body>
</html>