niceeval 0.8.1 → 0.9.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 (132) hide show
  1. package/INDEX.md +77 -45
  2. package/dist/agents/types.d.ts +28 -6
  3. package/dist/i18n/en.d.ts +2 -0
  4. package/dist/i18n/zh-CN.d.ts +2 -0
  5. package/dist/report/built-in/index.d.ts +3 -2
  6. package/dist/report/built-in/index.js +7 -8
  7. package/dist/report/built-in/standard.d.ts +1 -0
  8. package/dist/report/built-in/standard.js +30 -0
  9. package/dist/report/components.d.ts +69 -2
  10. package/dist/report/components.js +152 -3
  11. package/dist/report/compute.d.ts +28 -1
  12. package/dist/report/compute.js +123 -0
  13. package/dist/report/index.d.ts +4 -4
  14. package/dist/report/index.js +3 -2
  15. package/dist/report/locale.d.ts +39 -1
  16. package/dist/report/locale.js +69 -0
  17. package/dist/report/react/AttemptList.d.ts +3 -1
  18. package/dist/report/react/AttemptList.js +3 -3
  19. package/dist/report/react/CopyFixPrompt.d.ts +12 -0
  20. package/dist/report/react/CopyFixPrompt.js +12 -0
  21. package/dist/report/react/HeroCard.d.ts +13 -0
  22. package/dist/report/react/HeroCard.js +35 -0
  23. package/dist/report/react/PoweredBy.d.ts +5 -0
  24. package/dist/report/react/PoweredBy.js +7 -0
  25. package/dist/report/react/ScopeWarnings.d.ts +12 -0
  26. package/dist/report/react/ScopeWarnings.js +18 -0
  27. package/dist/report/react/TraceWaterfall.d.ts +14 -0
  28. package/dist/report/react/TraceWaterfall.js +22 -0
  29. package/dist/report/react/index.d.ts +6 -1
  30. package/dist/report/react/index.js +6 -0
  31. package/dist/report/report.d.ts +22 -6
  32. package/dist/report/report.js +66 -53
  33. package/dist/report/scope-warnings.d.ts +28 -0
  34. package/dist/report/scope-warnings.js +101 -0
  35. package/dist/report/text/faces.d.ts +19 -1
  36. package/dist/report/text/faces.js +61 -0
  37. package/dist/report/tree.js +6 -1
  38. package/dist/report/types.d.ts +45 -0
  39. package/dist/report/web.d.ts +5 -4
  40. package/dist/report/web.js +7 -22
  41. package/dist/results/select.d.ts +15 -2
  42. package/dist/results/select.js +75 -10
  43. package/dist/results/types.d.ts +26 -0
  44. package/dist/runner/fingerprint.d.ts +3 -3
  45. package/dist/runner/sandbox-selection.d.ts +12 -0
  46. package/dist/runner/types.d.ts +18 -6
  47. package/dist/sandbox/types.d.ts +12 -0
  48. package/docs-site/zh/explanation/evals.mdx +2 -1
  49. package/docs-site/zh/explanation/experiment.mdx +2 -0
  50. package/docs-site/zh/how-to/custom-reports.mdx +22 -6
  51. package/docs-site/zh/how-to/publish-report.mdx +5 -4
  52. package/docs-site/zh/how-to/viewing-results.mdx +3 -3
  53. package/docs-site/zh/how-to/write-experiment.mdx +40 -1
  54. package/docs-site/zh/reference/builtin-agents.mdx +40 -3
  55. package/docs-site/zh/reference/cli.mdx +1 -1
  56. package/docs-site/zh/reference/define-eval.mdx +8 -0
  57. package/docs-site/zh/reference/official-adapters.mdx +10 -5
  58. package/package.json +2 -1
  59. package/src/agents/bub.ts +13 -1
  60. package/src/agents/claude-code.test.ts +43 -1
  61. package/src/agents/claude-code.ts +32 -14
  62. package/src/agents/codex.test.ts +168 -1
  63. package/src/agents/codex.ts +51 -15
  64. package/src/agents/mcp.ts +31 -0
  65. package/src/agents/post-setup.ts +33 -0
  66. package/src/agents/types.ts +28 -7
  67. package/src/cli.ts +8 -6
  68. package/src/define.ts +3 -0
  69. package/src/i18n/en.ts +5 -2
  70. package/src/i18n/zh-CN.ts +5 -1
  71. package/src/index.ts +1 -0
  72. package/src/report/built-in/index.tsx +8 -7
  73. package/src/report/built-in/standard.tsx +59 -0
  74. package/src/report/components.tsx +218 -2
  75. package/src/report/compute.ts +138 -1
  76. package/src/report/dual-render.test.tsx +139 -12
  77. package/src/report/index.ts +20 -0
  78. package/src/report/locale.ts +83 -1
  79. package/src/report/react/AttemptList.tsx +13 -1
  80. package/src/report/react/CopyFixPrompt.tsx +37 -0
  81. package/src/report/react/HeroCard.tsx +59 -0
  82. package/src/report/react/PoweredBy.tsx +20 -0
  83. package/src/report/react/ScopeWarnings.tsx +74 -0
  84. package/src/report/react/TraceWaterfall.tsx +78 -0
  85. package/src/report/react/enhance.js +14 -0
  86. package/src/report/react/index.tsx +11 -0
  87. package/src/report/react/styles.css +187 -7
  88. package/src/report/report.ts +97 -62
  89. package/src/report/scope-warnings.ts +155 -0
  90. package/src/report/site-components.test.tsx +526 -0
  91. package/src/report/text/faces.ts +66 -0
  92. package/src/report/tree.ts +8 -1
  93. package/src/report/types.ts +51 -0
  94. package/src/report/web.ts +7 -40
  95. package/src/results/host-equivalence.test.ts +5 -1
  96. package/src/results/open.ts +5 -4
  97. package/src/results/results.test.ts +78 -1
  98. package/src/results/select.ts +78 -10
  99. package/src/results/types.ts +27 -0
  100. package/src/runner/attempt.ts +10 -9
  101. package/src/runner/discover.test.ts +9 -1
  102. package/src/runner/discover.ts +3 -3
  103. package/src/runner/fingerprint.ts +9 -4
  104. package/src/runner/ledger.test.ts +30 -1
  105. package/src/runner/ledger.ts +26 -4
  106. package/src/runner/run.ts +5 -1
  107. package/src/runner/sandbox-selection.test.ts +131 -0
  108. package/src/runner/sandbox-selection.ts +110 -0
  109. package/src/runner/types.ts +19 -2
  110. package/src/sandbox/types.ts +6 -0
  111. package/src/show/index.ts +17 -10
  112. package/src/show/render.ts +11 -11
  113. package/src/show/report-host.test.ts +32 -15
  114. package/src/show/report-host.ts +5 -4
  115. package/src/show/show.test.ts +140 -3
  116. package/src/view/app/App.test.tsx +78 -17
  117. package/src/view/app/App.tsx +17 -78
  118. package/src/view/app/components/CopyControls.tsx +4 -42
  119. package/src/view/app/i18n.ts +5 -227
  120. package/src/view/app/lib/rows.ts +3 -21
  121. package/src/view/app/shared.ts +1 -3
  122. package/src/view/app/types.ts +2 -2
  123. package/src/view/client-dist/app.css +1 -1
  124. package/src/view/client-dist/app.js +20 -20
  125. package/src/view/data.ts +2 -2
  126. package/src/view/shared/types.ts +11 -6
  127. package/src/view/styles.css +6 -266
  128. package/src/view/view-report.test.ts +59 -22
  129. package/src/view/app/components/LazyArtifact.tsx +0 -51
  130. package/src/view/app/components/SkippedRunsBanner.tsx +0 -140
  131. package/src/view/app/pages/AttemptsPage.tsx +0 -80
  132. package/src/view/app/pages/TracesPage.tsx +0 -35
