niceeval 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/INDEX.md +55 -0
  2. package/dist/agents/types.d.ts +317 -0
  3. package/dist/context/types.d.ts +335 -0
  4. package/dist/i18n/core.d.ts +11 -0
  5. package/dist/i18n/en.d.ts +181 -0
  6. package/dist/i18n/index.d.ts +5 -0
  7. package/dist/i18n/zh-CN.d.ts +183 -0
  8. package/dist/o11y/execution-tree.d.ts +103 -0
  9. package/dist/o11y/otlp/parse.d.ts +3 -0
  10. package/dist/o11y/otlp/receiver.d.ts +21 -0
  11. package/dist/o11y/otlp/select.d.ts +22 -0
  12. package/dist/o11y/otlp/turn-otel.d.ts +43 -0
  13. package/dist/o11y/types.d.ts +190 -0
  14. package/dist/report/aggregate.d.ts +57 -0
  15. package/dist/report/aggregate.js +212 -0
  16. package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +1 -0
  17. package/dist/report/built-ins/cost-pass-rate-comparison.js +17 -0
  18. package/dist/report/built-ins/index.d.ts +1 -0
  19. package/dist/report/built-ins/index.js +2 -0
  20. package/dist/report/components.d.ts +178 -0
  21. package/dist/report/components.js +132 -0
  22. package/dist/report/compute.d.ts +106 -0
  23. package/dist/report/compute.js +618 -0
  24. package/dist/report/flag.d.ts +6 -0
  25. package/dist/report/flag.js +8 -0
  26. package/dist/report/format.d.ts +32 -0
  27. package/dist/report/format.js +126 -0
  28. package/dist/report/index.d.ts +19 -0
  29. package/dist/report/index.js +30 -0
  30. package/dist/report/load.d.ts +12 -0
  31. package/dist/report/load.js +56 -0
  32. package/dist/report/locale.d.ts +83 -0
  33. package/dist/report/locale.js +160 -0
  34. package/dist/report/metrics.d.ts +21 -0
  35. package/dist/report/metrics.js +115 -0
  36. package/dist/report/primitives.d.ts +64 -0
  37. package/dist/report/primitives.js +113 -0
  38. package/dist/report/react/AttemptList.d.ts +23 -0
  39. package/dist/report/react/AttemptList.js +20 -0
  40. package/dist/report/react/DeltaTable.d.ts +8 -0
  41. package/dist/report/react/DeltaTable.js +24 -0
  42. package/dist/report/react/EvalList.d.ts +10 -0
  43. package/dist/report/react/EvalList.js +0 -0
  44. package/dist/report/react/ExperimentList.d.ts +10 -0
  45. package/dist/report/react/ExperimentList.js +15 -0
  46. package/dist/report/react/GroupSummary.d.ts +8 -0
  47. package/dist/report/react/GroupSummary.js +8 -0
  48. package/dist/report/react/MetricBars.d.ts +10 -0
  49. package/dist/report/react/MetricBars.js +48 -0
  50. package/dist/report/react/MetricLine.d.ts +9 -0
  51. package/dist/report/react/MetricLine.js +68 -0
  52. package/dist/report/react/MetricMatrix.d.ts +10 -0
  53. package/dist/report/react/MetricMatrix.js +23 -0
  54. package/dist/report/react/MetricScatter.d.ts +9 -0
  55. package/dist/report/react/MetricScatter.js +100 -0
  56. package/dist/report/react/MetricTable.d.ts +11 -0
  57. package/dist/report/react/MetricTable.js +26 -0
  58. package/dist/report/react/RunOverview.d.ts +8 -0
  59. package/dist/report/react/RunOverview.js +12 -0
  60. package/dist/report/react/Scoreboard.d.ts +8 -0
  61. package/dist/report/react/Scoreboard.js +26 -0
  62. package/dist/report/react/cell.d.ts +9 -0
  63. package/dist/report/react/cell.js +9 -0
  64. package/dist/report/react/chart-math.d.ts +14 -0
  65. package/dist/report/react/chart-math.js +68 -0
  66. package/dist/report/react/colors.d.ts +14 -0
  67. package/dist/report/react/colors.js +45 -0
  68. package/dist/report/react/fixtures.d.ts +19 -0
  69. package/dist/report/react/fixtures.js +393 -0
  70. package/dist/report/react/format.d.ts +3 -0
  71. package/dist/report/react/format.js +7 -0
  72. package/dist/report/react/index.d.ts +6 -0
  73. package/dist/report/react/index.js +14 -0
  74. package/dist/report/report.d.ts +24 -0
  75. package/dist/report/report.js +49 -0
  76. package/dist/report/text/faces.d.ts +29 -0
  77. package/dist/report/text/faces.js +503 -0
  78. package/dist/report/text/layout.d.ts +75 -0
  79. package/dist/report/text/layout.js +173 -0
  80. package/dist/report/text/plot.d.ts +37 -0
  81. package/dist/report/text/plot.js +81 -0
  82. package/dist/report/text/table.d.ts +8 -0
  83. package/dist/report/text/table.js +86 -0
  84. package/dist/report/tree.d.ts +99 -0
  85. package/dist/report/tree.js +228 -0
  86. package/dist/report/types.d.ts +420 -0
  87. package/dist/report/types.js +4 -0
  88. package/dist/report/web.d.ts +15 -0
  89. package/dist/report/web.js +37 -0
  90. package/dist/results/annotated-source.d.ts +61 -0
  91. package/dist/results/attempt-evidence.d.ts +69 -0
  92. package/dist/results/attempt-source.d.ts +15 -0
  93. package/dist/results/locator.d.ts +75 -0
  94. package/dist/results/locator.js +0 -0
  95. package/dist/results/select.d.ts +45 -0
  96. package/dist/results/select.js +230 -0
  97. package/dist/results/source-hash.d.ts +9 -0
  98. package/dist/results/types.d.ts +204 -0
  99. package/dist/runner/eval-source.d.ts +22 -0
  100. package/dist/runner/fingerprint.d.ts +23 -0
  101. package/dist/runner/types.d.ts +332 -0
  102. package/dist/sandbox/docker-stream.d.ts +24 -0
  103. package/dist/sandbox/docker.d.ts +84 -0
  104. package/dist/sandbox/e2b.d.ts +31 -0
  105. package/dist/sandbox/errors.d.ts +4 -0
  106. package/dist/sandbox/local-files.d.ts +6 -0
  107. package/dist/sandbox/paths.d.ts +4 -0
  108. package/dist/sandbox/registry.d.ts +13 -0
  109. package/dist/sandbox/resolve.d.ts +40 -0
  110. package/dist/sandbox/retry.d.ts +10 -0
  111. package/dist/sandbox/shell.d.ts +11 -0
  112. package/dist/sandbox/source-files.d.ts +13 -0
  113. package/dist/sandbox/types.d.ts +203 -0
  114. package/dist/sandbox/vercel.d.ts +37 -0
  115. package/dist/scoring/types.d.ts +70 -0
  116. package/dist/shared/aggregate.d.ts +29 -0
  117. package/dist/shared/aggregate.js +55 -0
  118. package/dist/shared/types.d.ts +27 -0
  119. package/dist/shared/verdict.d.ts +27 -0
  120. package/dist/shared/verdict.js +34 -0
  121. package/dist/tty-line.d.ts +6 -0
  122. package/dist/types.d.ts +7 -0
  123. package/dist/util.d.ts +22 -0
  124. package/docs-site/zh/concepts/adapter.mdx +9 -4
  125. package/docs-site/zh/concepts/assert.mdx +8 -8
  126. package/docs-site/zh/concepts/evals.mdx +5 -5
  127. package/docs-site/zh/concepts/experiment.mdx +3 -1
  128. package/docs-site/zh/concepts/judge.mdx +9 -9
  129. package/docs-site/zh/concepts/overview.mdx +2 -2
  130. package/docs-site/zh/concepts/tier.mdx +6 -6
  131. package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
  132. package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
  133. package/docs-site/zh/guides/agent-feedback-loop.mdx +235 -78
  134. package/docs-site/zh/guides/connect-otel.mdx +3 -3
  135. package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
  136. package/docs-site/zh/guides/custom-reports.mdx +196 -36
  137. package/docs-site/zh/guides/experiments.mdx +7 -0
  138. package/docs-site/zh/guides/fixtures.mdx +1 -1
  139. package/docs-site/zh/guides/official-adapters.mdx +31 -9
  140. package/docs-site/zh/guides/publish-report.mdx +4 -4
  141. package/docs-site/zh/guides/report-components.mdx +194 -55
  142. package/docs-site/zh/guides/reporters.mdx +2 -2
  143. package/docs-site/zh/guides/results-data.mdx +35 -34
  144. package/docs-site/zh/guides/runner.mdx +2 -2
  145. package/docs-site/zh/guides/sandbox-agent.mdx +10 -5
  146. package/docs-site/zh/guides/sandbox-providers.mdx +102 -0
  147. package/docs-site/zh/guides/viewing-results.mdx +199 -79
  148. package/docs-site/zh/guides/write-experiment.mdx +23 -1
  149. package/docs-site/zh/guides/write-send.mdx +2 -2
  150. package/docs-site/zh/index.mdx +4 -3
  151. package/docs-site/zh/introduction.mdx +2 -1
  152. package/docs-site/zh/quickstart.mdx +5 -3
  153. package/docs-site/zh/reference/builtin-agents.mdx +38 -13
  154. package/docs-site/zh/reference/capabilities.mdx +1 -1
  155. package/docs-site/zh/reference/cli.mdx +36 -13
  156. package/docs-site/zh/reference/define-agent.mdx +24 -12
  157. package/docs-site/zh/reference/define-config.mdx +1 -1
  158. package/docs-site/zh/reference/define-eval.mdx +3 -3
  159. package/docs-site/zh/reference/events.mdx +7 -1
  160. package/docs-site/zh/reference/expect.mdx +3 -3
  161. package/package.json +10 -6
  162. package/src/agents/bub.ts +134 -43
  163. package/src/agents/claude-code.test.ts +152 -0
  164. package/src/agents/claude-code.ts +127 -13
  165. package/src/agents/codex.test.ts +192 -0
  166. package/src/agents/codex.ts +151 -12
  167. package/src/agents/index.ts +11 -3
  168. package/src/agents/manifest.ts +41 -0
  169. package/src/agents/plugin-config.test.ts +94 -0
  170. package/src/agents/shared.ts +33 -2
  171. package/src/agents/skills.test.ts +214 -0
  172. package/src/agents/skills.ts +304 -0
  173. package/src/agents/streaming.ts +5 -5
  174. package/src/agents/types.ts +62 -2
  175. package/src/cli.ts +74 -47
  176. package/src/context/context.test.ts +20 -2
  177. package/src/context/context.ts +3 -0
  178. package/src/context/control-flow.ts +1 -1
  179. package/src/context/session.test.ts +92 -1
  180. package/src/context/session.ts +26 -1
  181. package/src/context/types.ts +4 -4
  182. package/src/define.test.ts +82 -0
  183. package/src/define.ts +72 -11
  184. package/src/expect/index.ts +5 -5
  185. package/src/i18n/en.ts +36 -15
  186. package/src/i18n/zh-CN.ts +39 -21
  187. package/src/index.ts +1 -1
  188. package/src/o11y/execution-tree.test.ts +452 -0
  189. package/src/o11y/execution-tree.ts +367 -0
  190. package/src/o11y/otlp/receiver.ts +1 -1
  191. package/src/o11y/otlp/select.ts +7 -3
  192. package/src/o11y/parsers/claude-code.test.ts +175 -0
  193. package/src/o11y/parsers/claude-code.ts +32 -0
  194. package/src/o11y/types.ts +6 -1
  195. package/src/report/aggregate.ts +26 -20
  196. package/src/report/built-in-user-parity.test.tsx +643 -0
  197. package/src/report/built-ins/cost-pass-rate-comparison.tsx +23 -0
  198. package/src/report/built-ins/index.ts +2 -0
  199. package/src/report/components.tsx +223 -50
  200. package/src/report/compute.ts +358 -53
  201. package/src/report/dual-render.test.tsx +1125 -0
  202. package/src/report/flag.ts +1 -1
  203. package/src/report/format.ts +50 -2
  204. package/src/report/index.ts +48 -14
  205. package/src/report/load.ts +1 -1
  206. package/src/report/locale.ts +206 -0
  207. package/src/report/metrics.ts +39 -15
  208. package/src/report/primitives.tsx +100 -2
  209. package/src/report/react/AttemptList.tsx +115 -0
  210. package/src/report/react/DeltaTable.tsx +9 -6
  211. package/src/report/react/EvalList.tsx +0 -0
  212. package/src/report/react/ExperimentList.tsx +108 -0
  213. package/src/report/react/GroupSummary.tsx +66 -0
  214. package/src/report/react/MetricBars.tsx +24 -13
  215. package/src/report/react/MetricLine.tsx +21 -21
  216. package/src/report/react/MetricMatrix.tsx +8 -4
  217. package/src/report/react/MetricScatter.tsx +136 -96
  218. package/src/report/react/MetricTable.tsx +115 -15
  219. package/src/report/react/RunOverview.tsx +48 -26
  220. package/src/report/react/Scoreboard.tsx +15 -7
  221. package/src/report/react/cell.tsx +18 -9
  222. package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
  223. package/src/report/react/colors.ts +24 -15
  224. package/src/report/react/enhance.js +129 -0
  225. package/src/report/react/fixtures.ts +163 -34
  226. package/src/report/react/format.ts +1 -1
  227. package/src/report/react/index.tsx +22 -6
  228. package/src/report/react/render.test.tsx +164 -31
  229. package/src/report/react/styles.css +405 -213
  230. package/src/report/report.test.ts +403 -94
  231. package/src/report/report.ts +29 -11
  232. package/src/report/text/faces.ts +303 -95
  233. package/src/report/text/layout.ts +76 -11
  234. package/src/report/text/table.ts +98 -0
  235. package/src/report/tree.ts +103 -20
  236. package/src/report/types.ts +196 -25
  237. package/src/report/web.ts +46 -19
  238. package/src/results/annotated-source.test.ts +146 -0
  239. package/src/results/annotated-source.ts +128 -0
  240. package/src/results/attempt-evidence.test.ts +158 -0
  241. package/src/results/attempt-evidence.ts +158 -0
  242. package/src/results/attempt-source.ts +48 -0
  243. package/src/results/copy.ts +154 -139
  244. package/src/results/format.ts +54 -47
  245. package/src/results/host-equivalence.test.ts +527 -0
  246. package/src/results/index.ts +56 -14
  247. package/src/results/locator.test.ts +198 -0
  248. package/src/results/locator.ts +0 -0
  249. package/src/results/open.ts +392 -175
  250. package/src/results/results.test.ts +943 -494
  251. package/src/results/select.ts +129 -23
  252. package/src/results/skipped-notice.ts +0 -0
  253. package/src/results/source-hash.ts +28 -0
  254. package/src/results/types.ts +98 -62
  255. package/src/results/writer.ts +304 -176
  256. package/src/runner/attempt.test.ts +170 -0
  257. package/src/runner/attempt.ts +97 -15
  258. package/src/runner/discover.test.ts +65 -0
  259. package/src/runner/discover.ts +6 -2
  260. package/src/runner/eval-source.test.ts +121 -0
  261. package/src/runner/eval-source.ts +45 -0
  262. package/src/runner/fingerprint.ts +54 -1
  263. package/src/runner/remote-sandbox.ts +1 -1
  264. package/src/runner/report.test.ts +6 -8
  265. package/src/runner/report.ts +5 -4
  266. package/src/runner/reporters/artifacts.ts +27 -41
  267. package/src/runner/reporters/braintrust.test.ts +3 -3
  268. package/src/runner/reporters/braintrust.ts +1 -1
  269. package/src/runner/reporters/console.ts +6 -6
  270. package/src/runner/reporters/json.ts +4 -4
  271. package/src/runner/reporters/live.test.ts +56 -0
  272. package/src/runner/reporters/live.ts +36 -10
  273. package/src/runner/reporters/quiet.test.ts +66 -0
  274. package/src/runner/reporters/quiet.ts +49 -0
  275. package/src/runner/reporters/shared.ts +5 -5
  276. package/src/runner/reporters/table.ts +17 -17
  277. package/src/runner/run.ts +61 -102
  278. package/src/runner/types.ts +43 -32
  279. package/src/sandbox/checkpoint.ts +1 -1
  280. package/src/sandbox/docker.ts +15 -2
  281. package/src/sandbox/e2b.ts +11 -5
  282. package/src/sandbox/errors.ts +13 -0
  283. package/src/sandbox/index.ts +3 -3
  284. package/src/sandbox/local-files.ts +1 -1
  285. package/src/sandbox/registry.ts +6 -5
  286. package/src/sandbox/resolve.ts +38 -23
  287. package/src/sandbox/retry.test.ts +68 -0
  288. package/src/sandbox/retry.ts +44 -0
  289. package/src/sandbox/types.ts +69 -28
  290. package/src/sandbox/vercel.ts +17 -3
  291. package/src/scoring/collector.ts +3 -3
  292. package/src/scoring/judge.ts +2 -2
  293. package/src/scoring/scoped.ts +13 -1
  294. package/src/scoring/types.ts +7 -7
  295. package/src/scoring/verdict.ts +4 -4
  296. package/src/shared/aggregate.ts +23 -3
  297. package/src/shared/types.ts +2 -2
  298. package/src/shared/{outcome.ts → verdict.ts} +15 -15
  299. package/src/show/compose.ts +12 -132
  300. package/src/show/index.ts +125 -94
  301. package/src/show/render.ts +660 -160
  302. package/src/show/show.test.ts +472 -253
  303. package/src/source-loc.ts +1 -1
  304. package/src/tty-line.ts +32 -0
  305. package/src/types.ts +2 -2
  306. package/src/util.test.ts +21 -1
  307. package/src/util.ts +5 -1
  308. package/src/view/app/App.tsx +33 -198
  309. package/src/view/app/components/AttemptModal.tsx +6 -6
  310. package/src/view/app/components/CodeView.tsx +2 -2
  311. package/src/view/app/components/CopyControls.tsx +32 -36
  312. package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
  313. package/src/view/app/components/ui/badge.tsx +1 -1
  314. package/src/view/app/components/ui/dialog.tsx +2 -2
  315. package/src/view/app/i18n.ts +40 -31
  316. package/src/view/app/lib/artifact-url.ts +1 -1
  317. package/src/view/app/lib/attempt-route.test.ts +37 -47
  318. package/src/view/app/lib/attempt-route.ts +26 -26
  319. package/src/view/app/lib/rows.ts +9 -112
  320. package/src/view/app/lib/verdict.ts +25 -0
  321. package/src/view/app/main.tsx +9 -12
  322. package/src/view/app/pages/RunsPage.tsx +4 -4
  323. package/src/view/app/pages/TracesPage.tsx +3 -3
  324. package/src/view/app/shared.ts +0 -1
  325. package/src/view/app/types.ts +6 -51
  326. package/src/view/artifact-serving.test.ts +136 -0
  327. package/src/view/client-dist/app.css +1 -1
  328. package/src/view/client-dist/app.js +21 -21
  329. package/src/view/data.test.ts +214 -143
  330. package/src/view/data.ts +166 -120
  331. package/src/view/index.ts +23 -11
  332. package/src/view/server.ts +52 -17
  333. package/src/view/shared/types.ts +22 -31
  334. package/src/view/styles.css +117 -327
  335. package/src/view/view-report.test.ts +146 -98
  336. package/docs-site/zh/guides/sandbox-backends.mdx +0 -75
  337. package/src/report/default-report.tsx +0 -222
  338. package/src/report/dual-face.test.tsx +0 -527
  339. package/src/report/react/CaseList.tsx +0 -70
  340. package/src/view/app/components/CostScoreChart.tsx +0 -147
  341. package/src/view/app/components/ExperimentTable.tsx +0 -284
  342. package/src/view/app/components/GroupSelector.tsx +0 -61
  343. package/src/view/app/components/primitives.tsx +0 -43
  344. package/src/view/app/lib/outcome.ts +0 -82
