niceeval 0.1.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 (94) hide show
  1. package/README.md +164 -0
  2. package/README.zh.md +160 -0
  3. package/bin/niceeval.js +11 -0
  4. package/package.json +96 -0
  5. package/src/agents/bub.ts +223 -0
  6. package/src/agents/builtin.ts +6 -0
  7. package/src/agents/claude-code.ts +96 -0
  8. package/src/agents/codex.ts +122 -0
  9. package/src/agents/index.ts +25 -0
  10. package/src/agents/shared.ts +145 -0
  11. package/src/cli.ts +421 -0
  12. package/src/context/context.test.ts +96 -0
  13. package/src/context/context.ts +427 -0
  14. package/src/context/control-flow.ts +27 -0
  15. package/src/context/session.ts +124 -0
  16. package/src/define.ts +112 -0
  17. package/src/expect/index.ts +243 -0
  18. package/src/i18n/en.ts +131 -0
  19. package/src/i18n/index.ts +39 -0
  20. package/src/i18n/zh-CN.ts +132 -0
  21. package/src/index.ts +39 -0
  22. package/src/loaders/index.ts +56 -0
  23. package/src/o11y/cost.ts +57 -0
  24. package/src/o11y/derive.ts +304 -0
  25. package/src/o11y/otlp/canonical.ts +112 -0
  26. package/src/o11y/otlp/mappers/bub.ts +30 -0
  27. package/src/o11y/otlp/mappers/codex.ts +38 -0
  28. package/src/o11y/otlp/mappers/index.ts +25 -0
  29. package/src/o11y/otlp/parse.ts +330 -0
  30. package/src/o11y/otlp/receiver.ts +100 -0
  31. package/src/o11y/otlp/sandbox-receiver.ts +113 -0
  32. package/src/o11y/otlp/select.ts +82 -0
  33. package/src/o11y/parsers/bub.ts +240 -0
  34. package/src/o11y/parsers/claude-code.ts +270 -0
  35. package/src/o11y/parsers/codex.ts +480 -0
  36. package/src/o11y/parsers/index.ts +37 -0
  37. package/src/o11y/prices.json +9873 -0
  38. package/src/runner/discover.ts +77 -0
  39. package/src/runner/reporters/artifacts.ts +81 -0
  40. package/src/runner/reporters/console.ts +76 -0
  41. package/src/runner/reporters/index.ts +5 -0
  42. package/src/runner/reporters/json.ts +52 -0
  43. package/src/runner/reporters/live.ts +178 -0
  44. package/src/runner/reporters/table.ts +328 -0
  45. package/src/runner/run.ts +860 -0
  46. package/src/runner/sandbox-prep.ts +91 -0
  47. package/src/sandbox/checkpoint.ts +39 -0
  48. package/src/sandbox/docker.ts +539 -0
  49. package/src/sandbox/e2b.ts +203 -0
  50. package/src/sandbox/index.ts +25 -0
  51. package/src/sandbox/registry.ts +60 -0
  52. package/src/sandbox/resolve.ts +131 -0
  53. package/src/sandbox/source-files.ts +30 -0
  54. package/src/sandbox/vercel.ts +236 -0
  55. package/src/scoring/collector.ts +113 -0
  56. package/src/scoring/judge.ts +236 -0
  57. package/src/scoring/scoped.ts +289 -0
  58. package/src/scoring/verdict.ts +20 -0
  59. package/src/source-loc.ts +53 -0
  60. package/src/types.ts +913 -0
  61. package/src/util.test.ts +31 -0
  62. package/src/util.ts +50 -0
  63. package/src/view/app/App.tsx +189 -0
  64. package/src/view/app/components/AttemptModal.tsx +66 -0
  65. package/src/view/app/components/CodeView.tsx +272 -0
  66. package/src/view/app/components/CopyControls.tsx +89 -0
  67. package/src/view/app/components/ExperimentTable.tsx +266 -0
  68. package/src/view/app/components/GroupSelector.tsx +61 -0
  69. package/src/view/app/components/LazyArtifact.tsx +50 -0
  70. package/src/view/app/components/Trace.tsx +100 -0
  71. package/src/view/app/components/Transcript.tsx +130 -0
  72. package/src/view/app/components/primitives.tsx +43 -0
  73. package/src/view/app/components/ui/badge.tsx +21 -0
  74. package/src/view/app/components/ui/dialog.tsx +34 -0
  75. package/src/view/app/components/ui/tabs.tsx +30 -0
  76. package/src/view/app/i18n.ts +341 -0
  77. package/src/view/app/index.html +13 -0
  78. package/src/view/app/lib/cn.ts +7 -0
  79. package/src/view/app/lib/format.ts +73 -0
  80. package/src/view/app/lib/guards.ts +61 -0
  81. package/src/view/app/lib/outcome.ts +96 -0
  82. package/src/view/app/lib/rows.ts +63 -0
  83. package/src/view/app/lib/transcript-data.tsx +121 -0
  84. package/src/view/app/main.tsx +17 -0
  85. package/src/view/app/pages/RunsPage.tsx +83 -0
  86. package/src/view/app/pages/TracesPage.tsx +40 -0
  87. package/src/view/app/shared.ts +10 -0
  88. package/src/view/app/types.ts +114 -0
  89. package/src/view/app/vite.config.ts +26 -0
  90. package/src/view/client-dist/app.css +2 -0
  91. package/src/view/client-dist/app.js +56 -0
  92. package/src/view/index.ts +406 -0
  93. package/src/view/styles.css +1074 -0
  94. package/src/view/template.html +15 -0