@@ -0,0 +1,59 @@
1
+ // HeroCard:站点标题区的 web 面——hero 标题(h1)、运行 meta(最后运行时间按渲染 locale
2
+ // 格式化;latestStartedAt 为 null 时内置「暂无运行」文案;snapshots > 1 时标注合成来源)
3
+ // 与品牌行(等同 PoweredBy,恒含、无拆除 prop)。标题输入是站点声明与 Scope 的合成物,
4
+ // 组件只收 data 形态(docs/feature/reports/library/site-components.md「HeroCard」)。
5
+
6
+ import type { ReactElement } from "react";
7
+ import type { HeroData } from "../types.ts";
8
+ import { DEFAULT_REPORT_LOCALE, localeText, resolveLocalizedText, type LocalizedText, type ReportLocale } from "../locale.ts";
9
+ import { cx } from "./format.ts";
10
+ import { PoweredBy } from "./PoweredBy.tsx";
11
+
12
+ /** ISO 时间 → 按 locale 的「最后运行」显示(年月日 时:分);不可解析原样返回。 */
13
+ function formatLastRun(iso: string, locale: ReportLocale): string {
14
+ const date = new Date(iso);
15
+ if (Number.isNaN(date.valueOf())) return iso;
16
+ try {
17
+ return new Intl.DateTimeFormat(locale, {
18
+ year: "numeric",
19
+ month: "short",
20
+ day: "numeric",
21
+ hour: "2-digit",
22
+ minute: "2-digit",
23
+ }).format(date);
24
+ } catch {
25
+ return iso;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * 站点标题区(纯 web 渲染面):`<h1>` 标题 + meta 行 + 品牌行。
31
+ * 嵌入自有 React 页面时配合 `heroData()` 使用。
32
+ */
33
+ export function HeroCard({
34
+ title,
35
+ data,
36
+ className,
37
+ locale = DEFAULT_REPORT_LOCALE,
38
+ }: {
39
+ title: LocalizedText;
40
+ data: HeroData;
41
+ className?: string;
42
+ locale?: ReportLocale;
43
+ }): ReactElement {
44
+ const meta =
45
+ data.latestStartedAt === null
46
+ ? localeText(locale, "hero.noRuns")
47
+ : [
48
+ localeText(locale, "hero.lastRun", { time: formatLastRun(data.latestStartedAt, locale) }),
49
+ ...(data.snapshots > 1 ? [localeText(locale, "hero.composedRuns", { n: data.snapshots })] : []),
50
+ ].join(" · ");
51
+ return (
52
+ <header className={cx("nre", "nre-hero", className)}>
53
+ <h1 className="nre-hero-title">{resolveLocalizedText(title, locale)}</h1>
54
+ <p className="nre-hero-meta">{meta}</p>
55
+ {/* 品牌行与 PoweredBy 同一渲染:品牌跟着组件走,不区分官方宿主与嵌入页面 */}
56
+ <PoweredBy />
57
+ </header>
58
+ );
59
+ }
@@ -0,0 +1,20 @@
1
+ // PoweredBy:唯一的品牌件的 web 面。品牌契约是「提供一个组件,不给开关」——无 props、
2
+ // 无关闭配置;不想要品牌 = 不用它(docs/feature/reports/library/site-components.md「PoweredBy」)。
3
+ // rel 只声明 noopener 以保留 Referer(默认策略只发 origin):官网统计由此得知点击来自哪个
4
+ // 报告站点;静态导出在构建期不知道自己托管在哪个域名,来源不进 URL 参数。
5
+
6
+ import type { ReactElement } from "react";
7
+
8
+ /** 品牌行的固定去处:niceeval 官网,utm 标记「来自报告的品牌行」。 */
9
+ export const POWERED_BY_HREF = "https://niceeval.com/?utm_source=report&utm_medium=powered-by";
10
+
11
+ /** 一行品牌色小字 `Powered by NiceEval`,链接官网;HeroCard 的品牌行与它同一渲染。 */
12
+ export function PoweredBy(): ReactElement {
13
+ return (
14
+ <p className="nre nre-powered-by">
15
+ <a href={POWERED_BY_HREF} target="_blank" rel="noopener">
16
+ Powered by NiceEval
17
+ </a>
18
+ </p>
19
+ );
20
+ }
@@ -0,0 +1,74 @@
1
+ // ScopeWarnings:选择警告区的 web 面。按「下一步动作」聚合(../scope-warnings.ts,
2
+ // 与 text 面共用同一聚合层):整个警告区是默认折叠的原生 <details>,<summary> 是
3
+ // 分类计数汇总行(恒可见);展开后每组组头 = 标题 + kind 徽标 + 去重后恰一条的
4
+ // 可复制命令,逐条原始 message 收进第二层 <details>(总条数 ≤ 3 默认展开)。
5
+ // 空警告集零输出,不渲染空容器
6
+ // (docs/feature/reports/library/site-components.md「ScopeWarnings」)。
7
+
8
+ import type { ReactElement } from "react";
9
+ import type { ScopeWarning } from "../types.ts";
10
+ import { DEFAULT_REPORT_LOCALE, type ReportLocale } from "../locale.ts";
11
+ import { groupScopeWarnings, warningDetailsLabel } from "../scope-warnings.ts";
12
+ import { cx } from "./format.ts";
13
+
14
+ /** 命令的可复制块:无 JS 时点击全选(user-select: all),enhance.js 在场时点击复制。 */
15
+ function CommandBlock({ command }: { command: string }): ReactElement {
16
+ return (
17
+ <code className="nre-warning-command" data-nre-copy={command}>
18
+ {command}
19
+ </code>
20
+ );
21
+ }
22
+
23
+ /**
24
+ * 选择警告区(纯 web 渲染面):按动作聚合的警告组。嵌入自有 React 页面时传
25
+ * `data={scope.warnings}`;空集返回 null,不渲染空容器。
26
+ */
27
+ export function ScopeWarnings({
28
+ data,
29
+ className,
30
+ locale = DEFAULT_REPORT_LOCALE,
31
+ }: {
32
+ data: readonly ScopeWarning[];
33
+ className?: string;
34
+ locale?: ReportLocale;
35
+ }): ReactElement | null {
36
+ if (data.length === 0) return null;
37
+ const { summary, groups, detailsOpen } = groupScopeWarnings(data, locale);
38
+ return (
39
+ <div className={cx("nre", "nre-scope-warnings", className)}>
40
+ <details className="nre-warnings">
41
+ <summary className="nre-warnings-summary">{summary}</summary>
42
+ <ul className="nre-warning-groups">
43
+ {groups.map((group, i) => (
44
+ <li key={i} className="nre-warning-group" data-category={group.category}>
45
+ <div className="nre-warning-head">
46
+ <span className="nre-warning-title">{group.title}</span>
47
+ {group.badges.map((badge, j) => (
48
+ <span key={j} className="nre-warning-badge" data-kind={badge.kind}>
49
+ {badge.text}
50
+ </span>
51
+ ))}
52
+ {group.headCommand !== null && <CommandBlock command={group.headCommand} />}
53
+ </div>
54
+ <details className="nre-warning-details" open={detailsOpen || undefined}>
55
+ <summary>{warningDetailsLabel(locale, group.warnings.length)}</summary>
56
+ <ul>
57
+ {group.warnings.map((w, j) => (
58
+ <li key={j} className="nre-warning" data-kind={w.kind}>
59
+ {w.message}
60
+ {/* 组头命令的含义是「复制即推进整组」;去重后多于一条时命令随明细逐条走 */}
61
+ {group.headCommand === null && "command" in w && w.command !== undefined && (
62
+ <CommandBlock command={w.command} />
63
+ )}
64
+ </li>
65
+ ))}
66
+ </ul>
67
+ </details>
68
+ </li>
69
+ ))}
70
+ </ul>
71
+ </details>
72
+ </div>
73
+ );
74
+ }
@@ -0,0 +1,78 @@
1
+ // TraceWaterfall:执行时间瀑布的 web 面——每个 attempt 一行,静态渲染顶层 span 分解条
2
+ // (定位按 startOffsetMs / durationMs 百分比;失败 span 带失败标记),行链接 attempt 详情。
3
+ // trace 缺失的行照常出现并如实显示缺失;排序、缩放是渐进增强,静态 HTML 无 JS 完整可读
4
+ // (docs/feature/reports/library/site-components.md「TraceWaterfall」)。
5
+
6
+ import type { ReactElement } from "react";
7
+ import type { TraceWaterfallRow } from "../types.ts";
8
+ import type { AttemptLocator } from "../../results/locator.ts";
9
+ import { DEFAULT_REPORT_LOCALE, countText, localeText, type ReportLocale } from "../locale.ts";
10
+ import { cx, formatDurationMs } from "./format.ts";
11
+
12
+ const DEFAULT_ATTEMPT_HREF = (locator: AttemptLocator): string => `#/attempt/${locator}`;
13
+
14
+ function pct(part: number, total: number): string {
15
+ if (total <= 0) return "0%";
16
+ return `${Math.min(100, Math.max(0, (part / total) * 100)).toFixed(2)}%`;
17
+ }
18
+
19
+ /**
20
+ * 执行时间瀑布(纯 web 渲染面):嵌入自有 React 页面时配合 `traceWaterfallData()` 使用。
21
+ * 只画被测 agent 的原始 span;runner 生命周期节点不在 data 里,组合视图归 attempt 详情。
22
+ */
23
+ export function TraceWaterfall({
24
+ data,
25
+ attemptHref = DEFAULT_ATTEMPT_HREF,
26
+ className,
27
+ locale = DEFAULT_REPORT_LOCALE,
28
+ }: {
29
+ data: readonly TraceWaterfallRow[];
30
+ attemptHref?: (locator: AttemptLocator) => string;
31
+ className?: string;
32
+ locale?: ReportLocale;
33
+ }): ReactElement {
34
+ return (
35
+ <section className={cx("nre", "nre-trace-waterfall", className)}>
36
+ {data.length === 0 && <p className="nre-waterfall-empty">{localeText(locale, "traceWaterfall.empty")}</p>}
37
+ <ul className="nre-waterfall">
38
+ {data.map((row) => {
39
+ const failedSpans = row.spans.filter((span) => span.failed).length;
40
+ return (
41
+ <li key={row.locator} className="nre-waterfall-row">
42
+ <div className="nre-waterfall-head">
43
+ <a className="nre-locator" href={attemptHref(row.locator)}>
44
+ {row.locator}
45
+ </a>
46
+ <span className="nre-waterfall-eval">{row.evalId}</span>
47
+ <span className="nre-waterfall-experiment">{row.experimentId}</span>
48
+ {/* trace 缺失如实显示缺失,不猜值、不藏行 */}
49
+ <span className="nre-waterfall-duration">
50
+ {row.durationMs === null ? localeText(locale, "traceWaterfall.noTrace") : formatDurationMs(row.durationMs)}
51
+ </span>
52
+ <span className="nre-waterfall-count">{countText(locale, "traceWaterfall.spans", row.spans.length)}</span>
53
+ {failedSpans > 0 && (
54
+ <span className="nre-waterfall-failed">✗ {countText(locale, "traceWaterfall.failedSpans", failedSpans)}</span>
55
+ )}
56
+ </div>
57
+ {row.durationMs !== null && row.spans.length > 0 && (
58
+ <div className="nre-waterfall-track">
59
+ {row.spans.map((span, i) => (
60
+ <span
61
+ key={i}
62
+ className={cx("nre-waterfall-span", `nre-span-${span.kind}`, span.failed && "nre-span-failed")}
63
+ style={{
64
+ left: pct(span.startOffsetMs, row.durationMs!),
65
+ width: `max(${pct(span.durationMs, row.durationMs!)}, 0.5%)`,
66
+ }}
67
+ title={`${span.name} · ${formatDurationMs(span.durationMs)}${span.failed ? " · ✗" : ""}`}
68
+ />
69
+ ))}
70
+ </div>
71
+ )}
72
+ </li>
73
+ );
74
+ })}
75
+ </ul>
76
+ </section>
77
+ );
78
+ }
@@ -196,6 +196,20 @@
196
196
  }
197
197
  });