@@ -1,18 +1,21 @@
1
1
  // Scoreboard:考试成绩单——总分 + 分科小计。
2
2
  // 固定分母的口径不藏:没跑的题挣 0 分但留在分母里,科目行如实报 missing;
3
- // weights 是「实际生效的权重表」,渲染出来让成绩单可审计(docs/reports.md scoreboard 公式一节)。
3
+ // weights 是「实际生效的权重表」,渲染出来让成绩单可审计(docs/feature/reports/library.md「Scoreboard」)。
4
4
 
5
5
  import type { ReactElement } from "react";
6
6
  import type { ScoreboardData } from "../types.ts";
7
+ import { DEFAULT_REPORT_LOCALE, countText, localeText, type ReportLocale } from "../locale.ts";
7
8
  import { colorClassForKey } from "./colors.ts";
8
9
  import { cx } from "./format.ts";
9
10
 
10
11
  export function Scoreboard({
11
12
  data,
12
13
  className,
14
+ locale = DEFAULT_REPORT_LOCALE,
13
15
  }: {
14
16
  data: ScoreboardData;
15
17
  className?: string;
18
+ locale?: ReportLocale;
16
19
  }): ReactElement {
17
20
  // 科目列 = 各行 subjects 的并集,按首次出现顺序;固定分母下各行本应一致,这里防御性合并
18
21
  const subjectKeys: string[] = [];
@@ -31,7 +34,8 @@ export function Scoreboard({
31
34
  {data.dimension}
32
35
  </th>
33
36
  <th scope="col" className="nre-total-col">
34
- Total<span className="nre-full-marks">/ {data.fullMarks}</span>
37
+ {localeText(locale, "scoreboard.total")}
38
+ <span className="nre-full-marks">/ {data.fullMarks}</span>
35
39
  </th>
36
40
  {subjectKeys.map((key) => (
37
41
  <th scope="col" key={key} className="nre-subject-col">
@@ -55,13 +59,17 @@ export function Scoreboard({
55
59
  <td key={key} className="nre-subject">
56
60
  <span
57
61
  className="nre-subject-score"
58
- title={`${subject.evals} evals, weighted ${subject.earned} of ${subject.possible}`}
62
+ title={localeText(locale, "scoreboard.subjectTitle", {
63
+ evals: subject.evals,
64
+ earned: subject.earned,
65
+ possible: subject.possible,
66
+ })}
59
67
  >
60
68
  {subject.earned}/{subject.possible}
61
69
  </span>
62
70
  {/* 固定分母的如实注脚:没跑、按 0 计的题数 */}
63
71
  {subject.missing > 0 && (
64
- <span className="nre-subject-missing">{subject.missing} {subject.missing === 1 ? "eval" : "evals"} missing, scored 0</span>
72
+ <span className="nre-subject-missing">{countText(locale, "scoreboard.missing", subject.missing)}</span>
65
73
  )}
66
74
  </td>
67
75
  );
@@ -72,15 +80,15 @@ export function Scoreboard({
72
80
  </table>
73
81
  {/* 实际生效的权重表:成绩单可审计 */}
74
82
  <p className="nre-weights">
75
- weights:{" "}
83
+ {localeText(locale, "scoreboard.weights")}{" "}
76
84
  {data.weights.length === 0
77
- ? "all evals ×1"
85
+ ? localeText(locale, "scoreboard.allWeights")
78
86
  : data.weights.map((w) => (
79
87
  <span key={w.prefix} className="nre-weight">
80
88
  {w.prefix} ×{w.weight}
81
89
  </span>
82
90
  ))}
83
- {data.weights.length > 0 && <span className="nre-weight-rest">others ×1</span>}
91
+ {data.weights.length > 0 && <span className="nre-weight-rest">{localeText(locale, "scoreboard.othersWeight")}</span>}
84
92
  </p>
85
93
  </section>
86
94
  );
@@ -3,42 +3,51 @@
3
3
  // 纯渲染、零 hooks;交互只有普通 <a>(下钻由使用者的 attemptHref 决定去处)。
4
4
 
5
5
  import type { ReactElement } from "react";
6
- import type { AttemptRef, MetricCell } from "../types.ts";
7
- import { MISSING_TEXT } from "./format.ts";
6
+ import type { MetricCell } from "../types.ts";
7
+ import type { AttemptLocator } from "../../results/locator.ts";
8
+ import { DEFAULT_REPORT_LOCALE, localeText, type ReportLocale } from "../locale.ts";
8
9
 
9
10
  export function MetricCellView({
10
11
  cell,
11
12
  attemptHref,
13
+ locale = DEFAULT_REPORT_LOCALE,
12
14
  }: {
13
15
  cell: MetricCell;
14
- attemptHref?: (ref: AttemptRef) => string;
16
+ attemptHref?: (locator: AttemptLocator) => string;
17
+ locale?: ReportLocale;
15
18
  }): ReactElement {
16
19
  // 全 null(没有任何有效样本)→ 缺数据文案,绝不画 0;total 仍如实入 title
17
20
  if (cell.value === null) {
18
21
  return (
19
22
  <span className="nre-cell nre-cell-missing">
20
- <span className="nre-missing" title={`0/${cell.total} attempts measurable`}>
21
- {MISSING_TEXT}
23
+ <span className="nre-missing" title={localeText(locale, "cell.noneMeasurableTitle", { total: cell.total })}>
24
+ {localeText(locale, "cell.missing")}
22
25
  </span>
23
26
  </span>
24
27
  );
25
28
  }
26
29
  return (
27
30
  <span className="nre-cell">
28
- <span className="nre-value" title={`${cell.samples}/${cell.total} attempts measured`}>
31
+ <span
32
+ className="nre-value"
33
+ title={localeText(locale, "cell.measuredTitle", { samples: cell.samples, total: cell.total })}
34
+ >
29
35
  {cell.display}
30
36
  </span>
31
37
  {/* samples < total:有 attempt 测不了这个指标,覆盖率角标如实标出 */}
32
38
  {cell.samples < cell.total && (
33
- <sup className="nre-coverage" title={`coverage ${cell.samples}/${cell.total}: this metric is null for the remaining attempts`}>
39
+ <sup
40
+ className="nre-coverage"
41
+ title={localeText(locale, "cell.coverageTitle", { samples: cell.samples, total: cell.total })}
42
+ >
34
43
  {cell.samples}/{cell.total}
35
44
  </sup>
36
45
  )}
37
46
  {/* refs + attemptHref:格子可点,「给我看那次 attempt」就在手边 */}
38
47
  {attemptHref && cell.refs && cell.refs.length > 0 && (
39
48
  <span className="nre-refs">
40
- {cell.refs.map((ref, i) => (
41
- <a key={`${ref.run}:${ref.result}`} className="nre-ref" href={attemptHref(ref)}>
49
+ {cell.refs.map((locator, i) => (
50
+ <a key={locator} className="nre-ref" href={attemptHref(locator)}>
42
51
  #{i + 1}
43
52
  </a>
44
53
  ))}
@@ -1,4 +1,5 @@
1
- // 通用图表数值工具:round-number 刻度生成,无第三方依赖(遵循 view/ 不引图表库的现状)。
1
+ // 通用图表数值工具:round-number 刻度生成与标签防撞布局,零依赖纯函数。
2
+ // 从 src/view/app/lib/chart.ts 迁来(view 侧从这里 re-export),报告组件与 view 共用同一份。
2
3
 
3
4
  function niceNumber(range: number, round: boolean): number {
4
5
  if (range <= 0) return 1;
@@ -34,11 +35,6 @@ export function niceTicks(min: number, max: number, count = 5): number[] {
34
35
  return ticks;
35
36
  }
36
37
 
37
- /** 六色分类色板之外的第 7+ 个系列一律回退到中性色,避免同色误导身份(见 dataviz 分类色板规则)。 */
38
- export function seriesColor(index: number): string {
39
- return index < 6 ? `var(--series-${index + 1})` : "var(--muted)";
40
- }
41
-
42
38
  export interface LabelInput {
43
39
  cx: number;
44
40
  cy: number;
@@ -1,21 +1,22 @@
1
- // 跨块配色一致(docs/reports.md 四条跨组件契约之二):
1
+ // 跨块配色一致(docs/feature/reports/architecture.md「静态网页」):
2
2
  // 系列/维度键 → 固定调色板下标,用稳定散列而不是「按出现顺序分配」。
3
3
  // 这样同一个 agent 在 scatter 的线、DeltaTable 的行、matrix 的列头永远同色,
4
4
  // 不需要 Provider、不需要手工配置,甚至不需要两个组件见过同一份数据。
5
- // 调色板的十六进制值以这里为准:SVG(scatter 的线和点)直接内联 hex,
6
- // 保证漏加载 styles.css 时图形仍然分得清系列;styles.css 里的 --nre-c0..c7
7
- // 是同一组值的拷贝(供 .nre-cN 文本类用),改色时两边一起改。
5
+ //
6
+ // 色板与 view 统一:六色 CVD 校验色板(src/view/styles.css --series-1..6),
7
+ // 顺序不要重排。深浅两套值都住在 styles.css 的 --nre-c0..c5(light-dark()),
8
+ // 「CSS 与 colors.ts 逐个对应」契约:NRE_PALETTE 是浅色主题那套值的拷贝,
9
+ // 渲染面优先挂类名(nre-cN 上文字色、nre-series-cN 上图形系列色)由 CSS 上色,
10
+ // 深色主题才能跟随;colorHexForKey 只留给「不经 CSS」的消费方。改色时两边一起改。
8
11
 
9
- /** 固定调色板;下标即 nre-cN 的 N,与 styles.css 的 --nre-cN 逐个对应。 */
12
+ /** 固定调色板(浅色主题值);下标即 nre-cN / nre-series-cN 的 N,与 styles.css 的 --nre-cN 逐个对应。 */
10
13
  export const NRE_PALETTE = [
11
- "#2563eb", // c0 蓝
12
- "#db2777", // c1 玫红
13
- "#059669", // c2 绿
14
- "#d97706", // c3
15
- "#7c3aed", // c4
16
- "#0891b2", // c5
17
- "#dc2626", // c6 红
18
- "#65a30d", // c7 橄榄
14
+ "#2a78d6", // c0 蓝(dark: #3987e5)
15
+ "#1baf7a", // c1 绿(dark: #199e70)
16
+ "#eda100", // c2 琥珀(dark: #c98500)
17
+ "#008300", // c3 深绿(dark: #008300)
18
+ "#e34948", // c4 红(dark: #e66767)
19
+ "#eb6834", // c5 橙(dark: #d95926)
19
20
  ] as const;
20
21
 
21
22
  export const NRE_PALETTE_SIZE = NRE_PALETTE.length;
@@ -31,12 +32,20 @@ export function colorIndexForKey(key: string): number {
31
32
  return hash % NRE_PALETTE_SIZE;
32
33
  }
33
34
 
34
- /** 键对应的稳定 class 名("nre-c3"),配 styles.css 的 .nre-cN 上色。 */
35
+ /** 键对应的稳定 class 名("nre-c3"),配 styles.css 的 .nre-cN 上文字色。 */
35
36
  export function colorClassForKey(key: string): string {
36
37
  return `nre-c${colorIndexForKey(key)}`;
37
38
  }
38
39
 
39
- /** 键对应的十六进制颜色,给 SVG 内联 stroke/fill 用(不依赖 CSS 加载)。 */
40
+ /**
41
+ * 键对应的系列 class 名("nre-series-c3"):挂在 SVG 图形元素(线/点/柱/系列名)上,
42
+ * styles.css 用它设置 --nre-series,fill/stroke 走 var —— 深色主题下图表随之切换。
43
+ */
44
+ export function seriesClassForKey(key: string): string {
45
+ return `nre-series-c${colorIndexForKey(key)}`;
46
+ }
47
+
48
+ /** 键对应的十六进制颜色(浅色主题值);渲染面优先类名,这里只留给不经 CSS 的消费方。 */
40
49
  export function colorHexForKey(key: string): string {
41
50
  return NRE_PALETTE[colorIndexForKey(key)];
42
51
  }
@@ -0,0 +1,129 @@
1
+ // niceeval 报告的渐进增强 runtime:纯 vanilla JS、零依赖、IIFE、幂等。
2
+ // 只作用于 .nre DOM 与 data-nre-* 属性;三个行为——表格排序、行过滤、SVG 点 tooltip。
3
+ // 静态 HTML 无 JS 时内容完整可读是硬约束:排序有数据侧预排、tooltip 退化为原生
4
+ // <title>、过滤输入框静默无功能。全部经 document 级事件委托绑定,重复注入本文件
5
+ // 只在首次生效(window.__nreEnhanced 守卫),DOM 被搬动(如 view 把 <template> 内容
6
+ // 摆进报告槽)也无需重新绑定。
7
+
8
+ (function () {
9
+ "use strict";
10
+ if (typeof window === "undefined" || window.__nreEnhanced) return;
11
+ window.__nreEnhanced = true;
12
+
13
+ function closest(target, selector) {
14
+ return target && target.closest ? target.closest(selector) : null;
15
+ }
16
+
17
+ // ───────────────────────── 排序:th[data-nre-sort] ─────────────────────────
18
+ // 点击按该列排序 tbody 行(td/th 的 data-sort-value,数值优先、退回字符串;
19
+ // 空值恒沉底),再点反向;方向指示由 th 上的 nre-sort-asc / nre-sort-desc 类驱动。
20
+
21
+ function sortValue(row, index) {
22
+ var cell = row.cells[index];
23
+ if (!cell) return "";
24
+ var v = cell.getAttribute("data-sort-value");
25
+ return v !== null ? v : cell.textContent.trim();
26
+ }
27
+
28
+ document.addEventListener("click", function (e) {
29
+ var th = closest(e.target, ".nre table th[data-nre-sort]");
30
+ if (!th) return;
31
+ var table = th.closest("table");
32
+ var tbody = table && table.tBodies[0];
33
+ if (!tbody) return;
34
+ var index = Array.prototype.indexOf.call(th.parentNode.children, th);
35
+ var dir = th.classList.contains("nre-sort-asc") ? "desc" : "asc";
36
+ var siblings = th.parentNode.querySelectorAll("th[data-nre-sort]");
37
+ for (var i = 0; i < siblings.length; i++) siblings[i].classList.remove("nre-sort-asc", "nre-sort-desc");
38
+ th.classList.add(dir === "asc" ? "nre-sort-asc" : "nre-sort-desc");
39
+
40
+ var rows = Array.prototype.slice.call(tbody.rows);
41
+ rows.sort(function (a, b) {
42
+ var va = sortValue(a, index);
43
+ var vb = sortValue(b, index);
44
+ // 空值 = 缺数据:恒沉底,与「缺数据不编 0」同一姿势
45
+ if (va === "" && vb === "") return 0;
46
+ if (va === "") return 1;
47
+ if (vb === "") return -1;
48
+ var na = Number(va);
49
+ var nb = Number(vb);
50
+ var out;
51
+ if (!isNaN(na) && !isNaN(nb)) out = na - nb;
52
+ else out = String(va).localeCompare(String(vb));
53
+ return dir === "asc" ? out : -out;
54
+ });
55
+ for (var u = 0; u < rows.length; u++) tbody.appendChild(rows[u]);
56
+ });
57
+
58
+ // ───────────────────────── 过滤:input[data-nre-filter] ─────────────────────────
59
+ // 对同容器内的表格行做 textContent 匹配,不匹配者加隐藏类(样式在 styles.css)。
60
+
61
+ document.addEventListener("input", function (e) {
62
+ var input = closest(e.target, "input[data-nre-filter]");
63
+ if (!input) return;
64
+ var scope = input.parentElement;
65
+ var table = scope ? scope.querySelector("table") : null;
66
+ if (!table || !table.tBodies[0]) return;
67
+ var query = input.value.trim().toLowerCase();
68
+ var rows = table.tBodies[0].rows;
69
+ for (var i = 0; i < rows.length; i++) {
70
+ var row = rows[i];
71
+ var hide = query !== "" && row.textContent.toLowerCase().indexOf(query) === -1;
72
+ row.classList.toggle("nre-row-hidden", hide);
73
+ }
74
+ });
75
+
76
+ // ───────────────────────── tooltip:.nre-scatter-point / .nre-line-point ─────────────────────────
77
+ // 首次 hover 时把点内 <title> 的内容搬进 data-nre-title(避免与原生 tooltip 重影),
78
+ // 渲染样式化 tooltip div(定位在点上方,挂在所属 figure 里)。无 JS 时 <title> 原样生效。
79
+
80
+ var tooltip = null;
81
+
82
+ function hideTooltip() {
83
+ if (tooltip && tooltip.parentNode) tooltip.parentNode.removeChild(tooltip);
84
+ tooltip = null;
85
+ }
86
+
87
+ function tooltipText(point) {
88
+ var text = point.getAttribute("data-nre-title");
89
+ if (text === null) {
90
+ var title = point.querySelector("title");
91
+ text = title ? title.textContent : "";
92
+ if (title && title.parentNode) title.parentNode.removeChild(title);
93
+ point.setAttribute("data-nre-title", text);
94
+ }
95
+ return text;
96
+ }
97
+
98
+ document.addEventListener("mouseover", function (e) {
99
+ var point = closest(e.target, ".nre-scatter-point, .nre-line-point");
100
+ if (!point) return;
101
+ var text = tooltipText(point);
102
+ if (!text) return;
103
+ var figure = point.closest("figure") || document.body;
104
+ hideTooltip();
105
+ tooltip = document.createElement("div");
106
+ tooltip.className = "nre-tooltip";
107
+ var lines = text.split("\n");
108
+ for (var i = 0; i < lines.length; i++) {
109
+ var line = document.createElement(i === 0 ? "b" : "div");
110
+ if (i > 0) line.className = "nre-tooltip-meta";
111
+ line.textContent = lines[i];
112
+ tooltip.appendChild(line);
113
+ }
114
+ if (getComputedStyle(figure).position === "static") figure.style.position = "relative";
115
+ figure.appendChild(tooltip);
116
+ var pointBox = point.getBoundingClientRect();
117
+ var figureBox = figure.getBoundingClientRect();
118
+ tooltip.style.left = pointBox.left + pointBox.width / 2 - figureBox.left + "px";
119
+ tooltip.style.top = pointBox.top - figureBox.top + "px";
120
+ });
121
+
122
+ document.addEventListener("mouseout", function (e) {
123
+ var point = closest(e.target, ".nre-scatter-point, .nre-line-point");
124
+ if (!point) return;
125
+ // 移入 tooltip 自身不算离开(pointer-events: none 下 relatedTarget 不会是它,防御性判断)
126
+ if (e.relatedTarget && point.contains(e.relatedTarget)) return;
127
+ hideTooltip();
128
+ });
129
+ })();
@@ -4,8 +4,11 @@
4
4
  // 稀疏矩阵、缺数据的散点、delta 的 null 不硬算、truncated 计数。
5
5
 
6
6
  import type {
7
- CaseListData,
7
+ AttemptListItem,
8
8
  DeltaData,
9
+ EvalListItem,
10
+ ExperimentListItem,
11
+ GroupSummaryData,
9
12
  LineData,
10
13
  MatrixData,
11
14
  MetricColumn,
@@ -14,6 +17,9 @@ import type {
14
17
  ScoreboardData,
15
18
  TableData,
16
19
  } from "../types.ts";
20
+ import type { AttemptLocator } from "../../results/locator.ts";
21
+
22
+ const locator = (s: string): AttemptLocator => s as AttemptLocator;
17
23
 
18
24
  export const passRateColumn: MetricColumn = { key: "pass-rate", label: "pass rate", unit: "%", better: "higher" };
19
25
  export const codeLinesColumn: MetricColumn = { key: "code-lines", label: "code lines", unit: "lines", better: "lower" };
@@ -31,6 +37,9 @@ export const overviewData: OverviewData = {
31
37
  failed: 8,
32
38
  errored: 2,
33
39
  skipped: 2,
40
+ // 两级聚合口径(computeCell)刻意不等于 36/(36+8+2)≈78% 的 attempt 原始占比:
41
+ // 组件必须原样渲染这个字段,不得从上面四个 verdict 计票现场重算。
42
+ passRate: { value: 0.7, display: "70%", samples: 46, total: 48, refs: [] },
34
43
  costUSD: null, // 全部 attempt 都没报成本:null,组件必须显示缺数据而不是 $0
35
44
  durationMs: 261_000,
36
45
  },
@@ -51,6 +60,19 @@ export const overviewWithCost: OverviewData = {
51
60
  warnings: [],
52
61
  };
53
62
 
63
+ /** GroupSummary.data 的产物形态:eval 级折叠计票 + 旧 GroupSelector 口径的通过率(包成 MetricCell)。 */
64
+ export const groupSummaryData: GroupSummaryData = {
65
+ experiments: 2,
66
+ // evals = 全部 verdicts 之和(3+1+1+1=6);passRate 分母只数非 skipped 的 5 道
67
+ evals: 6,
68
+ attempts: 9,
69
+ verdicts: { passed: 3, failed: 1, errored: 1, skipped: 1 },
70
+ // 3 passed / (3 + 1 + 1) = 60%;samples=ran=5 < total=evals=6:1 道 skipped 未计入分母的覆盖率角标
71
+ passRate: { value: 0.6, display: "60%", samples: 5, total: 6, refs: [] },
72
+ totalCostUSD: 1.5,
73
+ lastRunAt: "2026-07-01T11:30:00Z",
74
+ };
75
+
54
76
  export const tableData: TableData<"pass-rate" | "code-lines"> = {
55
77
  dimension: "agent",
56
78
  // 行顺序故意不按 passRate 排:组件必须按传入顺序渲染,不重排
@@ -72,7 +94,7 @@ export const tableData: TableData<"pass-rate" | "code-lines"> = {
72
94
  display: "87%",
73
95
  samples: 6,
74
96
  total: 6,
75
- refs: [{ run: "run-a", result: 0 }],
97
+ refs: [locator("@1a0a0a0")],
76
98
  },
77
99
  // samples < total:有 attempt 测不了 → 覆盖率角标 5/6
78
100
  "code-lines": { value: 120, display: "120 lines", samples: 5, total: 6, refs: [] },
@@ -81,6 +103,31 @@ export const tableData: TableData<"pass-rate" | "code-lines"> = {
81
103
  ],
82
104
  };
83
105
 
106
+ /** rows: "experiment" 的榜单形态:行携带 agent/model 元信息与 eval 级折叠计票。 */
107
+ export const tableDataWithMeta: TableData<"pass-rate"> = {
108
+ dimension: "experiment",
109
+ columns: [passRateColumn],
110
+ rows: [
111
+ {
112
+ key: "compare/bub",
113
+ cells: { "pass-rate": { value: 0.5, display: "50%", samples: 2, total: 2, refs: [] } },
114
+ meta: {
115
+ agent: "bub",
116
+ model: "gpt-5.4",
117
+ verdicts: { passed: 1, failed: 1, errored: 0, skipped: 0 },
118
+ },
119
+ },
120
+ {
121
+ key: "compare/codex",
122
+ cells: { "pass-rate": { value: 1, display: "100%", samples: 2, total: 2, refs: [] } },
123
+ meta: {
124
+ agent: "codex",
125
+ verdicts: { passed: 2, failed: 0, errored: 0, skipped: 0 },
126
+ },
127
+ },
128
+ ],
129
+ };
130
+
84
131
  export const matrixData: MatrixData = {
85
132
  rows: "eval",
86
133
  columns: "agent",
@@ -95,10 +142,7 @@ export const matrixData: MatrixData = {
95
142
  display: "100%",
96
143
  samples: 2,
97
144
  total: 2,
98
- refs: [
99
- { run: "run-b", result: 3 },
100
- { run: "run-b", result: 7 },
101
- ],
145
+ refs: [locator("@1b3b3b3"), locator("@1b7b7b7")],
102
146
  },
103
147
  },
104
148
  {
@@ -264,35 +308,120 @@ export const deltaData: DeltaData<"pass-rate" | "cost"> = {
264
308
  ],
265
309
  };
266
310
 
267
- export const caseListData: CaseListData = {
268
- rows: [
269
- {
270
- eval: "algebra/quadratic",
271
- experimentId: "compare/bub",
272
- agent: "bub",
273
- outcome: "failed",
274
- failedAssertions: [
275
- {
276
- name: "roots-correct",
277
- score: 0,
278
- detail: "expected x=2, got x=3",
279
- evidence: "judge: sign flipped when substituting into the quadratic formula",
280
- },
281
- ],
282
- durationMs: 32_000,
283
- costUSD: 0.12,
284
- ref: { run: "run-a", result: 4 },
285
- },
311
+ // ───────────────────────── 实体列表(ExperimentList / EvalList / AttemptList)─────────────────────────
312
+
313
+ /** algebra/quadratic 在 compare/bub 上失败的那次 attempt——两条子失败夹具共用同一条。 */
314
+ const failedAttempt: AttemptListItem = {
315
+ evalId: "algebra/quadratic",
316
+ experimentId: "compare/bub",
317
+ attempt: 3,
318
+ agent: "bub",
319
+ verdict: "failed",
320
+ assertions: [
286
321
  {
287
- eval: "geometry/angles",
288
- experimentId: "compare/codex",
289
- agent: "codex",
290
- outcome: "errored",
291
- error: "TypeError: cannot read properties of undefined (reading 'foo')",
292
- failedAssertions: [],
293
- durationMs: 4_500,
294
- ref: { run: "run-c", result: 1 },
322
+ name: "roots-correct",
323
+ severity: "gate",
324
+ score: 0,
325
+ passed: false,
326
+ detail: "expected x=2, got x=3",
327
+ evidence: "judge: sign flipped when substituting into the quadratic formula",
295
328
  },
296
329
  ],
297
- truncated: 2,
330
+ durationMs: 32_000,
331
+ costUSD: 0.12,
332
+ locator: locator("@1a4a4a4"),
333
+ capabilities: { eval: true, execution: true, timing: true, diff: false },
298
334
  };
335
+
336
+ const erroredAttempt: AttemptListItem = {
337
+ evalId: "geometry/angles",
338
+ experimentId: "compare/codex",
339
+ attempt: 0,
340
+ agent: "codex",
341
+ verdict: "errored",
342
+ error: "TypeError: cannot read properties of undefined (reading 'foo')",
343
+ assertions: [],
344
+ durationMs: 4_500,
345
+ locator: locator("@1c1c1c1"),
346
+ capabilities: { eval: false, execution: true, timing: false, diff: false },
347
+ };
348
+
349
+ export const attemptListItems: AttemptListItem[] = [failedAttempt, erroredAttempt];
350
+
351
+ export const evalListItems: EvalListItem[] = [
352
+ {
353
+ evalId: "algebra/quadratic",
354
+ experimentId: "compare/bub",
355
+ verdict: "failed",
356
+ reason: "roots-correct: expected x=2, got x=3",
357
+ score: { value: 0, display: "0%", samples: 1, total: 1, refs: [failedAttempt.locator] },
358
+ duration: { value: 32_000, display: "32.0s", samples: 1, total: 1, refs: [failedAttempt.locator] },
359
+ cost: { value: 0.12, display: "$0.12", samples: 1, total: 1, refs: [failedAttempt.locator] },
360
+ attempts: [failedAttempt],
361
+ },
362
+ {
363
+ evalId: "geometry/angles",
364
+ experimentId: "compare/codex",
365
+ verdict: "errored",
366
+ reason: erroredAttempt.error,
367
+ score: { value: 0, display: "0%", samples: 1, total: 1, refs: [erroredAttempt.locator] },
368
+ duration: { value: 4_500, display: "4.5s", samples: 1, total: 1, refs: [erroredAttempt.locator] },
369
+ cost: { value: null, display: "—", samples: 0, total: 1, refs: [] },
370
+ attempts: [erroredAttempt],
371
+ },
372
+ ];
373
+
374
+ export const experimentListItems: ExperimentListItem[] = [
375
+ {
376
+ experimentId: "compare/bub",
377
+ agent: "bub",
378
+ model: "gpt-5.4",
379
+ verdicts: { passed: 1, failed: 1, errored: 0, skipped: 0 },
380
+ passRate: { value: 0.5, display: "50%", samples: 2, total: 2, refs: [] },
381
+ cost: { value: 0.12, display: "$0.12", samples: 1, total: 2, refs: [failedAttempt.locator] },
382
+ duration: { value: 32_000, display: "32.0s", samples: 2, total: 2, refs: [] },
383
+ tokens: { value: null, display: "—", samples: 0, total: 2, refs: [] },
384
+ evals: 2,
385
+ attempts: 2,
386
+ lastRunAt: "2026-07-01T10:00:00Z",
387
+ evalRows: [
388
+ {
389
+ evalId: "algebra/quadratic",
390
+ verdict: "failed",
391
+ reason: "roots-correct: expected x=2, got x=3",
392
+ duration: { value: 32_000, display: "32.0s", samples: 1, total: 1, refs: [failedAttempt.locator] },
393
+ cost: { value: 0.12, display: "$0.12", samples: 1, total: 1, refs: [failedAttempt.locator] },
394
+ attempts: [failedAttempt],
395
+ },
396
+ {
397
+ evalId: "algebra/simple",
398
+ verdict: "passed",
399
+ duration: { value: 5_000, display: "5.0s", samples: 1, total: 1, refs: [] },
400
+ cost: { value: 0.02, display: "$0.02", samples: 1, total: 1, refs: [] },
401
+ attempts: [],
402
+ },
403
+ ],
404
+ },
405
+ {
406
+ experimentId: "compare/codex",
407
+ agent: "codex",
408
+ verdicts: { passed: 0, failed: 0, errored: 1, skipped: 0 },
409
+ passRate: { value: 0, display: "0%", samples: 1, total: 1, refs: [] },
410
+ cost: { value: null, display: "—", samples: 0, total: 1, refs: [] },
411
+ duration: { value: 4_500, display: "4.5s", samples: 1, total: 1, refs: [] },
412
+ tokens: { value: null, display: "—", samples: 0, total: 1, refs: [] },
413
+ evals: 1,
414
+ attempts: 1,
415
+ lastRunAt: "2026-07-01T11:30:00Z",
416
+ evalRows: [
417
+ {
418
+ evalId: "geometry/angles",
419
+ verdict: "errored",
420
+ reason: erroredAttempt.error,
421
+ duration: { value: 4_500, display: "4.5s", samples: 1, total: 1, refs: [erroredAttempt.locator] },
422
+ cost: { value: null, display: "—", samples: 0, total: 1, refs: [] },
423
+ attempts: [erroredAttempt],
424
+ },
425
+ ],
426
+ },
427
+ ];
@@ -1,7 +1,7 @@
1
1
  // web 面的小工具:展示格式化统一住在计算侧的 ../format.ts(两个渲染面同一份),
2
2
  // 这里 re-export 并补 class 名拼接。MetricCell 一律自带 display,组件不重算。
3
3
 
4
- export { MISSING_TEXT, formatDurationMs, formatPercent, formatUSD } from "../format.ts";
4
+ export { MISSING_TEXT, capabilityBadge, formatDurationMs, formatPercent, formatUSD, verdictMark } from "../format.ts";
5
5
 
6
6
  /** 拼 class 名:过滤空值,末尾接使用者透传的 className。 */
7
7
  export function cx(...parts: (string | undefined | false)[]): string {
@@ -9,8 +9,11 @@
9
9
  // - 跨块配色一致:维度键 → 稳定散列 → 固定调色板下标(colors.ts)。
10
10
 
11
11
  export {
12
- CaseList,
12
+ AttemptList,
13
13
  DeltaTable,
14
+ EvalList,
15
+ ExperimentList,
16
+ GroupSummary,
14
17
  MetricBars,
15
18
  MetricLine,
16
19
  MetricMatrix,
@@ -20,8 +23,11 @@ export {
20
23
  Scoreboard,
21
24
  } from "../components.tsx";
22
25
  export type {
23
- CaseListProps,
26
+ AttemptListProps,
24
27
  DeltaTableProps,
28
+ EvalListProps,
29
+ ExperimentListProps,
30
+ GroupSummaryProps,
25
31
  MetricLineProps,
26
32
  MetricMatrixProps,
27
33
  MetricScatterProps,
@@ -32,9 +38,14 @@ export type {
32
38
 
33
39
  // 数据契约类型(家在 ../types.ts,「算」与「画」两侧共用同一份)
34
40
  export type {
35
- AttemptRef,
36
- CaseListData,
41
+ AttemptEvidenceCapabilities,
42
+ AttemptListItem,
43
+ AttemptLocator,
37
44
  DeltaData,
45
+ EvalListItem,
46
+ ExperimentListEvalRow,
47
+ ExperimentListItem,
48
+ GroupSummaryData,
38
49
  LineAxis,
39
50
  LineData,
40
51
  MatrixData,
@@ -45,7 +56,12 @@ export type {
45
56
  ScoreboardData,
46
57
  SelectionWarning,
47
58
  TableData,
59
+ TableRowMeta,
48
60
  } from "../types.ts";
49
61
 
50
- // 稳定配色(自定义组件想与官方组件同键同色时用)
51
- export { NRE_PALETTE, colorClassForKey, colorHexForKey, colorIndexForKey } from "./colors.ts";
62
+ // locale(官方组件 chrome 文案;指标 label 的按 locale 字典也用它解析)
63
+ export { DEFAULT_REPORT_LOCALE, resolveMetricLabel } from "../locale.ts";
64
+ export type { LocalizedLabel, ReportLocale } from "../locale.ts";
65
+
66
+ // 稳定配色(自定义组件想与官方组件同键同色时用;seriesClassForKey 配 CSS 的 --nre-series)
67
+ export { NRE_PALETTE, colorClassForKey, colorHexForKey, colorIndexForKey, seriesClassForKey } from "./colors.ts";