@@ -0,0 +1,1074 @@
1
+ @import "tailwindcss";
2
+
3
+ /* 把现有设计令牌暴露给 Tailwind:既保留 var(--panel) 给旧 CSS 用,又生成 bg-panel / text-muted /
4
+ border-line 等 utility,新组件(shadcn)直接用 token 配色,深浅色仍由 light-dark() 驱动。 */
5
+ @theme {
6
+ --color-bg: var(--bg);
7
+ --color-panel: var(--panel);
8
+ --color-panel-2: var(--panel-2);
9
+ --color-line: var(--line);
10
+ --color-line-strong: var(--line-strong);
11
+ --color-text: var(--text);
12
+ --color-muted: var(--muted);
13
+ --color-soft: var(--soft);
14
+ --color-good: var(--good);
15
+ --color-bad: var(--bad);
16
+ --color-warn: var(--warn);
17
+ --color-focus: var(--focus);
18
+ }
19
+
20
+ :root {
21
+ color-scheme: light dark;
22
+ --bg: light-dark(#fafafa, #050505);
23
+ --panel: light-dark(#ffffff, #0b0b0b);
24
+ --panel-2: light-dark(#f4f4f5, #111111);
25
+ --line: light-dark(#dedee2, #262626);
26
+ --line-strong: light-dark(#c9c9cf, #343434);
27
+ --text: light-dark(#111113, #ededed);
28
+ --muted: light-dark(#62636a, #a1a1aa);
29
+ --soft: light-dark(#8b8d98, #74747b);
30
+ --good: light-dark(#087f5b, #3ddc97);
31
+ --bad: light-dark(#b42318, #ff6b6b);
32
+ --warn: light-dark(#9a6700, #e8b84a);
33
+ --focus: light-dark(#111113, #ededed);
34
+ font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
35
+ }
36
+
37
+ * { box-sizing: border-box; }
38
+ html {
39
+ overflow-y: scroll;
40
+ scrollbar-gutter: stable;
41
+ }
42
+ body {
43
+ margin: 0;
44
+ background: var(--bg);
45
+ color: var(--text);
46
+ letter-spacing: 0;
47
+ }
48
+ a { color: inherit; }
49
+ .topbar {
50
+ height: 64px;
51
+ border-bottom: 1px solid var(--line);
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: space-between;
55
+ padding: 0 clamp(20px, 5vw, 80px);
56
+ background: color-mix(in oklch, var(--bg), transparent 4%);
57
+ position: sticky;
58
+ top: 0;
59
+ z-index: 10;
60
+ backdrop-filter: blur(10px);
61
+ }
62
+ .brand {
63
+ display: flex;
64
+ align-items: baseline;
65
+ gap: 12px;
66
+ font-weight: 690;
67
+ font-size: 20px;
68
+ }
69
+ .mark {
70
+ width: 18px;
71
+ height: 18px;
72
+ border: 1.25px solid var(--text);
73
+ transform: rotate(45deg);
74
+ display: inline-block;
75
+ }
76
+ /* 顶部导航 .nav / .nav-tab → components/ui/tabs.tsx(Radix Tabs + Tailwind utilities)。 */
77
+ .lang-switch {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ border: 1px solid var(--line);
81
+ border-radius: 7px;
82
+ background: var(--panel);
83
+ overflow: hidden;
84
+ flex: 0 0 auto;
85
+ }
86
+ .lang-switch button {
87
+ height: 30px;
88
+ min-width: 44px;
89
+ border: 0;
90
+ border-right: 1px solid var(--line);
91
+ background: transparent;
92
+ color: var(--muted);
93
+ font: inherit;
94
+ font-size: 12px;
95
+ line-height: 1;
96
+ cursor: pointer;
97
+ }
98
+ .lang-switch button:last-child { border-right: 0; }
99
+ .lang-switch button:hover { color: var(--text); }
100
+ .lang-switch button.is-active {
101
+ background: var(--panel-2);
102
+ color: var(--text);
103
+ font-weight: 620;
104
+ }
105
+ main {
106
+ width: min(1120px, calc(100vw - 40px));
107
+ margin: 0 auto;
108
+ padding: clamp(52px, 7vw, 82px) 0 72px;
109
+ }
110
+ .hero {
111
+ text-align: center;
112
+ max-width: 740px;
113
+ margin: 0 auto clamp(38px, 6vw, 58px);
114
+ }
115
+ h1 {
116
+ margin: 0;
117
+ font-size: clamp(42px, 5.6vw, 66px);
118
+ line-height: 0.94;
119
+ font-weight: 760;
120
+ letter-spacing: 0;
121
+ }
122
+ .subtitle {
123
+ margin: 20px auto 0;
124
+ color: color-mix(in oklch, var(--muted), var(--text) 12%);
125
+ font-size: clamp(17px, 2vw, 21px);
126
+ line-height: 1.58;
127
+ max-width: 660px;
128
+ }
129
+ .meta {
130
+ display: flex;
131
+ flex-wrap: wrap;
132
+ justify-content: center;
133
+ gap: 14px;
134
+ margin-top: 24px;
135
+ color: var(--muted);
136
+ font-size: 14px;
137
+ }
138
+ .meta b { color: var(--text); font-weight: 570; }
139
+ .summary {
140
+ display: grid;
141
+ grid-template-columns: repeat(4, minmax(0, 1fr));
142
+ border: 1px solid var(--line);
143
+ border-radius: 8px;
144
+ overflow: hidden;
145
+ margin: 0 auto 36px;
146
+ background: linear-gradient(180deg, color-mix(in oklch, var(--panel), var(--panel-2) 18%), var(--panel));
147
+ max-width: 100%;
148
+ }
149
+ .metric {
150
+ padding: 14px 20px 15px;
151
+ border-right: 1px solid var(--line);
152
+ min-width: 0;
153
+ display: grid;
154
+ grid-template-columns: 1fr auto;
155
+ align-items: baseline;
156
+ column-gap: 12px;
157
+ }
158
+ .metric:last-child { border-right: 0; }
159
+ .metric .label {
160
+ color: var(--soft);
161
+ font-size: 12px;
162
+ text-transform: uppercase;
163
+ letter-spacing: 0.08em;
164
+ }
165
+ .metric .value {
166
+ margin-top: 0;
167
+ font-size: clamp(19px, 2vw, 24px);
168
+ line-height: 1;
169
+ font-weight: 680;
170
+ font-variant-numeric: tabular-nums;
171
+ white-space: nowrap;
172
+ }
173
+ .section-head {
174
+ display: flex;
175
+ align-items: end;
176
+ justify-content: space-between;
177
+ gap: 18px;
178
+ margin-bottom: 16px;
179
+ }
180
+ h2 {
181
+ margin: 0;
182
+ font-size: clamp(24px, 2.6vw, 31px);
183
+ line-height: 1.1;
184
+ font-weight: 720;
185
+ }
186
+ .controls {
187
+ display: flex;
188
+ gap: 8px;
189
+ align-items: center;
190
+ flex-wrap: wrap;
191
+ }
192
+ .search {
193
+ height: 36px;
194
+ min-width: 320px;
195
+ border: 1px solid var(--line);
196
+ border-radius: 7px;
197
+ padding: 0 12px;
198
+ background: var(--panel);
199
+ color: var(--text);
200
+ outline: none;
201
+ }
202
+ .search:focus { border-color: var(--focus); }
203
+ .table-wrap {
204
+ border: 1px solid var(--line);
205
+ border-radius: 8px;
206
+ overflow: auto;
207
+ background: var(--panel);
208
+ }
209
+ table {
210
+ width: 100%;
211
+ border-collapse: collapse;
212
+ min-width: 1040px;
213
+ }
214
+ th, td {
215
+ border-bottom: 1px solid var(--line);
216
+ padding: 0 16px;
217
+ height: 52px;
218
+ text-align: left;
219
+ vertical-align: middle;
220
+ font-size: 14px;
221
+ }
222
+ th {
223
+ color: var(--muted);
224
+ font-weight: 500;
225
+ background: var(--panel);
226
+ position: sticky;
227
+ top: 0;
228
+ z-index: 2;
229
+ }
230
+ th button {
231
+ color: inherit;
232
+ background: transparent;
233
+ border: 0;
234
+ padding: 0;
235
+ cursor: pointer;
236
+ font: inherit;
237
+ height: 100%;
238
+ }
239
+ th button::after {
240
+ content: "↕";
241
+ margin-left: 6px;
242
+ color: var(--soft);
243
+ font-size: 11px;
244
+ }
245
+ th button[data-sorted="asc"]::after { content: "↑"; color: var(--text); }
246
+ th button[data-sorted="desc"]::after { content: "↓"; color: var(--text); }
247
+ tbody tr.main-row { cursor: pointer; }
248
+ tbody tr.main-row:hover { background: color-mix(in oklch, var(--panel-2), var(--panel) 34%); }
249
+ tbody tr.is-open { background: var(--panel-2); }
250
+ tbody tr:last-child td { border-bottom: 0; }
251
+ .group-selector {
252
+ display: flex;
253
+ gap: 12px;
254
+ flex-wrap: wrap;
255
+ margin-bottom: 20px;
256
+ }
257
+ .group-card {
258
+ flex: 1;
259
+ min-width: 140px;
260
+ border: 1px solid var(--line);
261
+ border-radius: 8px;
262
+ padding: 16px 20px;
263
+ cursor: pointer;
264
+ background: var(--panel);
265
+ outline: none;
266
+ transition: border-color 120ms, background 120ms;
267
+ }
268
+ .group-card:hover { border-color: var(--line-strong); background: var(--panel-2); }
269
+ .group-card.is-selected { border-color: var(--text); background: var(--panel-2); }
270
+ .group-card-name { font-size: 13px; font-weight: 680; color: var(--muted); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.05em; }
271
+ .group-card-rate { font-size: 30px; font-weight: 760; font-variant-numeric: tabular-nums; line-height: 1; margin-bottom: 6px; }
272
+ .group-card-meta { font-size: 12px; color: var(--muted); }
273
+ .group-card-time { font-size: 11px; color: var(--soft); margin-top: 2px; }
274
+ .section-sub-head {
275
+ display: flex;
276
+ align-items: center;
277
+ justify-content: space-between;
278
+ gap: 18px;
279
+ margin-bottom: 16px;
280
+ min-height: 36px;
281
+ }
282
+ .group-detail-label {
283
+ font-size: 15px;
284
+ font-weight: 680;
285
+ color: var(--text);
286
+ }
287
+ .chev {
288
+ display: inline-block;
289
+ width: 9px;
290
+ height: 9px;
291
+ border-right: 1.5px solid var(--muted);
292
+ border-bottom: 1.5px solid var(--muted);
293
+ transform: rotate(-45deg);
294
+ margin-right: 12px;
295
+ transition: transform 160ms ease;
296
+ }
297
+ .is-open .chev { transform: rotate(45deg); }
298
+ .chev-icon {
299
+ display: inline-block;
300
+ width: 14px;
301
+ height: 14px;
302
+ flex-shrink: 0;
303
+ margin-right: 9px;
304
+ color: var(--muted);
305
+ vertical-align: -2px;
306
+ transition: transform 160ms ease;
307
+ }
308
+ .is-open .chev-icon { transform: rotate(90deg); }
309
+ .name { font-weight: 610; }
310
+ .sub { color: var(--muted); font-size: 12px; margin-top: 3px; }
311
+ .num {
312
+ font-variant-numeric: tabular-nums;
313
+ white-space: nowrap;
314
+ font-feature-settings: "tnum";
315
+ }
316
+ .good { color: var(--good); }
317
+ .bad { color: var(--bad); }
318
+ .warn { color: var(--warn); }
319
+ .infra-err { color: var(--warn); }
320
+ .pill {
321
+ display: inline-flex;
322
+ align-items: center;
323
+ height: 22px;
324
+ border: 1px solid var(--line);
325
+ border-radius: 999px;
326
+ padding: 0 8px;
327
+ color: var(--muted);
328
+ font-size: 12px;
329
+ white-space: nowrap;
330
+ }
331
+ .detail-cell {
332
+ padding: 0;
333
+ background: color-mix(in oklch, var(--panel-2), var(--panel) 24%);
334
+ }
335
+ .detail {
336
+ padding: 20px 30px 24px;
337
+ border-bottom: 1px solid var(--line);
338
+ }
339
+ .config-strip {
340
+ display: flex;
341
+ flex-wrap: wrap;
342
+ gap: 8px;
343
+ margin: 0 0 18px;
344
+ }
345
+ .config-chip {
346
+ display: inline-flex;
347
+ align-items: center;
348
+ gap: 6px;
349
+ min-height: 26px;
350
+ border: 1px solid color-mix(in oklch, var(--line), transparent 16%);
351
+ border-radius: 999px;
352
+ padding: 4px 9px;
353
+ color: var(--muted);
354
+ font-size: 12px;
355
+ line-height: 1.2;
356
+ }
357
+ .config-chip b {
358
+ color: var(--text);
359
+ font-weight: 560;
360
+ }
361
+ .detail-kpis {
362
+ display: grid;
363
+ grid-template-columns: repeat(7, minmax(0, 1fr));
364
+ border-bottom: 1px solid var(--line);
365
+ margin: -2px 0 18px;
366
+ }
367
+ .detail-kpi {
368
+ padding: 0 18px 16px 0;
369
+ min-width: 0;
370
+ }
371
+ .detail-kpi span {
372
+ display: block;
373
+ color: var(--soft);
374
+ font-size: 11px;
375
+ text-transform: uppercase;
376
+ letter-spacing: 0.07em;
377
+ }
378
+ .detail-kpi b {
379
+ display: block;
380
+ margin-top: 7px;
381
+ font-size: 18px;
382
+ font-weight: 680;
383
+ font-variant-numeric: tabular-nums;
384
+ }
385
+ .detail h3 {
386
+ margin: 0 0 14px;
387
+ font-size: 13px;
388
+ color: var(--muted);
389
+ font-weight: 580;
390
+ text-transform: uppercase;
391
+ letter-spacing: 0.06em;
392
+ }
393
+ .eval-list {
394
+ display: grid;
395
+ max-height: 430px;
396
+ overflow: auto;
397
+ padding-right: 4px;
398
+ }
399
+ .eval-grid-head,
400
+ .eval-item {
401
+ display: grid;
402
+ grid-template-columns: 76px minmax(220px, 1.1fr) minmax(260px, 1fr) 84px 86px 82px 54px;
403
+ column-gap: 16px;
404
+ align-items: baseline;
405
+ padding: 11px 0;
406
+ border-bottom: 1px solid color-mix(in oklch, var(--line), transparent 35%);
407
+ font-size: 13px;
408
+ }
409
+ .eval-grid-head {
410
+ position: sticky;
411
+ top: 0;
412
+ z-index: 1;
413
+ background: color-mix(in oklch, var(--panel-2), var(--panel) 24%);
414
+ color: var(--soft);
415
+ font-size: 11px;
416
+ text-transform: uppercase;
417
+ letter-spacing: 0.06em;
418
+ }
419
+ .eval-id {
420
+ display: flex;
421
+ align-items: center;
422
+ overflow-wrap: anywhere;
423
+ font-weight: 560;
424
+ }
425
+ .eval-id:not(:has(.chev-icon)) {
426
+ padding-left: 23px; /* 14px 图标 + 9px 间距,单轮 eval 没有展开箭头,靠 padding 补位对齐 */
427
+ }
428
+ .assertions-cell {
429
+ min-width: 0;
430
+ display: flex;
431
+ align-items: flex-start;
432
+ gap: 8px;
433
+ overflow-wrap: anywhere;
434
+ line-height: 1.25;
435
+ }
436
+ .assertions {
437
+ min-width: 0;
438
+ flex: 1;
439
+ color: color-mix(in oklch, var(--muted), var(--text) 6%);
440
+ display: -webkit-box;
441
+ -webkit-box-orient: vertical;
442
+ -webkit-line-clamp: 2;
443
+ line-clamp: 2;
444
+ overflow: hidden;
445
+ }
446
+ .assertions-link { cursor: pointer; }
447
+ .assertions-link:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
448
+ .copy-reason {
449
+ width: 26px;
450
+ height: 26px;
451
+ display: inline-grid;
452
+ place-items: center;
453
+ flex: 0 0 auto;
454
+ background: var(--panel);
455
+ border: 1px solid var(--line);
456
+ border-radius: 6px;
457
+ cursor: pointer;
458
+ color: var(--muted);
459
+ font-family: inherit;
460
+ opacity: 0;
461
+ transition: opacity 0.1s, color 0.1s, border-color 0.1s, background 0.1s;
462
+ }
463
+ .copy-reason svg {
464
+ width: 14px;
465
+ height: 14px;
466
+ fill: none;
467
+ stroke: currentColor;
468
+ stroke-width: 2;
469
+ stroke-linecap: round;
470
+ stroke-linejoin: round;
471
+ }
472
+ .eval-item:hover .copy-reason,
473
+ .copy-reason:focus-visible,
474
+ .copy-reason.is-copied { opacity: 1; }
475
+ .copy-reason:hover { color: var(--text); border-color: var(--line-strong); }
476
+ .copy-reason.is-copied {
477
+ color: var(--good);
478
+ border-color: color-mix(in oklch, var(--good), var(--line) 40%);
479
+ background: color-mix(in oklch, var(--good), transparent 88%);
480
+ }
481
+ .copy-all-errors {
482
+ height: 36px;
483
+ display: inline-flex;
484
+ align-items: center;
485
+ gap: 6px;
486
+ padding: 0 12px;
487
+ border: 1px solid var(--line);
488
+ border-radius: 7px;
489
+ background: var(--panel);
490
+ color: var(--muted);
491
+ font-family: inherit;
492
+ font-size: 13px;
493
+ cursor: pointer;
494
+ white-space: nowrap;
495
+ flex-shrink: 0;
496
+ transition: color 0.12s, border-color 0.12s, background 0.12s;
497
+ }
498
+ .copy-all-errors svg {
499
+ width: 14px;
500
+ height: 14px;
501
+ fill: none;
502
+ stroke: currentColor;
503
+ stroke-width: 2;
504
+ stroke-linecap: round;
505
+ stroke-linejoin: round;
506
+ }
507
+ .copy-all-errors:hover {
508
+ color: var(--text);
509
+ border-color: var(--line-strong);
510
+ }
511
+ .copy-all-errors.is-copied {
512
+ color: var(--good);
513
+ border-color: color-mix(in oklch, var(--good), var(--line) 40%);
514
+ background: color-mix(in oklch, var(--good), transparent 88%);
515
+ }
516
+ .raw-details {
517
+ margin-top: 16px;
518
+ border-top: 1px solid color-mix(in oklch, var(--line), transparent 15%);
519
+ padding-top: 14px;
520
+ color: color-mix(in oklch, var(--muted), var(--text) 6%);
521
+ }
522
+ .raw-details summary {
523
+ cursor: pointer;
524
+ color: var(--muted);
525
+ font-size: 13px;
526
+ font-weight: 560;
527
+ user-select: none;
528
+ }
529
+ .raw-details pre { margin-top: 12px; }
530
+ .raw-note {
531
+ margin-left: 8px;
532
+ color: var(--soft);
533
+ font-weight: 400;
534
+ }
535
+ .reason-empty { color: var(--soft); }
536
+ .ran-at { display: block; margin-top: 2px; color: var(--soft); font-size: 11px; font-variant-numeric: tabular-nums; }
537
+
538
+ .attempt-status {
539
+ display: flex;
540
+ align-items: center;
541
+ gap: 4px;
542
+ min-width: 0;
543
+ }
544
+
545
+
546
+ .eval-item-clickable {
547
+ cursor: pointer;
548
+ user-select: none;
549
+ }
550
+ .eval-item-clickable:hover {
551
+ background: color-mix(in oklch, var(--panel-2), var(--panel) 30%);
552
+ }
553
+ /* eval 折叠行:Run 列的 通过/总 轮次比 */
554
+ .run-ratio { color: var(--soft); }
555
+ /* 展开后的各轮 attempt:左侧加描边 + 轻微缩进,标识它们从属于上面的 eval 行 */
556
+ .eval-attempts {
557
+ display: grid;
558
+ margin-left: 8px;
559
+ border-left: 2px solid color-mix(in oklch, var(--accent, var(--line)), transparent 55%);
560
+ background: color-mix(in oklch, var(--panel-2), transparent 55%);
561
+ }
562
+ .eval-attempts .eval-item {
563
+ padding-left: 12px;
564
+ }
565
+ .eval-attempts .eval-item:last-child {
566
+ border-bottom: none;
567
+ }
568
+ /* Modal */
569
+ /* modal chrome(backdrop / panel / header / title / close / body / outcome chip)→
570
+ 迁到 components/ui/dialog.tsx + components/ui/badge.tsx + AttemptModal 的 Tailwind utilities。 */
571
+
572
+ /* No-source modal body: same visual language as the code view (checks + transcript). */
573
+ .nosource { display: flex; flex-direction: column; gap: 14px; }
574
+ .nosource-note {
575
+ font-size: 12.5px;
576
+ line-height: 1.55;
577
+ color: var(--muted);
578
+ padding: 9px 12px;
579
+ border: 1px dashed color-mix(in oklch, var(--line-strong), transparent 25%);
580
+ border-radius: 8px;
581
+ background: color-mix(in oklch, var(--panel), var(--line) 20%);
582
+ }
583
+ .nosource-block {
584
+ border: 1px solid var(--line);
585
+ border-radius: 10px;
586
+ overflow: hidden;
587
+ background: var(--panel-2);
588
+ }
589
+ .nosource-head {
590
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
591
+ font-size: 11px;
592
+ color: var(--muted);
593
+ padding: 7px 12px;
594
+ background: color-mix(in oklch, var(--panel-2), var(--line) 35%);
595
+ border-bottom: 1px solid var(--line);
596
+ }
597
+ .nosource-block > .assert-detail,
598
+ .nosource-block > .transcript { padding: 10px 14px; }
599
+ .conv-loading { color: var(--soft); font-size: 12px; padding: 8px 0; }
600
+ .trace-details {
601
+ border-bottom: 1px solid color-mix(in oklch, var(--line), transparent 35%);
602
+ padding: 4px 0 10px;
603
+ }
604
+ .trace-details summary {
605
+ cursor: pointer;
606
+ color: var(--muted);
607
+ font-size: 12px;
608
+ user-select: none;
609
+ padding: 4px 0;
610
+ }
611
+ .trace-details summary:hover { color: var(--text); }
612
+ .trace {
613
+ margin: 8px 0 4px;
614
+ padding: 10px 12px;
615
+ border: 1px solid color-mix(in oklch, var(--line), transparent 20%);
616
+ border-radius: 7px;
617
+ background: color-mix(in oklch, var(--panel), black 6%);
618
+ }
619
+ .trace-span-meta {
620
+ color: var(--soft);
621
+ font-size: 11px;
622
+ text-transform: uppercase;
623
+ letter-spacing: 0.06em;
624
+ margin-bottom: 8px;
625
+ }
626
+ .span-d { border-radius: 4px; }
627
+ .span-d > .trace-row { cursor: default; }
628
+ details.span-d > summary.trace-row { cursor: pointer; list-style: none; }
629
+ details.span-d > summary.trace-row::-webkit-details-marker { display: none; }
630
+ details.span-d[open] > summary.trace-row { background: color-mix(in oklch, var(--panel-2), var(--panel) 10%); }
631
+ details.span-d > summary.trace-row:hover { background: color-mix(in oklch, var(--panel-2), var(--panel) 20%); }
632
+ .trace-row {
633
+ display: grid;
634
+ grid-template-columns: minmax(140px, 0.9fr) minmax(0, 2fr) 56px;
635
+ column-gap: 12px;
636
+ align-items: center;
637
+ height: 22px;
638
+ font-size: 12px;
639
+ }
640
+ .span-attrs {
641
+ padding: 6px 8px 10px 16px;
642
+ margin: 2px 0 6px;
643
+ border-left: 2px solid color-mix(in oklch, var(--line-strong), transparent 30%);
644
+ font-size: 12px;
645
+ }
646
+ .attr-row { display: grid; grid-template-columns: minmax(120px, 220px) 1fr; column-gap: 12px; padding: 2px 0; }
647
+ .attr-k { color: var(--soft); overflow-wrap: anywhere; }
648
+ .attr-v { color: var(--text); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
649
+ .attr-io { margin: 4px 0; }
650
+ .attr-io .attr-k { display: block; margin-bottom: 3px; text-transform: uppercase; letter-spacing: 0.05em; font-size: 11px; }
651
+ .attr-pre {
652
+ margin: 0;
653
+ padding: 8px 10px;
654
+ max-height: 220px;
655
+ overflow: auto;
656
+ border: 1px solid var(--line);
657
+ border-radius: 6px;
658
+ background: color-mix(in oklch, var(--panel), black 8%);
659
+ color: var(--muted);
660
+ font-size: 11px;
661
+ line-height: 1.45;
662
+ white-space: pre-wrap;
663
+ overflow-wrap: anywhere;
664
+ }
665
+ .trace-label {
666
+ overflow: hidden;
667
+ text-overflow: ellipsis;
668
+ white-space: nowrap;
669
+ color: color-mix(in oklch, var(--muted), var(--text) 12%);
670
+ }
671
+ .trace-track {
672
+ position: relative;
673
+ height: 12px;
674
+ border-radius: 3px;
675
+ background: color-mix(in oklch, var(--panel-2), var(--panel) 20%);
676
+ }
677
+ .trace-bar {
678
+ position: absolute;
679
+ top: 0;
680
+ height: 12px;
681
+ min-width: 2px;
682
+ border-radius: 3px;
683
+ background: var(--good);
684
+ }
685
+ .trace-bar.bad { background: var(--bad); }
686
+ /* canonical SpanKind 配色:跨 agent 同义同色(model/tool/agent/turn);raw 名不参与着色。 */
687
+ .trace-bar.k-model { background: oklch(0.72 0.13 250); } /* 模型调用 — 蓝 */
688
+ .trace-bar.k-tool { background: oklch(0.74 0.14 150); } /* 工具执行 — 绿 */
689
+ .trace-bar.k-agent { background: oklch(0.74 0.15 300); } /* 子 agent — 紫 */
690
+ .trace-bar.k-turn { background: oklch(0.78 0.13 90); } /* 回合骨架 — 黄 */
691
+ .trace-bar.k-other { background: color-mix(in oklch, var(--soft), var(--panel) 30%); } /* plumbing — 灰 */
692
+ .trace-dur { color: var(--soft); text-align: right; }
693
+ /* kind 小标:span label 前的语义角色徽标,和 bar 同色系。 */
694
+ .kind-chip {
695
+ display: inline-block;
696
+ margin-right: 6px;
697
+ padding: 0 5px;
698
+ border-radius: 3px;
699
+ font-size: 10px;
700
+ font-weight: 600;
701
+ text-transform: uppercase;
702
+ letter-spacing: 0.03em;
703
+ color: var(--panel);
704
+ vertical-align: middle;
705
+ }
706
+ .kind-chip.k-model { background: oklch(0.72 0.13 250); }
707
+ .kind-chip.k-tool { background: oklch(0.74 0.14 150); }
708
+ .kind-chip.k-agent { background: oklch(0.74 0.15 300); }
709
+ .kind-chip.k-turn { background: oklch(0.78 0.13 90); }
710
+
711
+ /* ── Claude-Code 风格 transcript:按事件顺序读「LLM 说了什么 / 调了什么工具」,不强调时间 ── */
712
+ .transcript { margin: 8px 0 4px; display: flex; flex-direction: column; gap: 6px; }
713
+ .ts-msg {
714
+ padding: 8px 11px;
715
+ border-radius: 7px;
716
+ border: 1px solid color-mix(in oklch, var(--line), transparent 30%);
717
+ background: color-mix(in oklch, var(--panel), black 4%);
718
+ }
719
+ .ts-assistant { border-left: 2px solid var(--good); }
720
+ .ts-user { border-left: 2px solid var(--line-strong); }
721
+ .ts-input { border-left: 2px solid var(--warn); }
722
+ .ts-role {
723
+ display: block;
724
+ font-size: 10px;
725
+ text-transform: uppercase;
726
+ letter-spacing: 0.07em;
727
+ color: var(--soft);
728
+ margin-bottom: 4px;
729
+ }
730
+ .ts-text {
731
+ white-space: pre-wrap;
732
+ overflow-wrap: anywhere;
733
+ font-size: 13px;
734
+ line-height: 1.55;
735
+ color: var(--text);
736
+ max-height: 360px;
737
+ overflow: auto;
738
+ }
739
+ .ts-think { font-size: 12px; }
740
+ .ts-think > summary {
741
+ cursor: pointer;
742
+ color: var(--soft);
743
+ font-style: italic;
744
+ list-style: none;
745
+ padding: 2px 0;
746
+ }
747
+ .ts-think > summary::-webkit-details-marker { display: none; }
748
+ .ts-think > summary::before { content: "\2726 "; }
749
+ .ts-think-text {
750
+ white-space: pre-wrap;
751
+ overflow-wrap: anywhere;
752
+ color: var(--muted);
753
+ font-style: italic;
754
+ padding: 4px 0 6px 16px;
755
+ max-height: 280px;
756
+ overflow: auto;
757
+ }
758
+ .ts-tool-d {
759
+ border: 1px solid color-mix(in oklch, var(--line), transparent 30%);
760
+ border-radius: 7px;
761
+ background: color-mix(in oklch, var(--panel), black 6%);
762
+ }
763
+ .ts-tool-d > summary.ts-row {
764
+ list-style: none;
765
+ cursor: pointer;
766
+ display: grid;
767
+ grid-template-columns: 8px minmax(110px, max-content) 1fr;
768
+ align-items: center;
769
+ column-gap: 9px;
770
+ padding: 7px 11px;
771
+ }
772
+ .ts-tool-d > summary.ts-row::-webkit-details-marker { display: none; }
773
+ .ts-tool-d[open] > summary.ts-row { border-bottom: 1px solid color-mix(in oklch, var(--line), transparent 40%); }
774
+ .ts-tool-d > summary.ts-row:hover { background: color-mix(in oklch, var(--panel-2), var(--panel) 20%); }
775
+ .ts-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); }
776
+ .ts-dot.bad { background: var(--bad); }
777
+ .ts-dot.warn { background: var(--warn); }
778
+ .ts-dot.pending { background: var(--soft); }
779
+ .ts-tool {
780
+ font-weight: 600;
781
+ font-size: 12.5px;
782
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
783
+ color: var(--text);
784
+ white-space: nowrap;
785
+ overflow: hidden;
786
+ text-overflow: ellipsis;
787
+ }
788
+ .ts-preview { font-size: 12px; color: var(--soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
789
+ .ts-body { padding: 8px 11px 10px; display: flex; flex-direction: column; gap: 8px; }
790
+ .ts-field .ts-k {
791
+ display: block;
792
+ font-size: 10px;
793
+ text-transform: uppercase;
794
+ letter-spacing: 0.05em;
795
+ color: var(--soft);
796
+ margin-bottom: 3px;
797
+ }
798
+ .ts-error {
799
+ padding: 8px 11px;
800
+ border-radius: 7px;
801
+ border: 1px solid color-mix(in oklch, var(--bad), transparent 55%);
802
+ color: var(--bad);
803
+ font-size: 12.5px;
804
+ }
805
+ .ts-compaction {
806
+ text-align: center;
807
+ font-size: 11px;
808
+ color: var(--soft);
809
+ text-transform: uppercase;
810
+ letter-spacing: 0.06em;
811
+ padding: 2px 0;
812
+ }
813
+ pre {
814
+ margin: 0;
815
+ padding: 12px;
816
+ border: 1px solid var(--line);
817
+ border-radius: 7px;
818
+ background: color-mix(in oklch, var(--panel), black 8%);
819
+ color: var(--muted);
820
+ overflow-wrap: anywhere;
821
+ overflow: auto;
822
+ max-height: 360px;
823
+ font-size: 12px;
824
+ line-height: 1.5;
825
+ }
826
+ .empty {
827
+ border: 1px dashed var(--line-strong);
828
+ border-radius: 8px;
829
+ padding: 40px 24px;
830
+ color: var(--muted);
831
+ text-align: center;
832
+ background: var(--panel);
833
+ }
834
+ a.brand { text-decoration: none; color: inherit; }
835
+ a.brand:hover { opacity: 0.75; }
836
+ .traces-entry {
837
+ border: 1px solid var(--line);
838
+ border-radius: 8px;
839
+ background: var(--panel);
840
+ margin-bottom: 12px;
841
+ padding: 14px 18px;
842
+ }
843
+ .traces-entry-head {
844
+ display: flex;
845
+ align-items: baseline;
846
+ gap: 12px;
847
+ margin-bottom: 8px;
848
+ font-size: 13px;
849
+ }
850
+ .traces-verdict { font-weight: 600; min-width: 52px; }
851
+ .traces-exp { color: var(--muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
852
+ .traces-dur { color: var(--soft); }
853
+ @media (max-width: 760px) {
854
+ .topbar { padding: 0 20px; }
855
+ .lang-switch button { min-width: 40px; }
856
+ main { width: min(100vw - 28px, 1120px); padding-top: 42px; }
857
+ .hero { text-align: left; margin-left: 0; }
858
+ .meta { justify-content: flex-start; }
859
+ .summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
860
+ .metric { grid-template-columns: 1fr; row-gap: 8px; }
861
+ .metric:nth-child(2) { border-right: 0; }
862
+ .metric:nth-child(-n+2) { border-bottom: 1px solid var(--line); }
863
+ .section-head { align-items: stretch; flex-direction: column; }
864
+ .search { width: 100%; min-width: 0; }
865
+ .detail { padding: 18px 16px 20px; }
866
+ .config-strip { gap: 6px; }
867
+ .detail-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 10px; }
868
+ .eval-grid-head,
869
+ .eval-item { grid-template-columns: 72px minmax(180px, 1fr) minmax(220px, 1fr) 76px 78px 72px 50px; }
870
+ }
871
+
872
+ /* ───────────────────────── Modal: error + code view (outcome chip → components/ui/badge.tsx) ───────────────────────── */
873
+ .modal-error {
874
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
875
+ font-size: 12px;
876
+ color: var(--bad);
877
+ background: color-mix(in oklch, var(--bad), transparent 92%);
878
+ border: 1px solid color-mix(in oklch, var(--bad), var(--line) 50%);
879
+ border-radius: 8px;
880
+ padding: 8px 10px;
881
+ margin-bottom: 12px;
882
+ white-space: pre-wrap;
883
+ word-break: break-word;
884
+ }
885
+
886
+ .codeview { display: flex; flex-direction: column; gap: 14px; }
887
+
888
+ .code-file {
889
+ border: 1px solid var(--line);
890
+ border-radius: 10px;
891
+ overflow: hidden;
892
+ background: var(--panel-2);
893
+ }
894
+ .code-file-head {
895
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
896
+ font-size: 11px;
897
+ color: var(--muted);
898
+ padding: 7px 12px;
899
+ background: color-mix(in oklch, var(--panel-2), var(--line) 35%);
900
+ border-bottom: 1px solid var(--line);
901
+ }
902
+ .code-lines {
903
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
904
+ font-size: 12.5px;
905
+ line-height: 1.7;
906
+ }
907
+
908
+ .code-line {
909
+ display: grid;
910
+ grid-template-columns: 46px 22px minmax(0, 1fr) auto;
911
+ align-items: center;
912
+ border-left: 3px solid transparent;
913
+ }
914
+ .code-line.line-clickable { cursor: pointer; }
915
+ .code-line.line-clickable:hover { background: color-mix(in oklch, var(--panel), var(--line) 45%); }
916
+ .code-line.line-clickable:hover .gchev { color: var(--text); }
917
+ .code-line.line-send { border-left-color: var(--line-strong); }
918
+ .code-line.is-open { background: color-mix(in oklch, var(--panel), var(--line) 34%); }
919
+
920
+ /* github-diff 式整行红绿:行底色 + 更深的行号格 + 实色左缘(过=绿/加,不过=红/减)。 */
921
+ .code-line.line-pass {
922
+ background: color-mix(in oklch, var(--good), var(--panel) 74%);
923
+ border-left-color: var(--good);
924
+ }
925
+ .code-line.line-pass .ln {
926
+ background: color-mix(in oklch, var(--good), var(--panel) 52%);
927
+ color: color-mix(in oklch, var(--good), var(--text) 45%);
928
+ }
929
+ .code-line.line-fail {
930
+ background: color-mix(in oklch, var(--bad), var(--panel) 74%);
931
+ border-left-color: var(--bad);
932
+ }
933
+ .code-line.line-fail .ln {
934
+ background: color-mix(in oklch, var(--bad), var(--panel) 52%);
935
+ color: color-mix(in oklch, var(--bad), var(--text) 45%);
936
+ }
937
+ /* soft 断言没过阈值:不影响 outcome,视觉上跟真正的 gate 失败(红)区分开,用 warn(黄)。 */
938
+ .code-line.line-warn {
939
+ background: color-mix(in oklch, var(--warn), var(--panel) 74%);
940
+ border-left-color: var(--warn);
941
+ }
942
+ .code-line.line-warn .ln {
943
+ background: color-mix(in oklch, var(--warn), var(--panel) 52%);
944
+ color: color-mix(in oklch, var(--warn), var(--text) 45%);
945
+ }
946
+ .code-line.line-pass.line-clickable:hover { background: color-mix(in oklch, var(--good), var(--panel) 64%); }
947
+ .code-line.line-fail.line-clickable:hover { background: color-mix(in oklch, var(--bad), var(--panel) 64%); }
948
+ .code-line.line-warn.line-clickable:hover { background: color-mix(in oklch, var(--warn), var(--panel) 64%); }
949
+
950
+ .ln {
951
+ text-align: right;
952
+ font-size: 11px;
953
+ color: var(--soft);
954
+ user-select: none;
955
+ padding: 1px 8px 1px 0;
956
+ align-self: stretch;
957
+ display: flex;
958
+ align-items: center;
959
+ justify-content: flex-end;
960
+ }
961
+ .gmark { display: inline-flex; align-items: center; justify-content: center; }
962
+ .gchev { width: 12px; height: 12px; color: var(--muted); transition: transform 0.12s; }
963
+ .gchev.is-open { transform: rotate(90deg); }
964
+ .gstat { font-size: 13px; font-weight: 800; line-height: 1; }
965
+ .gstat.good { color: var(--good); }
966
+ .gstat.bad { color: var(--bad); }
967
+ .gstat.warn { color: var(--warn); }
968
+
969
+ .ctext { white-space: pre; overflow-x: auto; color: var(--text); padding-left: 8px; }
970
+ .ctext::-webkit-scrollbar { height: 0; }
971
+ .lbadges { padding-right: 12px; }
972
+
973
+ .lbadges { display: inline-flex; gap: 6px; align-items: center; white-space: nowrap; justify-self: end; }
974
+ .abadge {
975
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
976
+ font-size: 11px;
977
+ font-weight: 650;
978
+ padding: 0 6px;
979
+ border-radius: 5px;
980
+ border: 1px solid transparent;
981
+ }
982
+ .abadge.good { color: var(--good); border-color: color-mix(in oklch, var(--good), var(--line) 55%); background: color-mix(in oklch, var(--good), transparent 90%); }
983
+ .abadge.bad { color: var(--bad); border-color: color-mix(in oklch, var(--bad), var(--line) 55%); background: color-mix(in oklch, var(--bad), transparent 90%); }
984
+ .abadge.warn { color: var(--warn); border-color: color-mix(in oklch, var(--warn), var(--line) 55%); background: color-mix(in oklch, var(--warn), transparent 90%); }
985
+ .abadge-th { opacity: 0.55; font-weight: 400; }
986
+
987
+ /* 可点击行的显式提示:send 行一个 "reply" 药丸,断言行一个右侧展开箭头;hover 时变亮。 */
988
+ .reply-hint {
989
+ font-size: 10px;
990
+ font-weight: 650;
991
+ letter-spacing: 0.02em;
992
+ color: var(--muted);
993
+ border: 1px solid color-mix(in oklch, var(--line-strong), transparent 12%);
994
+ border-radius: 5px;
995
+ padding: 1px 7px;
996
+ background: color-mix(in oklch, var(--panel), var(--line) 28%);
997
+ transition: color 0.12s, border-color 0.12s, background 0.12s;
998
+ }
999
+ .code-line.line-clickable:hover .reply-hint {
1000
+ color: var(--text);
1001
+ border-color: var(--line-strong);
1002
+ background: color-mix(in oklch, var(--panel), var(--line) 52%);
1003
+ }
1004
+ .line-chev {
1005
+ width: 14px;
1006
+ height: 14px;
1007
+ color: var(--soft);
1008
+ transition: transform 0.12s, color 0.12s;
1009
+ }
1010
+ .line-chev.is-open { transform: rotate(90deg); }
1011
+ .code-line.line-clickable:hover .line-chev { color: var(--text); }
1012
+
1013
+ .line-detail {
1014
+ font-family: var(--sans, -apple-system, system-ui, "Segoe UI", sans-serif);
1015
+ white-space: normal;
1016
+ padding: 10px 16px 12px 60px;
1017
+ background: color-mix(in oklch, var(--panel), var(--line) 22%);
1018
+ border-left: 3px solid var(--line-strong);
1019
+ font-size: 12.5px;
1020
+ }
1021
+ .reply-panel { display: flex; flex-direction: column; gap: 9px; }
1022
+ .reply-role { font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--good); display: block; margin-bottom: 2px; }
1023
+ .reply-text { color: var(--text); white-space: pre-wrap; word-break: break-word; }
1024
+ .reply-think { font-size: 12px; color: var(--muted); }
1025
+ .reply-think summary { cursor: pointer; user-select: none; }
1026
+ .reply-think-text { white-space: pre-wrap; color: var(--soft); margin-top: 4px; padding-left: 4px; }
1027
+ .reply-tool { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--text); display: flex; gap: 8px; flex-wrap: wrap; }
1028
+ .reply-tool-name { color: var(--warn); }
1029
+ .reply-tool-out { color: var(--muted); }
1030
+ .reply-err { color: var(--bad); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
1031
+ .reply-empty { color: var(--soft); }
1032
+
1033
+ .assert-detail { display: flex; flex-direction: column; gap: 9px; }
1034
+ .assert-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 8px; align-items: baseline; }
1035
+ .assert-name { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--text); overflow-wrap: anywhere; }
1036
+ .assert-sev { font-size: 10px; color: var(--soft); text-transform: uppercase; letter-spacing: 0.04em; }
1037
+ .assert-score { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--muted); white-space: nowrap; }
1038
+ .assert-reason { grid-column: 1 / -1; color: var(--muted); white-space: pre-wrap; font-size: 12px; }
1039
+
1040
+ /* 可观测性:裁判看到的材料(默认折叠,展开排查「为什么是这个分」)。 */
1041
+ .assert-evidence { grid-column: 1 / -1; margin-top: 2px; }
1042
+ .assert-evidence > summary {
1043
+ cursor: pointer;
1044
+ user-select: none;
1045
+ font-size: 11px;
1046
+ color: var(--soft);
1047
+ letter-spacing: 0.02em;
1048
+ }
1049
+ .assert-evidence > summary:hover { color: var(--text); }
1050
+ .assert-evidence-pre {
1051
+ margin: 6px 0 0;
1052
+ padding: 8px 10px;
1053
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1054
+ font-size: 11.5px;
1055
+ line-height: 1.5;
1056
+ color: var(--text);
1057
+ white-space: pre-wrap;
1058
+ overflow-wrap: anywhere;
1059
+ background: color-mix(in oklch, var(--panel), var(--line) 24%);
1060
+ border: 1px solid var(--line);
1061
+ border-radius: 7px;
1062
+ max-height: 260px;
1063
+ overflow-y: auto;
1064
+ }
1065
+
1066
+ .code-orphans { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; }
1067
+ .code-orphans-head { font-size: 11px; color: var(--muted); margin-bottom: 8px; }
1068
+
1069
+ /* lightweight TS syntax tokens */
1070
+ .tok-comment { color: var(--soft); font-style: italic; }
1071
+ .tok-str { color: light-dark(#0a7d3c, #7ee787); }
1072
+ .tok-kw { color: light-dark(#9333ea, #c792ea); }
1073
+ .tok-num { color: light-dark(#b25000, #f0a36b); }
1074
+ .tok-fn { color: light-dark(#0b62d6, #82aaff); }