198
198
 
199
+ // AttemptList 的过滤(<AttemptList filter />):按条目全文 + data-nre-verdict(verdict 词
200
+ // 不在可见文本里,判定符是 ✓/✗)收窄 .nre-attempt 行。只改浏览状态,不改数据与初始 HTML。
201
+ document.addEventListener("input", function (e) {
202
+ var input = closest(e.target, "input[data-nre-attempt-filter]");
203
+ if (!input) return;
204
+ var scope = input.parentElement;
205
+ var rows = scope ? scope.querySelectorAll(".nre-attempt") : [];
206
+ var query = input.value.trim().toLowerCase();
207
+ for (var i = 0; i < rows.length; i++) {
208
+ var haystack = rows[i].textContent + " " + (rows[i].getAttribute("data-nre-verdict") || "");
209
+ rows[i].classList.toggle("nre-row-hidden", query !== "" && haystack.toLowerCase().indexOf(query) === -1);
210
+ }
211
+ });
212
+
199
213
  // ───────────────────────── tooltip:.nre-scatter-point / .nre-line-point ─────────────────────────
200
214
  // 首次 hover 时把点内 <title> 的内容搬进 data-nre-title(避免与原生 tooltip 重影),
201
215
  // 渲染样式化 tooltip div(定位在点上方,挂在所属 figure 里)。无 JS 时 <title> 原样生效。
@@ -21,16 +21,25 @@ export { MetricLine } from "./MetricLine.tsx";
21
21
  export { DeltaTable } from "./DeltaTable.tsx";
22
22
  export { Scoreboard } from "./Scoreboard.tsx";
23
23
 
24
+ // 站点组件的纯 web 面(data 形态;Hero 是组合组件,只住 niceeval/report)
25
+ export { HeroCard } from "./HeroCard.tsx";
26
+ export { PoweredBy } from "./PoweredBy.tsx";
27
+ export { ScopeWarnings } from "./ScopeWarnings.tsx";
28
+ export { CopyFixPrompt } from "./CopyFixPrompt.tsx";
29
+ export { TraceWaterfall } from "./TraceWaterfall.tsx";
30
+
24
31
  // 数据契约类型(家在 ../types.ts,「算」与「画」两侧共用同一份)
25
32
  export type {
26
33
  AttemptListItem,
27
34
  AttemptLocator,
35
+ CopyFixPromptData,
28
36
  DeltaData,
29
37
  EvalListItem,
30
38
  ExperimentComparisonData,
31
39
  ExperimentComparisonGroupData,
32
40
  ExperimentListEvalRow,
33
41
  ExperimentListItem,
42
+ HeroData,
34
43
  LineData,
35
44
  MatrixData,
36
45
  MetricCell,
@@ -40,6 +49,8 @@ export type {
40
49
  ScopeWarning,
41
50
  ScoreboardData,
42
51
  TableData,
52
+ TraceSpanSummary,
53
+ TraceWaterfallRow,
43
54
  VerdictTally,
44
55
  } from "../types.ts";
45
56
 
@@ -436,18 +436,63 @@ table.nre,
436
436
  margin: 0.25rem 0 0;
437
437
  }
438
438
 
439
- /* ---- 宿主警告块:.nre-report-warnings 外壳 + ul.nre-warnings + li.nre-warning[data-kind] ---- */
439
+ /* ---- 宿主警告块:.nre-warnings 是默认折叠的 <details>,<summary> 汇总行恒可见。
440
+ 不占框:无边框、无底色、不缩进,警示色只落在汇总行文字与徽标上。 ---- */
440
441
  .nre .nre-warnings {
441
- list-style: none;
442
442
  margin: 0;
443
- padding: 10px 14px;
444
- border: 1px solid color-mix(in oklch, var(--warn), var(--line) 45%);
445
- border-left: 3px solid var(--warn);
446
- border-radius: 8px;
447
- background: color-mix(in oklch, var(--warn), var(--panel) 92%);
448
443
  color: var(--text);
449
444
  font-size: 13px;
450
445
  }
446
+ /* 外层 <details> 的 <summary>:分类计数汇总行,原生无 JS 可展开。 */
447
+ .nre .nre-warnings > .nre-warnings-summary {
448
+ font-weight: 650;
449
+ color: var(--warn);
450
+ cursor: pointer;
451
+ }
452
+ .nre .nre-warning-groups {
453
+ list-style: none;
454
+ margin: 8px 0 0;
455
+ padding: 0;
456
+ }
457
+ .nre .nre-warning-group + .nre-warning-group {
458
+ margin-top: 10px;
459
+ padding-top: 10px;
460
+ border-top: 1px solid var(--line);
461
+ }
462
+ .nre .nre-warning-head {
463
+ display: flex;
464
+ flex-wrap: wrap;
465
+ align-items: baseline;
466
+ gap: 4px 10px;
467
+ }
468
+ .nre .nre-warning-title { font-weight: 650; }
469
+ .nre .nre-warning-badge {
470
+ padding: 1px 8px;
471
+ border: 1px solid color-mix(in oklch, var(--warn), var(--line) 35%);
472
+ border-radius: 999px;
473
+ background: color-mix(in oklch, var(--warn), var(--panel) 84%);
474
+ font-size: 0.9em;
475
+ white-space: nowrap;
476
+ }
477
+ /* freshness 组(数字对但可能过期)视觉降一档,integrity 保持警示底色。 */
478
+ .nre .nre-warning-group[data-category="freshness"] .nre-warning-badge {
479
+ border-color: var(--line);
480
+ background: var(--panel);
481
+ color: var(--soft);
482
+ }
483
+ .nre .nre-warning-details { margin: 4px 0 0; }
484
+ /* <details> 原生无 JS 可展开,cursor 不经 .nre-js 门控。 */
485
+ .nre .nre-warning-details > summary {
486
+ color: var(--soft);
487
+ font-size: 0.9em;
488
+ cursor: pointer;
489
+ }
490
+ .nre .nre-warning-details > ul {
491
+ list-style: none;
492
+ margin: 4px 0 0;
493
+ padding: 0 0 0 12px;
494
+ border-left: 2px solid var(--line);
495
+ }
451
496
  .nre .nre-warning + .nre-warning { margin-top: 8px; }
452
497
  /* 警告携带的下一步命令:等宽块,无 JS 时点击即全选;enhance.js 在场时点击复制 */
453
498
  .nre .nre-warning-command {
@@ -470,6 +515,141 @@ table.nre,
470
515
  color: var(--good);
471
516
  }
472
517
 
518
+ /* ---- Hero / PoweredBy(站点标题区与品牌行) ---- */
519
+ .nre-hero { margin: 0 0 18px; }
520
+ .nre-hero .nre-hero-title {
521
+ margin: 0;
522
+ font-size: 24px;
523
+ font-weight: 700;
524
+ letter-spacing: -0.01em;
525
+ }
526
+ .nre-hero .nre-hero-meta {
527
+ margin: 4px 0 0;
528
+ color: var(--muted);
529
+ font-size: 13px;
530
+ }
531
+ .nre-powered-by {
532
+ margin: 6px 0 0;
533
+ font-size: 12px;
534
+ }
535
+ .nre-powered-by a {
536
+ color: var(--soft);
537
+ text-decoration: none;
538
+ }
539
+ .nre-powered-by a:hover { color: var(--text); text-decoration: underline; }
540
+
541
+ /* ---- CopyFixPrompt(批量修复 prompt:折叠块 + 增强层复制) ---- */
542
+ .nre-copy-fix-prompt {
543
+ margin: 0 0 16px;
544
+ border: 1px solid var(--line);
545
+ border-radius: 8px;
546
+ background: var(--panel);
547
+ padding: 8px 12px;
548
+ }
549
+ .nre-copy-fix-prompt > summary {
550
+ cursor: pointer;
551
+ font-weight: 620;
552
+ font-size: 13px;
553
+ }
554
+ .nre-copy-fix-prompt .nre-copy-fix-prompt-copy {
555
+ margin: 8px 0 0;
556
+ padding: 4px 10px;
557
+ border: 1px solid var(--line);
558
+ border-radius: 7px;
559
+ background: var(--panel);
560
+ color: var(--text);
561
+ font: inherit;
562
+ font-size: 12px;
563
+ }
564
+ :root.nre-js .nre-copy-fix-prompt .nre-copy-fix-prompt-copy { cursor: copy; }
565
+ .nre-copy-fix-prompt .nre-copy-fix-prompt-copy[data-nre-copied]::after {
566
+ content: "✓";
567
+ margin-left: 0.6em;
568
+ color: var(--good);
569
+ }
570
+ .nre-copy-fix-prompt .nre-copy-fix-prompt-text {
571
+ margin: 8px 0 0;
572
+ padding: 8px 10px;
573
+ border: 1px solid var(--line);
574
+ border-radius: 7px;
575
+ background: var(--bg, transparent);
576
+ font-size: 12px;
577
+ line-height: 1.5;
578
+ overflow-x: auto;
579
+ white-space: pre-wrap;
580
+ overflow-wrap: anywhere;
581
+ }
582
+
583
+ /* ---- TraceWaterfall(每 attempt 一行的静态时间瀑布) ---- */
584
+ .nre-trace-waterfall .nre-waterfall {
585
+ list-style: none;
586
+ margin: 0;
587
+ padding: 0;
588
+ }
589
+ .nre-trace-waterfall .nre-waterfall-row {
590
+ border: 1px solid var(--line);
591
+ border-radius: 8px;
592
+ padding: 10px 14px;
593
+ margin-bottom: 8px;
594
+ background: var(--panel);
595
+ }
596
+ .nre-trace-waterfall .nre-waterfall-head {
597
+ display: flex;
598
+ flex-wrap: wrap;
599
+ gap: 0.75em;
600
+ align-items: baseline;
601
+ }
602
+ .nre-trace-waterfall .nre-waterfall-eval { font-weight: 620; }
603
+ .nre-trace-waterfall .nre-waterfall-experiment,
604
+ .nre-trace-waterfall .nre-waterfall-duration,
605
+ .nre-trace-waterfall .nre-waterfall-count {
606
+ color: var(--muted);
607
+ font-size: 0.9em;
608
+ font-variant-numeric: tabular-nums;
609
+ }
610
+ .nre-trace-waterfall .nre-waterfall-failed { color: var(--bad); font-size: 0.9em; }
611
+ /* 分解条:span 按 startOffsetMs / durationMs 百分比静态定位,失败 span 换警示色 */
612
+ .nre-trace-waterfall .nre-waterfall-track {
613
+ position: relative;
614
+ height: 14px;
615
+ margin-top: 8px;
616
+ border-radius: 4px;
617
+ background: color-mix(in oklch, var(--line), var(--panel) 60%);
618
+ overflow: hidden;
619
+ }
620
+ .nre-trace-waterfall .nre-waterfall-span {
621
+ position: absolute;
622
+ top: 2px;
623
+ bottom: 2px;
624
+ border-radius: 2px;
625
+ background: var(--line-strong);
626
+ }
627
+ .nre-trace-waterfall .nre-span-agent { background: color-mix(in oklch, var(--good), var(--line-strong) 40%); }
628
+ .nre-trace-waterfall .nre-span-model { background: color-mix(in oklch, var(--warn), var(--line-strong) 40%); }
629
+ .nre-trace-waterfall .nre-span-tool { background: color-mix(in oklch, var(--line-strong), var(--text) 25%); }
630
+ .nre-trace-waterfall .nre-span-failed { background: var(--bad); }
631
+ .nre-trace-waterfall .nre-waterfall-empty {
632
+ border: 1px dashed var(--line-strong);
633
+ border-radius: 8px;
634
+ padding: 18px;
635
+ color: var(--muted);
636
+ text-align: center;
637
+ background: var(--panel);
638
+ }
639
+
640
+ /* ---- AttemptList 的过滤框(<AttemptList filter />,渐进增强) ---- */
641
+ .nre-attempt-list > .nre-filter {
642
+ height: 34px;
643
+ min-width: 260px;
644
+ margin-bottom: 12px;
645
+ border: 1px solid var(--line);
646
+ border-radius: 7px;
647
+ padding: 0 11px;
648
+ background: var(--panel);
649
+ color: var(--text);
650
+ font: inherit;
651
+ }
652
+
473
653
  /* ---- MetricMatrix ---- */
474
654
  .nre-metric-matrix .nre-matrix-caption {
475
655
  caption-side: top;