niceeval 0.9.0 → 0.10.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.
- package/INDEX.md +31 -33
- package/dist/agents/types.d.ts +20 -17
- package/dist/context/types.d.ts +21 -21
- package/dist/i18n/en.d.ts +10 -2
- package/dist/i18n/zh-CN.d.ts +15 -7
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/report/{react → assets}/colors.d.ts +8 -0
- package/dist/report/{react → assets}/colors.js +23 -0
- package/dist/report/built-in/index.d.ts +2 -2
- package/dist/report/built-in/index.js +2 -2
- package/dist/report/built-in/standard.d.ts +8 -1
- package/dist/report/built-in/standard.js +17 -7
- package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
- package/dist/report/components/attempt-detail/AttemptConversation.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptConversation.js +34 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.js +7 -0
- package/dist/report/components/attempt-detail/AttemptDiff.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiff.js +11 -0
- package/dist/report/components/attempt-detail/AttemptError.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptError.js +16 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.js +7 -0
- package/dist/report/components/attempt-detail/AttemptSource.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptSource.js +27 -0
- package/dist/report/components/attempt-detail/AttemptSummary.d.ts +8 -0
- package/dist/report/components/attempt-detail/AttemptSummary.js +20 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.js +28 -0
- package/dist/report/components/attempt-detail/AttemptTrace.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTrace.js +29 -0
- package/dist/report/components/attempt-detail/AttemptUsage.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptUsage.js +20 -0
- package/dist/report/components/attempt-detail/compute.d.ts +24 -0
- package/dist/report/components/attempt-detail/compute.js +254 -0
- package/dist/report/components/attempt-detail/faces.d.ts +14 -0
- package/dist/report/components/attempt-detail/faces.js +235 -0
- package/dist/report/components/attempt-detail/index.d.ts +38 -0
- package/dist/report/components/attempt-detail/index.js +527 -0
- package/dist/report/{react → components}/cell.d.ts +5 -3
- package/dist/report/{react → components}/cell.js +3 -3
- package/dist/report/{react → components/entity-lists}/AttemptList.d.ts +8 -4
- package/dist/report/{react → components/entity-lists}/AttemptList.js +16 -10
- package/dist/report/{react → components/entity-lists}/EvalList.d.ts +3 -3
- package/dist/report/{react → components/entity-lists}/EvalList.js +0 -0
- package/dist/report/{react → components/entity-lists}/ExperimentList.d.ts +3 -3
- package/dist/report/{react → components/entity-lists}/ExperimentList.js +10 -10
- package/dist/report/components/entity-lists/compute.d.ts +23 -0
- package/dist/report/components/entity-lists/compute.js +171 -0
- package/dist/report/components/entity-lists/faces.d.ts +5 -0
- package/dist/report/components/entity-lists/faces.js +174 -0
- package/dist/report/components/entity-lists/index.d.ts +51 -0
- package/dist/report/components/entity-lists/index.js +165 -0
- package/dist/report/{react → components}/fixtures.d.ts +2 -2
- package/dist/report/{react → components}/fixtures.js +1 -1
- package/dist/report/{react → components/metric-views}/DeltaTable.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/DeltaTable.js +4 -4
- package/dist/report/{react → components/metric-views}/MetricBars.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricBars.js +3 -3
- package/dist/report/{react → components/metric-views}/MetricLine.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/MetricLine.js +8 -5
- package/dist/report/{react → components/metric-views}/MetricMatrix.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricMatrix.js +4 -4
- package/dist/report/components/metric-views/MetricScatter.d.ts +11 -0
- package/dist/report/{react → components/metric-views}/MetricScatter.js +26 -23
- package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
- package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
- package/dist/report/components/metric-views/compute.d.ts +89 -0
- package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
- package/dist/report/components/metric-views/faces.d.ts +13 -0
- package/dist/report/components/metric-views/faces.js +381 -0
- package/dist/report/components/metric-views/index.d.ts +50 -0
- package/dist/report/components/metric-views/index.js +273 -0
- package/dist/report/{text → components/metric-views}/plot.js +1 -1
- package/dist/report/components/shared-compute.d.ts +22 -0
- package/dist/report/components/shared-compute.js +47 -0
- package/dist/report/components/shared-faces.d.ts +9 -0
- package/dist/report/components/shared-faces.js +26 -0
- package/dist/report/components/shared.d.ts +68 -0
- package/dist/report/components/shared.js +125 -0
- package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
- package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
- package/dist/report/components/site-components/compute.d.ts +28 -0
- package/dist/report/components/site-components/compute.js +132 -0
- package/dist/report/components/site-components/faces.d.ts +21 -0
- package/dist/report/components/site-components/faces.js +75 -0
- package/dist/report/components/site-components/index.d.ts +74 -0
- package/dist/report/components/site-components/index.js +220 -0
- package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
- package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
- package/dist/report/components/summaries/compute.d.ts +7 -0
- package/dist/report/components/summaries/compute.js +51 -0
- package/dist/report/components/summaries/faces.d.ts +7 -0
- package/dist/report/components/summaries/faces.js +38 -0
- package/dist/report/components/summaries/index.d.ts +27 -0
- package/dist/report/components/summaries/index.js +78 -0
- package/dist/report/definition/grid-layout.d.ts +50 -0
- package/dist/report/definition/grid-layout.js +89 -0
- package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
- package/dist/report/{primitives.js → definition/primitives.js} +101 -10
- package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
- package/dist/report/{report.js → definition/report.js} +58 -90
- package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
- package/dist/report/{text/table.js → definition/table-text.js} +2 -2
- package/dist/report/{tree.d.ts → definition/tree.d.ts} +44 -12
- package/dist/report/{tree.js → definition/tree.js} +10 -12
- package/dist/report/index.d.ts +33 -18
- package/dist/report/index.js +23 -14
- package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
- package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
- package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
- package/dist/report/{flag.js → model/flag.js} +35 -0
- package/dist/report/{format.d.ts → model/format.d.ts} +11 -5
- package/dist/report/{format.js → model/format.js} +48 -4
- package/dist/report/{locale.d.ts → model/locale.d.ts} +17 -17
- package/dist/report/{locale.js → model/locale.js} +31 -33
- package/dist/report/{metrics.d.ts → model/metrics.d.ts} +2 -2
- package/dist/report/{metrics.js → model/metrics.js} +2 -2
- package/dist/report/{types.d.ts → model/types.d.ts} +140 -20
- package/dist/report/react/index.d.ts +32 -21
- package/dist/report/react/index.js +33 -21
- package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
- package/dist/report/{load.js → runtime/load.js} +1 -1
- package/dist/report/runtime/text.d.ts +67 -0
- package/dist/report/runtime/text.js +114 -0
- package/dist/report/runtime/web.d.ts +35 -0
- package/dist/report/runtime/web.js +70 -0
- package/dist/results/annotated-source.d.ts +87 -0
- package/dist/results/attempt-evidence.d.ts +74 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/select.d.ts +12 -2
- package/dist/results/select.js +27 -5
- package/dist/runner/eval-selection.d.ts +26 -0
- package/dist/runner/feedback/sink.d.ts +26 -1
- package/dist/runner/types.d.ts +149 -23
- package/dist/sandbox/docker.d.ts +6 -0
- package/dist/sandbox/types.d.ts +23 -21
- package/dist/scoring/display.d.ts +7 -2
- package/dist/scoring/display.js +42 -4
- package/dist/scoring/types.d.ts +3 -3
- package/dist/shared/aggregate.d.ts +15 -1
- package/dist/shared/aggregate.js +32 -1
- package/dist/shared/types.d.ts +6 -1
- package/docs-site/images/logo-dark.svg +7 -0
- package/docs-site/images/logo.svg +6 -5
- package/docs-site/zh/README.md +8 -7
- package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
- package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
- package/docs-site/zh/examples/index.mdx +3 -3
- package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +1 -1
- package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
- package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
- package/docs-site/zh/explanation/adapter.mdx +19 -19
- package/docs-site/zh/explanation/assert.mdx +9 -9
- package/docs-site/zh/explanation/drive.mdx +4 -4
- package/docs-site/zh/explanation/evals.mdx +8 -8
- package/docs-site/zh/explanation/experiment.mdx +8 -8
- package/docs-site/zh/explanation/hitl.mdx +13 -13
- package/docs-site/zh/explanation/judge.mdx +3 -3
- package/docs-site/zh/explanation/overview.mdx +7 -7
- package/docs-site/zh/explanation/runner.mdx +11 -11
- package/docs-site/zh/explanation/tier.mdx +6 -6
- package/docs-site/zh/index.mdx +14 -14
- package/docs-site/zh/introduction.mdx +13 -16
- package/docs-site/zh/reference/builtin-agents.mdx +61 -28
- package/docs-site/zh/reference/capabilities.mdx +7 -27
- package/docs-site/zh/reference/cli.mdx +35 -32
- package/docs-site/zh/reference/define-agent.mdx +32 -31
- package/docs-site/zh/reference/define-config.mdx +6 -6
- package/docs-site/zh/reference/define-eval.mdx +33 -20
- package/docs-site/zh/reference/events.mdx +4 -4
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/docs-site/zh/reference/official-adapters.mdx +17 -17
- package/docs-site/zh/reference/report-components.mdx +193 -121
- package/docs-site/zh/reference/results-data.mdx +13 -13
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
- package/docs-site/zh/troubleshooting/debugging.mdx +43 -19
- package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
- package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
- package/docs-site/zh/tutorials/authoring.mdx +211 -0
- package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
- package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
- package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
- package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
- package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
- package/docs-site/zh/tutorials/experiments.mdx +103 -0
- package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
- package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +11 -5
- package/docs-site/zh/tutorials/quickstart.mdx +18 -18
- package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
- package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
- package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +59 -50
- package/docs-site/zh/tutorials/write-experiment.mdx +355 -0
- package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
- package/package.json +8 -4
- package/src/agents/bub.ts +21 -8
- package/src/agents/claude-code.ts +23 -10
- package/src/agents/codex.test.ts +22 -7
- package/src/agents/codex.ts +22 -9
- package/src/agents/index.ts +2 -2
- package/src/agents/openai-compat.ts +1 -1
- package/src/agents/post-setup.ts +45 -22
- package/src/agents/streaming.ts +1 -1
- package/src/agents/types.ts +20 -17
- package/src/agents/ui-message-stream.test.ts +10 -0
- package/src/agents/ui-message-stream.ts +12 -1
- package/src/cli.ts +77 -64
- package/src/context/types.ts +21 -21
- package/src/define.ts +13 -0
- package/src/i18n/en.ts +30 -15
- package/src/i18n/zh-CN.ts +30 -15
- package/src/index.ts +2 -0
- package/src/report/{react → assets}/colors.ts +22 -0
- package/src/report/{react → assets}/enhance.js +1 -33
- package/src/report/{react → assets}/styles.css +136 -112
- package/src/report/built-in/index.tsx +2 -2
- package/src/report/built-in/standard.tsx +18 -6
- package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
- package/src/report/components/attempt-detail/AttemptConversation.tsx +110 -0
- package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
- package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
- package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
- package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
- package/src/report/components/attempt-detail/AttemptSource.tsx +80 -0
- package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
- package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
- package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
- package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
- package/src/report/components/attempt-detail/attempt-components.test.tsx +680 -0
- package/src/report/components/attempt-detail/compute.ts +287 -0
- package/src/report/components/attempt-detail/faces.ts +257 -0
- package/src/report/components/attempt-detail/index.tsx +583 -0
- package/src/report/components/attempt-detail/validate.test.ts +235 -0
- package/src/report/{react → components}/cell.tsx +6 -3
- package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
- package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
- package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
- package/src/report/{react → components/entity-lists}/ExperimentList.tsx +23 -20
- package/src/report/components/entity-lists/compute.ts +196 -0
- package/src/report/components/entity-lists/faces.ts +207 -0
- package/src/report/components/entity-lists/index.tsx +241 -0
- package/src/report/components/entity-lists/validate.test.ts +114 -0
- package/src/report/{react → components}/fixtures.ts +2 -2
- package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
- package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
- package/src/report/{react → components/metric-views}/MetricScatter.tsx +41 -33
- package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
- package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
- package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
- package/src/report/components/metric-views/faces.ts +423 -0
- package/src/report/components/metric-views/index.tsx +352 -0
- package/src/report/{text → components/metric-views}/plot.ts +1 -1
- package/src/report/components/metric-views/validate.test.ts +211 -0
- package/src/report/{react → components}/render.test.tsx +47 -17
- package/src/report/components/shared-compute.ts +59 -0
- package/src/report/components/shared-faces.ts +30 -0
- package/src/report/components/shared.ts +192 -0
- package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
- package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
- package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
- package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
- package/src/report/components/site-components/compute.ts +144 -0
- package/src/report/components/site-components/faces.ts +80 -0
- package/src/report/components/site-components/index.tsx +265 -0
- package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
- package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
- package/src/report/components/site-components/validate.test.ts +89 -0
- package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
- package/src/report/components/summaries/compute.ts +56 -0
- package/src/report/components/summaries/faces.ts +48 -0
- package/src/report/components/summaries/index.tsx +127 -0
- package/src/report/components/summaries/validate.test.ts +49 -0
- package/src/report/definition/grid-layout.test.ts +124 -0
- package/src/report/definition/grid-layout.ts +146 -0
- package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
- package/src/report/{report.ts → definition/report.ts} +98 -165
- package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
- package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
- package/src/report/{tree.ts → definition/tree.ts} +58 -22
- package/src/report/index.ts +126 -66
- package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
- package/src/report/{flag.ts → model/flag.ts} +39 -1
- package/src/report/{format.ts → model/format.ts} +59 -5
- package/src/report/{locale.ts → model/locale.ts} +34 -36
- package/src/report/{metrics.ts → model/metrics.ts} +3 -3
- package/src/report/{types.ts → model/types.ts} +143 -22
- package/src/report/react/index.tsx +52 -26
- package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +564 -49
- package/src/report/runtime/host.test.ts +44 -0
- package/src/report/runtime/host.ts +138 -0
- package/src/report/{load.ts → runtime/load.ts} +1 -1
- package/src/report/runtime/text.ts +192 -0
- package/src/report/runtime/web.ts +106 -0
- package/src/results/attempt-evidence.ts +5 -1
- package/src/results/host-equivalence.test.ts +54 -3
- package/src/results/select.ts +30 -6
- package/src/runner/attempt.test.ts +48 -1
- package/src/runner/attempt.ts +62 -42
- package/src/runner/cleanup-timeout.test.ts +16 -0
- package/src/runner/cleanup-timeout.ts +23 -0
- package/src/runner/discover.ts +1 -2
- package/src/runner/eval-selection.test.ts +187 -0
- package/src/runner/eval-selection.ts +109 -0
- package/src/runner/experiment-cleanup-registry.test.ts +89 -0
- package/src/runner/experiment-cleanup-registry.ts +39 -0
- package/src/runner/experiment-labels.test.ts +71 -0
- package/src/runner/feedback/agent.test.ts +33 -1
- package/src/runner/feedback/agent.ts +12 -16
- package/src/runner/feedback/ci.test.ts +35 -1
- package/src/runner/feedback/ci.ts +16 -18
- package/src/runner/feedback/coordinator.ts +28 -0
- package/src/runner/feedback/human.test.ts +108 -1
- package/src/runner/feedback/human.ts +71 -21
- package/src/runner/feedback/reducer.test.ts +64 -0
- package/src/runner/feedback/reducer.ts +26 -0
- package/src/runner/feedback/sink.ts +40 -1
- package/src/runner/fingerprint.ts +2 -1
- package/src/runner/report.test.ts +14 -0
- package/src/runner/report.ts +3 -1
- package/src/runner/run.test.ts +390 -12
- package/src/runner/run.ts +259 -26
- package/src/runner/sandbox-selection.test.ts +13 -3
- package/src/runner/sandbox-selection.ts +4 -2
- package/src/runner/types.ts +158 -23
- package/src/sandbox/docker.ts +7 -0
- package/src/sandbox/e2b.ts +12 -13
- package/src/sandbox/types.ts +23 -21
- package/src/scoring/display.test.ts +28 -2
- package/src/scoring/display.ts +38 -5
- package/src/scoring/types.ts +3 -3
- package/src/shared/aggregate.test.ts +52 -0
- package/src/shared/aggregate.ts +30 -1
- package/src/shared/types.ts +6 -1
- package/src/show/command.test.ts +34 -0
- package/src/show/command.ts +16 -0
- package/src/show/compose.ts +1 -1
- package/src/show/index.ts +58 -22
- package/src/show/render.ts +16 -240
- package/src/show/show.test.ts +128 -35
- package/src/view/app/App.test.tsx +8 -7
- package/src/view/app/App.tsx +135 -51
- package/src/view/app/i18n.ts +7 -147
- package/src/view/app/lib/attempt-dialog.test.ts +65 -0
- package/src/view/app/lib/attempt-dialog.ts +69 -0
- package/src/view/app/main.tsx +0 -1
- package/src/view/app/types.ts +3 -86
- package/src/view/artifact-serving.test.ts +22 -38
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +15 -22
- package/src/view/data.test.ts +48 -36
- package/src/view/data.ts +122 -89
- package/src/view/index.ts +3 -13
- package/src/view/server.ts +36 -6
- package/src/view/shared/types.ts +6 -31
- package/src/view/site-parity.test.ts +24 -1
- package/src/view/site.ts +144 -17
- package/src/view/styles.css +18 -806
- package/src/view/view-report.test.ts +182 -34
- package/dist/report/components.d.ts +0 -179
- package/dist/report/components.js +0 -544
- package/dist/report/compute.d.ts +0 -139
- package/dist/report/react/ExperimentComparison.d.ts +0 -10
- package/dist/report/react/ExperimentComparison.js +0 -12
- package/dist/report/react/MetricScatter.d.ts +0 -9
- package/dist/report/react/format.d.ts +0 -3
- package/dist/report/react/format.js +0 -7
- package/dist/report/text/faces.d.ts +0 -45
- package/dist/report/text/faces.js +0 -671
- package/dist/report/web.d.ts +0 -32
- package/dist/report/web.js +0 -48
- package/docs-site/zh/how-to/authoring.mdx +0 -162
- package/docs-site/zh/how-to/custom-reports.mdx +0 -414
- package/docs-site/zh/how-to/experiments.mdx +0 -86
- package/docs-site/zh/how-to/write-experiment.mdx +0 -164
- package/src/report/components.tsx +0 -925
- package/src/report/react/ExperimentComparison.tsx +0 -73
- package/src/report/react/format.ts +0 -9
- package/src/report/text/faces.ts +0 -767
- package/src/report/web.ts +0 -77
- package/src/show/report-host.test.ts +0 -205
- package/src/show/report-host.ts +0 -389
- package/src/view/app/components/AttemptModal.tsx +0 -496
- package/src/view/app/components/CodeView.test.tsx +0 -142
- package/src/view/app/components/CodeView.tsx +0 -310
- package/src/view/app/components/CopyControls.tsx +0 -106
- package/src/view/app/components/Trace.tsx +0 -100
- package/src/view/app/components/Transcript.tsx +0 -157
- package/src/view/app/components/ui/badge.tsx +0 -21
- package/src/view/app/lib/artifact-url.ts +0 -17
- package/src/view/app/lib/attempt-route.test.ts +0 -80
- package/src/view/app/lib/attempt-route.ts +0 -52
- package/src/view/app/lib/format.ts +0 -70
- package/src/view/app/lib/guards.test.ts +0 -108
- package/src/view/app/lib/guards.ts +0 -71
- package/src/view/app/lib/rows.ts +0 -22
- package/src/view/app/lib/transcript-data.tsx +0 -151
- package/src/view/app/lib/verdict.ts +0 -23
- package/src/view/app/shared.ts +0 -8
- /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
- /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
- /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
- /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
- /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
- /package/dist/report/{types.js → model/types.js} +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
- /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
- /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { DEFAULT_REPORT_LOCALE, localeText } from "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { DEFAULT_REPORT_LOCALE, localeText } from "../../model/locale.js";
|
|
3
|
+
import { formatReportDateTime, formatReportDateTimeRange } from "../../model/format.js";
|
|
4
|
+
import { MetricCellView } from "../cell.js";
|
|
5
|
+
import { cx } from "../shared.js";
|
|
5
6
|
function VerdictTallyView({ tally, locale }) {
|
|
6
7
|
const kinds = ["passed", "failed", "errored", "skipped"].filter((k) => tally[k] > 0);
|
|
7
8
|
return (_jsxs("span", { className: "nre-verdict-tally", children: [kinds.map((kind) => (_jsxs("span", { className: cx("nre-verdict-pill", `nre-verdict-${kind}`), children: [tally[kind], " ", localeText(locale, `verdict.${kind}`)] }, kind))), kinds.length === 0 && _jsx("span", { className: "nre-missing", children: "\u2014" })] }));
|
|
8
9
|
}
|
|
9
10
|
export function ScopeSummary({ data, votes = "eval", className, locale = DEFAULT_REPORT_LOCALE, }) {
|
|
10
11
|
const tally = votes === "attempt" ? data.attemptVerdicts : data.evalVerdicts;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
const formattedRange = data.range.earliestStartedAt !== null && data.range.latestStartedAt !== null
|
|
13
|
+
? formatReportDateTimeRange(data.range.earliestStartedAt, data.range.latestStartedAt, locale)
|
|
14
|
+
: null;
|
|
15
|
+
return (_jsxs("div", { className: cx("nre", "nre-scope-summary", className), "data-votes": votes, children: [_jsxs("dl", { className: "nre-scope-kpis", children: [_jsxs("div", { className: "nre-scope-kpi nre-scope-kpi-rate", children: [_jsx("dt", { children: localeText(locale, "scopeSummary.passRate") }), _jsx("dd", { children: _jsx(MetricCellView, { cell: data.endToEndPassRate, locale: locale }) })] }), _jsxs("div", { className: "nre-scope-kpi", children: [_jsx("dt", { children: localeText(locale, "scopeSummary.experiments") }), _jsx("dd", { children: data.experiments })] }), _jsxs("div", { className: "nre-scope-kpi", children: [_jsx("dt", { children: localeText(locale, "scopeSummary.evals") }), _jsx("dd", { children: data.evals })] }), _jsxs("div", { className: "nre-scope-kpi", children: [_jsx("dt", { children: localeText(locale, "scopeSummary.attempts") }), _jsx("dd", { children: data.attempts })] }), _jsxs("div", { className: "nre-scope-kpi nre-scope-kpi-verdicts", children: [_jsx("dt", { children: localeText(locale, votes === "attempt" ? "scopeSummary.votesAttempt" : "scopeSummary.votesEval") }), _jsx("dd", { children: _jsx(VerdictTallyView, { tally: tally, locale: locale }) })] }), _jsxs("div", { className: "nre-scope-kpi", children: [_jsx("dt", { children: localeText(locale, "scopeSummary.totalCost") }), _jsxs("dd", { children: [_jsx(MetricCellView, { cell: data.totalCostUSD, locale: locale, showCoverage: false }), data.totalCostUSD.samples < data.totalCostUSD.total && (_jsx("small", { className: "nre-scope-kpi-note", children: localeText(locale, "scopeSummary.costCoverage", {
|
|
16
|
+
samples: data.totalCostUSD.samples,
|
|
17
|
+
total: data.totalCostUSD.total,
|
|
18
|
+
}) }))] })] })] }), data.range.latestStartedAt !== null && (_jsx("p", { className: "nre-scope-summary-range", children: data.range.earliestStartedAt !== null && data.range.earliestStartedAt !== data.range.latestStartedAt
|
|
19
|
+
? localeText(locale, "scopeSummary.runRange", {
|
|
20
|
+
from: formattedRange.from,
|
|
21
|
+
to: formattedRange.to,
|
|
15
22
|
})
|
|
16
|
-
:
|
|
23
|
+
: localeText(locale, "scopeSummary.lastRun", {
|
|
24
|
+
time: formatReportDateTime(data.range.latestStartedAt, locale),
|
|
25
|
+
}) }))] }));
|
|
17
26
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReportInput, ScopeSummaryData } from "../../model/types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* `scopeSummaryData(input)`:范围摘要——快照时间窗、experiment / eval / attempt 数、
|
|
4
|
+
* 两级判定计票、端到端通过率与总成本(docs/feature/reports/library/summaries.md)。
|
|
5
|
+
* data 恒携带两级计票;通过率来自官方两级指标引擎,不从任一计票重算。
|
|
6
|
+
*/
|
|
7
|
+
export declare function scopeSummaryData(input: ReportInput): Promise<ScopeSummaryData>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// 计算函数(*Data):ReportInput → 一份组件数据。ScopeSummary 的 scopeSummaryData 住在这里
|
|
2
|
+
// (docs/feature/reports/library/summaries.md)。ExperimentComparison 是纯组合组件,不产生
|
|
3
|
+
// 独立的 data,没有对应的计算函数。
|
|
4
|
+
//
|
|
5
|
+
// 共同约定(docs/feature/reports/architecture.md「指标聚合不变量」):
|
|
6
|
+
// - 第一参收 ReportInput = Scope | readonly Snapshot[];warnings 不进组件数据(宿主统一显示);
|
|
7
|
+
// - 聚合前按身份键去重(dedupeAttempts;missing-startedAt 不去重、如实保留、不透出警告);
|
|
8
|
+
// - null ≠ 0:缺数据不编数,覆盖率经 samples/total 如实暴露。
|
|
9
|
+
import { collectItems, computeCell, resolveInput } from "../../model/aggregate.js";
|
|
10
|
+
import { costUSD, defineMetric, endToEndPassRate } from "../../model/metrics.js";
|
|
11
|
+
import { selectedEvalsOnly, summarizeItems, tallyOf } from "../shared-compute.js";
|
|
12
|
+
// ───────────────────────── scopeSummaryData ─────────────────────────
|
|
13
|
+
/** costUSD 的求和投影:两级都 sum(题内多轮求和 + 跨题求和 = 全量求和),display 走 $。 */
|
|
14
|
+
const totalCostMetric = defineMetric({
|
|
15
|
+
name: "total-cost",
|
|
16
|
+
label: costUSD.label,
|
|
17
|
+
unit: "$",
|
|
18
|
+
value: costUSD.value,
|
|
19
|
+
aggregate: { perEval: "sum", acrossEvals: "sum" },
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* `scopeSummaryData(input)`:范围摘要——快照时间窗、experiment / eval / attempt 数、
|
|
23
|
+
* 两级判定计票、端到端通过率与总成本(docs/feature/reports/library/summaries.md)。
|
|
24
|
+
* data 恒携带两级计票;通过率来自官方两级指标引擎,不从任一计票重算。
|
|
25
|
+
*/
|
|
26
|
+
export async function scopeSummaryData(input) {
|
|
27
|
+
const snapshots = selectedEvalsOnly(resolveInput(input).snapshots);
|
|
28
|
+
const items = collectItems(snapshots);
|
|
29
|
+
let earliest = null;
|
|
30
|
+
let latest = null;
|
|
31
|
+
for (const snapshot of snapshots) {
|
|
32
|
+
if (earliest === null || snapshot.startedAt < earliest)
|
|
33
|
+
earliest = snapshot.startedAt;
|
|
34
|
+
if (latest === null || snapshot.startedAt > latest)
|
|
35
|
+
latest = snapshot.startedAt;
|
|
36
|
+
}
|
|
37
|
+
const stats = summarizeItems(items);
|
|
38
|
+
const attemptVerdicts = tallyOf();
|
|
39
|
+
for (const item of items)
|
|
40
|
+
attemptVerdicts[item.attempt.result.verdict] += 1;
|
|
41
|
+
return {
|
|
42
|
+
range: { earliestStartedAt: earliest, latestStartedAt: latest },
|
|
43
|
+
experiments: stats.experiments,
|
|
44
|
+
evals: stats.evals,
|
|
45
|
+
attempts: stats.attempts,
|
|
46
|
+
evalVerdicts: stats.verdicts,
|
|
47
|
+
attemptVerdicts,
|
|
48
|
+
endToEndPassRate: await computeCell(endToEndPassRate, items),
|
|
49
|
+
totalCostUSD: await computeCell(totalCostMetric, items),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ScopeSummaryData } from "../../model/types.ts";
|
|
2
|
+
import type { TextContext } from "../../definition/tree.ts";
|
|
3
|
+
/**
|
|
4
|
+
* 一至两行:头行是端到端通过率(官方 MetricCell,不重算)+ experiment/eval/attempt 数 +
|
|
5
|
+
* `votes` 选中的那级计票 + 总成本;第二行(有则加)是快照时间窗。
|
|
6
|
+
*/
|
|
7
|
+
export declare function scopeSummaryText(data: ScopeSummaryData, votes: "eval" | "attempt", ctx: TextContext): string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// ScopeSummary 的 text 面:同一份算好的数据,渲染成终端字符(niceeval show 的形态)。
|
|
2
|
+
// 与 web 面共守诚实契约:排序随 better、samples < total 角标、缺数据 — 不补 0。
|
|
3
|
+
// 零 react、零 IO、纯同步。
|
|
4
|
+
import { countText, localeText, resolveLocalizedText } from "../../model/locale.js";
|
|
5
|
+
import { formatReportDateTime, formatReportDateTimeRange } from "../../model/format.js";
|
|
6
|
+
import { wrapDisplay } from "../../model/text-layout.js";
|
|
7
|
+
import { cellText, verdictTallyText, MISSING_MARK } from "../shared-faces.js";
|
|
8
|
+
/**
|
|
9
|
+
* 一至两行:头行是端到端通过率(官方 MetricCell,不重算)+ experiment/eval/attempt 数 +
|
|
10
|
+
* `votes` 选中的那级计票 + 总成本;第二行(有则加)是快照时间窗。
|
|
11
|
+
*/
|
|
12
|
+
export function scopeSummaryText(data, votes, ctx) {
|
|
13
|
+
const locale = ctx.locale;
|
|
14
|
+
const tally = votes === "attempt" ? data.attemptVerdicts : data.evalVerdicts;
|
|
15
|
+
const head = [
|
|
16
|
+
`${localeText(locale, "scopeSummary.passRate")} ${cellText(data.endToEndPassRate, locale)}`,
|
|
17
|
+
countText(locale, "overview.experiments", data.experiments),
|
|
18
|
+
localeText(locale, "overview.evalsCount", { n: data.evals }),
|
|
19
|
+
localeText(locale, "overview.attemptsCount", { n: data.attempts }),
|
|
20
|
+
verdictTallyText(tally, locale),
|
|
21
|
+
`${localeText(locale, "scopeSummary.totalCost")} ${data.totalCostUSD.value === null ? MISSING_MARK : resolveLocalizedText(data.totalCostUSD.display, locale)}${data.totalCostUSD.samples < data.totalCostUSD.total
|
|
22
|
+
? ` (${localeText(locale, "scopeSummary.costCoverage", {
|
|
23
|
+
samples: data.totalCostUSD.samples,
|
|
24
|
+
total: data.totalCostUSD.total,
|
|
25
|
+
})})`
|
|
26
|
+
: ""}`,
|
|
27
|
+
].join(" · ");
|
|
28
|
+
// 头行拼接的字段较多,窄终端下按显示宽度折行(不截断内容)。
|
|
29
|
+
const lines = wrapDisplay(head, ctx.width);
|
|
30
|
+
if (data.range.latestStartedAt !== null) {
|
|
31
|
+
const from = data.range.earliestStartedAt;
|
|
32
|
+
const to = data.range.latestStartedAt;
|
|
33
|
+
lines.push(from !== null && from !== to
|
|
34
|
+
? localeText(locale, "scopeSummary.runRange", formatReportDateTimeRange(from, to, locale))
|
|
35
|
+
: localeText(locale, "scopeSummary.lastRun", { time: formatReportDateTime(to, locale) }));
|
|
36
|
+
}
|
|
37
|
+
return lines.join("\n");
|
|
38
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ReportComponent } from "../../definition/tree.ts";
|
|
2
|
+
import type { ReportInput, ScopeSummaryData, SeriesInput } from "../../model/types.ts";
|
|
3
|
+
import { type ChromeProps, type DataProps, type Validator } from "../shared.ts";
|
|
4
|
+
export declare const validateScopeSummaryData: Validator;
|
|
5
|
+
export type ScopeSummaryProps = DataProps<ScopeSummaryData, Record<never, never>, ChromeProps & {
|
|
6
|
+
/** 显示哪一级计票;默认 "eval"。data 恒携带两级,votes 只选择呈现。 */
|
|
7
|
+
votes?: "eval" | "attempt";
|
|
8
|
+
}>;
|
|
9
|
+
/** 范围摘要卡:时间窗、数量、两级计票、端到端通过率与总成本。 */
|
|
10
|
+
export declare const ScopeSummary: ReportComponent<ScopeSummaryProps>;
|
|
11
|
+
type ComparisonChrome = ChromeProps & {
|
|
12
|
+
/** 透传给散点;缺省跟随缺省 series 解析——按 line 归类时连线(声明了线就画线)。 */
|
|
13
|
+
connect?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type ExperimentComparisonProps = ComparisonChrome & {
|
|
16
|
+
input?: ReportInput;
|
|
17
|
+
/** 散点的 series 维度。缺省解析:Scope 内任一实验声明了 label `line` → `label("line")` 并连线;否则 `"agent"`、不连线。 */
|
|
18
|
+
series?: SeriesInput;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 内建报告的默认组合件:把同一个 input(缺省 ctx.scope)原样透传给 ScopeSummary、
|
|
22
|
+
* 成本 × 端到端通过率的 MetricScatter 与 ExperimentList——组合本身不二次计算或过滤,
|
|
23
|
+
* 也不导出自己的 data 形态;每个叶子组件按自己的公开契约取数,共享计算由 resolve 的
|
|
24
|
+
* 「同引用 input + 深相等 spec」记忆化保证。
|
|
25
|
+
*/
|
|
26
|
+
export declare const ExperimentComparison: ReportComponent<ExperimentComparisonProps>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// 官方双面组件的装配点:web 面(./ScopeSummary.tsx 的纯 React 组件)+ text 面(./faces.ts)
|
|
3
|
+
// + resolve 解析面(spec 形态由管线代调配套 ./compute.ts)。ScopeSummary 与 ExperimentComparison
|
|
4
|
+
// 同属汇总族——前者是范围摘要卡,后者是内建报告默认使用的组合件(装配 ScopeSummary +
|
|
5
|
+
// MetricScatter + ExperimentList,不产生自己的 data)。
|
|
6
|
+
import { defineComponent } from "../../definition/tree.js";
|
|
7
|
+
import { Col } from "../../definition/primitives.js";
|
|
8
|
+
import { resolveInput, seriesName } from "../../model/aggregate.js";
|
|
9
|
+
import { label } from "../../model/flag.js";
|
|
10
|
+
import { costUSD, endToEndPassRate } from "../../model/metrics.js";
|
|
11
|
+
import { cellProblem, isObject, makeDataComponent, tallyProblem, } from "../shared.js";
|
|
12
|
+
import { scopeSummaryData } from "./compute.js";
|
|
13
|
+
import { scopeSummaryText } from "./faces.js";
|
|
14
|
+
import { ScopeSummary as ScopeSummaryWeb } from "./ScopeSummary.js";
|
|
15
|
+
import { MetricScatter } from "../metric-views/index.js";
|
|
16
|
+
import { ExperimentList } from "../entity-lists/index.js";
|
|
17
|
+
export const validateScopeSummaryData = (data) => {
|
|
18
|
+
if (!isObject(data))
|
|
19
|
+
return "expected an object";
|
|
20
|
+
if (!isObject(data.range))
|
|
21
|
+
return 'missing "range" ({ earliestStartedAt, latestStartedAt })';
|
|
22
|
+
if (!(data.range.earliestStartedAt === null || typeof data.range.earliestStartedAt === "string")) {
|
|
23
|
+
return '"range.earliestStartedAt" must be a string or null';
|
|
24
|
+
}
|
|
25
|
+
if (!(data.range.latestStartedAt === null || typeof data.range.latestStartedAt === "string")) {
|
|
26
|
+
return '"range.latestStartedAt" must be a string or null';
|
|
27
|
+
}
|
|
28
|
+
if (typeof data.experiments !== "number")
|
|
29
|
+
return '"experiments" must be a number';
|
|
30
|
+
if (typeof data.evals !== "number")
|
|
31
|
+
return '"evals" must be a number';
|
|
32
|
+
if (typeof data.attempts !== "number")
|
|
33
|
+
return '"attempts" must be a number';
|
|
34
|
+
const evalVerdictsProblem = tallyProblem(data.evalVerdicts, "evalVerdicts");
|
|
35
|
+
if (evalVerdictsProblem !== null)
|
|
36
|
+
return evalVerdictsProblem;
|
|
37
|
+
const attemptVerdictsProblem = tallyProblem(data.attemptVerdicts, "attemptVerdicts");
|
|
38
|
+
if (attemptVerdictsProblem !== null)
|
|
39
|
+
return attemptVerdictsProblem;
|
|
40
|
+
const passRateProblem = cellProblem(data.endToEndPassRate, "endToEndPassRate");
|
|
41
|
+
if (passRateProblem !== null)
|
|
42
|
+
return passRateProblem;
|
|
43
|
+
return cellProblem(data.totalCostUSD, "totalCostUSD");
|
|
44
|
+
};
|
|
45
|
+
/** 范围摘要卡:时间窗、数量、两级计票、端到端通过率与总成本。 */
|
|
46
|
+
export const ScopeSummary = makeDataComponent({
|
|
47
|
+
name: "ScopeSummary",
|
|
48
|
+
dataFnName: "scopeSummaryData",
|
|
49
|
+
shapeName: "ScopeSummaryData",
|
|
50
|
+
dataFn: (input) => scopeSummaryData(input),
|
|
51
|
+
specKeys: [],
|
|
52
|
+
validate: validateScopeSummaryData,
|
|
53
|
+
web: (props, ctx) => _jsx(ScopeSummaryWeb, { ...props, locale: props.locale ?? ctx.locale }),
|
|
54
|
+
text: (props, ctx) => scopeSummaryText(props.data, props.votes ?? "eval", ctx),
|
|
55
|
+
});
|
|
56
|
+
/** 默认报告识别的归类键:声明了它的实验按线归类并连线(docs/feature/experiments/library.md「labels」)。 */
|
|
57
|
+
const LINE_LABEL_KEY = "line";
|
|
58
|
+
/**
|
|
59
|
+
* 缺省 series:Scope 内任一快照声明了 labels.line 用 line 维度,否则 agent;显式传入覆盖。
|
|
60
|
+
* connect 缺省跟随最终 series 是否解析为 line(即便是显式传入的)。
|
|
61
|
+
*/
|
|
62
|
+
function resolveComparisonSeries(input, props) {
|
|
63
|
+
const hasLine = resolveInput(input).snapshots.some((s) => s.experiment?.labels?.[LINE_LABEL_KEY] !== undefined);
|
|
64
|
+
const series = props.series ?? (hasLine ? label(LINE_LABEL_KEY) : "agent");
|
|
65
|
+
return { series, connect: props.connect ?? seriesName(series) === LINE_LABEL_KEY };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 内建报告的默认组合件:把同一个 input(缺省 ctx.scope)原样透传给 ScopeSummary、
|
|
69
|
+
* 成本 × 端到端通过率的 MetricScatter 与 ExperimentList——组合本身不二次计算或过滤,
|
|
70
|
+
* 也不导出自己的 data 形态;每个叶子组件按自己的公开契约取数,共享计算由 resolve 的
|
|
71
|
+
* 「同引用 input + 深相等 spec」记忆化保证。
|
|
72
|
+
*/
|
|
73
|
+
export const ExperimentComparison = defineComponent((props, ctx) => {
|
|
74
|
+
const input = props.input ?? ctx.scope;
|
|
75
|
+
const { series, connect } = resolveComparisonSeries(input, props);
|
|
76
|
+
return (_jsxs(Col, { className: props.className, children: [_jsx(ScopeSummary, { input: input, locale: props.locale }), _jsx(MetricScatter, { input: input, points: "experiment", series: series, connect: connect, x: costUSD, y: endToEndPassRate, locale: props.locale }), _jsx(ExperimentList, { input: input, filter: true, locale: props.locale })] }));
|
|
77
|
+
});
|
|
78
|
+
ExperimentComparison.displayName = "ExperimentComparison";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ReportNode } from "./tree.ts";
|
|
2
|
+
export type GridVariant = "plain" | "boxed";
|
|
3
|
+
export type GridDensity = "regular" | "compact";
|
|
4
|
+
/** 校验 `columns`:必须是有限正整数——TypeScript 的 `number` 排除不了 0/负数/小数/NaN/Infinity。 */
|
|
5
|
+
export declare function validateGridColumns(columns: number): number;
|
|
6
|
+
export interface NormalizedGridCell {
|
|
7
|
+
readonly node: ReportNode;
|
|
8
|
+
/** React key:复用元素自带 key,缺失时按展平后的声明序生成稳定回退。 */
|
|
9
|
+
readonly key: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `normalizeGrid` 的产物:有序、不可拆的 cell 列表 + columns / variant / density。
|
|
13
|
+
* 不是公开 data shape,也不进结果或 artifact——只是两个渲染面共享的同步排版中间值。
|
|
14
|
+
*/
|
|
15
|
+
export interface NormalizedGrid {
|
|
16
|
+
readonly cells: readonly NormalizedGridCell[];
|
|
17
|
+
readonly columns: number;
|
|
18
|
+
readonly variant: GridVariant;
|
|
19
|
+
readonly density: GridDensity;
|
|
20
|
+
}
|
|
21
|
+
/** `Grid` 组件创建时的一次性规范化:校验 props、展平 children 成有序 cell 列表。 */
|
|
22
|
+
export declare function normalizeGrid(input: {
|
|
23
|
+
children: ReportNode;
|
|
24
|
+
columns: number;
|
|
25
|
+
variant?: GridVariant;
|
|
26
|
+
density?: GridDensity;
|
|
27
|
+
}): NormalizedGrid;
|
|
28
|
+
/** text 面规划的输入:只有可用宽度、cell 数与规范化 Grid props 里跟宽度有关的两项。 */
|
|
29
|
+
export interface TextGridPlanInput {
|
|
30
|
+
readonly availableWidth: number;
|
|
31
|
+
readonly cellCount: number;
|
|
32
|
+
readonly columns: number;
|
|
33
|
+
readonly density: GridDensity;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* text 面的一次性排版计划:实际列数、每列内容显示宽度、格间 gutter。
|
|
37
|
+
* `boxed` 与 `plain` 复用同一份计划(规划总是先扣 boxed 的四边框与内 padding),
|
|
38
|
+
* `plain` 渲染时只是不打印这部分字符——两个 variant 因此列数一致,不会各挑各的列数。
|
|
39
|
+
*/
|
|
40
|
+
export interface TextGridPlan {
|
|
41
|
+
readonly columns: number;
|
|
42
|
+
readonly contentWidths: readonly number[];
|
|
43
|
+
readonly gutter: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 从 `min(columns, cellCount)` 向一列尝试,选出满足每格最小可读内容宽度的最大列数;
|
|
47
|
+
* 一列是无条件 fallback(即使内容宽度因此小于 24)。选定列数后,余下的显示列从左向右
|
|
48
|
+
* 逐列多补一列,因此任意一行的显示宽度都不会超过 `availableWidth`(见调用方组装)。
|
|
49
|
+
*/
|
|
50
|
+
export declare function planTextGrid(input: TextGridPlanInput): TextGridPlan;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Grid 的语义层与面内布局(docs/feature/reports/architecture.md「排版原语的语义层与面内布局」、
|
|
2
|
+
// docs/feature/reports/library/layout.md「Grid 与 Stat」)。只放同步纯函数与中间类型:
|
|
3
|
+
// normalizeGrid 把 resolved ReportNode children 校验并展平成有序 cell 列表;planTextGrid
|
|
4
|
+
// 只依赖 availableWidth / cell 数 / 规范化 Grid props,规划 text 面的列数与每列宽度。
|
|
5
|
+
// 不 import show / view、Results IO 或 stylesheet;primitives.tsx 消费本文件产出两面适配。
|
|
6
|
+
// react/jsx-runtime 的 Fragment 注册符号,跨 react 版本稳定(tree.ts 同一常量的独立取用,
|
|
7
|
+
// Symbol.for 全局注册表保证同一符号,不产生耦合)。
|
|
8
|
+
const REACT_FRAGMENT = Symbol.for("react.fragment");
|
|
9
|
+
function isElementNode(node) {
|
|
10
|
+
return typeof node === "object" && node !== null && !Array.isArray(node) && "type" in node && "props" in node;
|
|
11
|
+
}
|
|
12
|
+
function keyOf(node, fallback) {
|
|
13
|
+
if (isElementNode(node)) {
|
|
14
|
+
const key = node.key;
|
|
15
|
+
if (typeof key === "string" || typeof key === "number")
|
|
16
|
+
return String(key);
|
|
17
|
+
}
|
|
18
|
+
return fallback;
|
|
19
|
+
}
|
|
20
|
+
/** 校验 `columns`:必须是有限正整数——TypeScript 的 `number` 排除不了 0/负数/小数/NaN/Infinity。 */
|
|
21
|
+
export function validateGridColumns(columns) {
|
|
22
|
+
if (!(Number.isFinite(columns) && Number.isInteger(columns) && columns > 0)) {
|
|
23
|
+
throw new Error(`Grid columns must be a finite positive integer, received ${JSON.stringify(columns)}. ` +
|
|
24
|
+
"Pass columns={N} with a whole number greater than 0 (e.g. columns={3}).");
|
|
25
|
+
}
|
|
26
|
+
return columns;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 递归展开数组与 Fragment、跳过空分支(null/undefined/boolean),其余节点各占一格
|
|
30
|
+
* ——`Col` 归拢的多个子节点、任意自定义组件的渲染结果都是不透明的一格,不下钻其内部结构。
|
|
31
|
+
*/
|
|
32
|
+
function flattenGridChildren(children) {
|
|
33
|
+
const cells = [];
|
|
34
|
+
const visit = (node) => {
|
|
35
|
+
if (node === null || node === undefined || typeof node === "boolean")
|
|
36
|
+
return;
|
|
37
|
+
if (Array.isArray(node)) {
|
|
38
|
+
for (const child of node)
|
|
39
|
+
visit(child);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (isElementNode(node) && node.type === REACT_FRAGMENT) {
|
|
43
|
+
visit(node.props.children);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
cells.push({ node, key: keyOf(node, `nre-grid-cell-${cells.length}`) });
|
|
47
|
+
};
|
|
48
|
+
visit(children);
|
|
49
|
+
return cells;
|
|
50
|
+
}
|
|
51
|
+
/** `Grid` 组件创建时的一次性规范化:校验 props、展平 children 成有序 cell 列表。 */
|
|
52
|
+
export function normalizeGrid(input) {
|
|
53
|
+
return {
|
|
54
|
+
cells: flattenGridChildren(input.children),
|
|
55
|
+
columns: validateGridColumns(input.columns),
|
|
56
|
+
variant: input.variant ?? "plain",
|
|
57
|
+
density: input.density ?? "regular",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** text 面每 cell 的最小可读内容宽度;density 不以挤坏字段换取更多列。 */
|
|
61
|
+
const MIN_CONTENT_WIDTH = 24;
|
|
62
|
+
/** boxed 单 cell 的固定开销:左右各一根边框 + 左右各一格 padding。 */
|
|
63
|
+
const BOXED_OVERHEAD_PER_CELL = 4;
|
|
64
|
+
function gridGutter(density) {
|
|
65
|
+
return density === "compact" ? 1 : 2;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 从 `min(columns, cellCount)` 向一列尝试,选出满足每格最小可读内容宽度的最大列数;
|
|
69
|
+
* 一列是无条件 fallback(即使内容宽度因此小于 24)。选定列数后,余下的显示列从左向右
|
|
70
|
+
* 逐列多补一列,因此任意一行的显示宽度都不会超过 `availableWidth`(见调用方组装)。
|
|
71
|
+
*/
|
|
72
|
+
export function planTextGrid(input) {
|
|
73
|
+
const { availableWidth, cellCount, columns, density } = input;
|
|
74
|
+
const gutter = gridGutter(density);
|
|
75
|
+
const maxColumns = Math.max(1, Math.min(columns, Math.max(cellCount, 1)));
|
|
76
|
+
let chosen = 1;
|
|
77
|
+
for (let n = maxColumns; n >= 2; n--) {
|
|
78
|
+
const budget = availableWidth - BOXED_OVERHEAD_PER_CELL * n - gutter * (n - 1);
|
|
79
|
+
if (budget >= 0 && Math.floor(budget / n) >= MIN_CONTENT_WIDTH) {
|
|
80
|
+
chosen = n;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const budget = Math.max(0, availableWidth - BOXED_OVERHEAD_PER_CELL * chosen - gutter * (chosen - 1));
|
|
85
|
+
const base = Math.floor(budget / chosen);
|
|
86
|
+
const remainder = budget - base * chosen;
|
|
87
|
+
const contentWidths = Array.from({ length: chosen }, (_, i) => Math.max(1, base + (i < remainder ? 1 : 0)));
|
|
88
|
+
return { columns: chosen, contentWidths, gutter };
|
|
89
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { AttemptLocator } from "
|
|
1
|
+
import type { AttemptLocator } from "../../results/locator.ts";
|
|
2
2
|
import { type ReportNode } from "./tree.ts";
|
|
3
|
-
import { type LocalizedText, type ReportLocale } from "
|
|
4
|
-
import type { ColumnAlign } from "
|
|
3
|
+
import { type LocalizedText, type ReportLocale } from "../model/locale.ts";
|
|
4
|
+
import type { ColumnAlign } from "../model/text-layout.ts";
|
|
5
|
+
import { type GridDensity, type GridVariant } from "./grid-layout.ts";
|
|
5
6
|
export interface LayoutProps {
|
|
6
7
|
children?: ReportNode;
|
|
7
8
|
className?: string;
|
|
@@ -15,10 +16,39 @@ export declare const Col: import("./tree.ts").ReportComponent<LayoutProps>;
|
|
|
15
16
|
* 同一把尺),装不下时整块退化为纵向堆叠——不截断、不隐藏任何子块。
|
|
16
17
|
*/
|
|
17
18
|
export declare const Row: import("./tree.ts").ReportComponent<LayoutProps>;
|
|
19
|
+
export interface GridProps extends LayoutProps {
|
|
20
|
+
/** 宽面最多摆几列;必须是有限正整数,运行时校验(docs/feature/reports/library/layout.md「Grid 与 Stat」)。 */
|
|
21
|
+
columns: number;
|
|
22
|
+
/** plain 无框;boxed 给每个 cell 完整四边框。默认 plain。 */
|
|
23
|
+
variant?: GridVariant;
|
|
24
|
+
/** 改变格内留白,并调整内置 Stat 的主值字号;不改变内容和分组。默认 regular。 */
|
|
25
|
+
density?: GridDensity;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 自由摘要面板的格子容器:只负责呈现,不读取 Scope、不聚合 Metric。每个直接子节点
|
|
29
|
+
* (数组 / Fragment 先按 ReportNode 规则展平,空分支不占格)是一格;`Col` 把多个区块
|
|
30
|
+
* 归成一格。展平与 text 面排版的算术在 ./grid-layout.ts,这里只做两面结构适配。
|
|
31
|
+
*/
|
|
32
|
+
export declare const Grid: import("./tree.ts").ReportComponent<GridProps>;
|
|
33
|
+
export type StatTone = "neutral" | "positive" | "negative" | "warning";
|
|
34
|
+
export interface StatProps {
|
|
35
|
+
label: LocalizedText;
|
|
36
|
+
/** 已格式化的主值;null 明确渲染为 —,不补成 0。 */
|
|
37
|
+
value: LocalizedText | number | null;
|
|
38
|
+
/** 主值下面的短解释;省略时不留空行。 */
|
|
39
|
+
detail?: LocalizedText;
|
|
40
|
+
/** 主值的语义色;不从正负号、单位或 Metric.better 猜。默认 neutral。 */
|
|
41
|
+
tone?: StatTone;
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
/** label / 主值 / 辅助信息的最小内容单元;可以脱离 Grid 单独使用。 */
|
|
45
|
+
export declare const Stat: import("./tree.ts").ReportComponent<StatProps>;
|
|
18
46
|
export interface SectionProps extends LayoutProps {
|
|
19
47
|
title: LocalizedText;
|
|
48
|
+
/** 标题行右侧的短元信息;text 面与标题同一行,空间不足时换到下一行。 */
|
|
49
|
+
meta?: LocalizedText;
|
|
20
50
|
}
|
|
21
|
-
/**
|
|
51
|
+
/** 带标题的块:网页是标题层级(可选 meta 同行右对齐),终端是标题行加缩进。 */
|
|
22
52
|
export declare const Section: import("./tree.ts").ReportComponent<SectionProps>;
|
|
23
53
|
export interface TextProps {
|
|
24
54
|
/** 自由正文原样渲染,不随 locale 自动翻译。 */
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { COMPONENT_RAW_CHILDREN, COMPONENT_ROLE, defineComponent } from "./tree.js";
|
|
3
|
-
import { localeText, resolveLocalizedText } from "
|
|
4
|
-
import { indentBlock, joinColumns, stringWidth, wrapDisplay } from "
|
|
5
|
-
import { renderTableText } from "./text
|
|
3
|
+
import { localeText, resolveLocalizedText } from "../model/locale.js";
|
|
4
|
+
import { indentBlock, joinColumns, padDisplay, stringWidth, wrapDisplay } from "../model/text-layout.js";
|
|
5
|
+
import { renderTableText } from "./table-text.js";
|
|
6
|
+
import { normalizeGrid, planTextGrid } from "./grid-layout.js";
|
|
6
7
|
function childArray(children) {
|
|
7
8
|
if (children === null || children === undefined || typeof children === "boolean")
|
|
8
9
|
return [];
|
|
@@ -11,6 +12,8 @@ function childArray(children) {
|
|
|
11
12
|
function cx(...parts) {
|
|
12
13
|
return parts.filter(Boolean).join(" ");
|
|
13
14
|
}
|
|
15
|
+
/** `null`/缺数据的统一显示符:Table 与 Stat 共用,不补成 0。 */
|
|
16
|
+
const MISSING_MARK = "—";
|
|
14
17
|
/** 纵向依次排列:网页是块级堆叠,终端是逐块输出(块间空一行)。两面都按声明序。 */
|
|
15
18
|
export const Col = defineComponent({
|
|
16
19
|
web({ children, className }) {
|
|
@@ -56,18 +59,107 @@ export const Row = defineComponent({
|
|
|
56
59
|
},
|
|
57
60
|
});
|
|
58
61
|
Row.displayName = "Row";
|
|
59
|
-
/**
|
|
62
|
+
/** boxed 单个 cell 的完整边框:同一份 TextGridPlan 决定内容宽度,四边不因换行残缺。 */
|
|
63
|
+
function boxCellBlock(content, contentWidth) {
|
|
64
|
+
const lines = content.length > 0 ? content.split("\n") : [""];
|
|
65
|
+
const top = `┌${"─".repeat(contentWidth + 2)}┐`;
|
|
66
|
+
const bottom = `└${"─".repeat(contentWidth + 2)}┘`;
|
|
67
|
+
const body = lines.map((line) => `│ ${padDisplay(line, contentWidth)} │`);
|
|
68
|
+
return [top, ...body, bottom].join("\n");
|
|
69
|
+
}
|
|
70
|
+
/** 一个物理行内的 cell 并排:顶对齐、短 block 补空行到同高(joinColumns 已有语义)。 */
|
|
71
|
+
function renderGridRow(blocks, contentWidths, gutter, variant) {
|
|
72
|
+
const separator = " ".repeat(gutter);
|
|
73
|
+
if (variant === "boxed") {
|
|
74
|
+
const boxed = blocks.map((block, i) => boxCellBlock(block, contentWidths[i]));
|
|
75
|
+
const boxWidths = contentWidths.map((w) => w + 4);
|
|
76
|
+
return joinColumns(boxed, boxWidths, separator);
|
|
77
|
+
}
|
|
78
|
+
return joinColumns(blocks, contentWidths, separator);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 自由摘要面板的格子容器:只负责呈现,不读取 Scope、不聚合 Metric。每个直接子节点
|
|
82
|
+
* (数组 / Fragment 先按 ReportNode 规则展平,空分支不占格)是一格;`Col` 把多个区块
|
|
83
|
+
* 归成一格。展平与 text 面排版的算术在 ./grid-layout.ts,这里只做两面结构适配。
|
|
84
|
+
*/
|
|
85
|
+
export const Grid = defineComponent({
|
|
86
|
+
web({ children, columns, variant, density, className }) {
|
|
87
|
+
const normalized = normalizeGrid({ children, columns, variant, density });
|
|
88
|
+
const style = { "--nre-grid-max-columns": normalized.columns };
|
|
89
|
+
return (_jsx("div", { className: cx("nre", "nre-grid", `nre-grid--${normalized.variant}`, `nre-grid--${normalized.density}`, className), style: style, children: normalized.cells.map((cell) => (_jsx("div", { className: "nre-grid-cell", children: cell.node }, cell.key))) }));
|
|
90
|
+
},
|
|
91
|
+
text({ children, columns, variant, density }, ctx) {
|
|
92
|
+
const normalized = normalizeGrid({ children, columns, variant, density });
|
|
93
|
+
if (normalized.cells.length === 0)
|
|
94
|
+
return "";
|
|
95
|
+
const plan = planTextGrid({
|
|
96
|
+
availableWidth: ctx.width,
|
|
97
|
+
cellCount: normalized.cells.length,
|
|
98
|
+
columns: normalized.columns,
|
|
99
|
+
density: normalized.density,
|
|
100
|
+
});
|
|
101
|
+
// 确定计划后才对每个 cell 调用一次 ctx.render——不为试探列数重复渲染。
|
|
102
|
+
const blocks = normalized.cells.map((cell, i) => ctx.render(cell.node, plan.contentWidths[i % plan.columns]));
|
|
103
|
+
const rows = [];
|
|
104
|
+
for (let start = 0; start < blocks.length; start += plan.columns) {
|
|
105
|
+
const rowBlocks = blocks.slice(start, start + plan.columns);
|
|
106
|
+
const rowWidths = plan.contentWidths.slice(0, rowBlocks.length);
|
|
107
|
+
rows.push(renderGridRow(rowBlocks, rowWidths, plan.gutter, normalized.variant));
|
|
108
|
+
}
|
|
109
|
+
return rows.join("\n\n");
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
Grid.displayName = "Grid";
|
|
113
|
+
/**
|
|
114
|
+
* Grid / Stat 共享的显示值规范化:LocalizedText 走 resolveLocalizedText,number 走当前
|
|
115
|
+
* locale 的 Intl.NumberFormat,null 变 —;两个面调同一份,不各写一套。
|
|
116
|
+
*/
|
|
117
|
+
function resolveStatDisplay(value, locale) {
|
|
118
|
+
if (value === null)
|
|
119
|
+
return MISSING_MARK;
|
|
120
|
+
if (typeof value === "number")
|
|
121
|
+
return new Intl.NumberFormat(locale).format(value);
|
|
122
|
+
return resolveLocalizedText(value, locale);
|
|
123
|
+
}
|
|
124
|
+
/** label / 主值 / 辅助信息的最小内容单元;可以脱离 Grid 单独使用。 */
|
|
125
|
+
export const Stat = defineComponent({
|
|
126
|
+
web({ label, value, detail, tone = "neutral", className }, ctx) {
|
|
127
|
+
return (_jsxs("div", { className: cx("nre", "nre-stat", `nre-stat--${tone}`, className), children: [_jsx("div", { className: "nre-stat-label", children: resolveLocalizedText(label, ctx.locale) }), _jsx("div", { className: "nre-stat-value", children: resolveStatDisplay(value, ctx.locale) }), detail !== undefined ? _jsx("div", { className: "nre-stat-detail", children: resolveLocalizedText(detail, ctx.locale) }) : null] }));
|
|
128
|
+
},
|
|
129
|
+
text({ label, value, detail }, ctx) {
|
|
130
|
+
const lines = [resolveLocalizedText(label, ctx.locale), resolveStatDisplay(value, ctx.locale)];
|
|
131
|
+
if (detail !== undefined)
|
|
132
|
+
lines.push(resolveLocalizedText(detail, ctx.locale));
|
|
133
|
+
return lines.flatMap((line) => wrapDisplay(line, ctx.width)).join("\n");
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
Stat.displayName = "Stat";
|
|
137
|
+
/** 标题行右侧同一行放不下 meta 时,退化成标题后两格缩进折行。 */
|
|
138
|
+
function sectionHeadingLine(heading, metaText, width) {
|
|
139
|
+
if (metaText === undefined)
|
|
140
|
+
return heading;
|
|
141
|
+
const gap = width - stringWidth(heading) - stringWidth(metaText);
|
|
142
|
+
if (gap >= 1)
|
|
143
|
+
return heading + " ".repeat(gap) + metaText;
|
|
144
|
+
const wrapped = wrapDisplay(metaText, Math.max(1, width - 2)).join("\n");
|
|
145
|
+
return `${heading}\n${indentBlock(wrapped, " ")}`;
|
|
146
|
+
}
|
|
147
|
+
/** 带标题的块:网页是标题层级(可选 meta 同行右对齐),终端是标题行加缩进。 */
|
|
60
148
|
export const Section = defineComponent({
|
|
61
|
-
web({ title, children, className }, ctx) {
|
|
62
|
-
|
|
149
|
+
web({ title, meta, children, className }, ctx) {
|
|
150
|
+
const titleText = resolveLocalizedText(title, ctx.locale);
|
|
151
|
+
const metaText = meta !== undefined ? resolveLocalizedText(meta, ctx.locale) : undefined;
|
|
152
|
+
return (_jsxs("section", { className: cx("nre", "nre-section", className), children: [metaText !== undefined ? (_jsxs("header", { className: "nre-section-header", children: [_jsx("h2", { className: "nre-section-title", children: titleText }), _jsx("p", { className: "nre-section-meta", children: metaText })] })) : (_jsx("h2", { className: "nre-section-title", children: titleText })), children] }));
|
|
63
153
|
},
|
|
64
|
-
text({ title, children }, ctx) {
|
|
154
|
+
text({ title, meta, children }, ctx) {
|
|
65
155
|
const heading = resolveLocalizedText(title, ctx.locale);
|
|
156
|
+
const metaText = meta !== undefined ? resolveLocalizedText(meta, ctx.locale) : undefined;
|
|
157
|
+
const headingLine = sectionHeadingLine(heading, metaText, ctx.width);
|
|
66
158
|
const body = childArray(children)
|
|
67
159
|
.map((child) => ctx.render(child, ctx.width - 2))
|
|
68
160
|
.filter((block) => block.length > 0)
|
|
69
161
|
.join("\n\n");
|
|
70
|
-
return body.length > 0 ? `${
|
|
162
|
+
return body.length > 0 ? `${headingLine}\n${indentBlock(body, " ")}` : headingLine;
|
|
71
163
|
},
|
|
72
164
|
});
|
|
73
165
|
Section.displayName = "Section";
|
|
@@ -164,7 +256,6 @@ export const Tab = defineComponent({
|
|
|
164
256
|
});
|
|
165
257
|
Tab.displayName = "Tab";
|
|
166
258
|
Tab[COMPONENT_ROLE] = "tab";
|
|
167
|
-
const MISSING_MARK = "—";
|
|
168
259
|
/** 列 key 唯一、行 cells 不携带未声明 key、空列拒绝——无类型 JS 输入在渲染前同样校验。 */
|
|
169
260
|
function validateTableProps(props) {
|
|
170
261
|
if (!Array.isArray(props.columns) || props.columns.length === 0) {
|
|
@@ -212,7 +303,7 @@ export const Table = defineComponent({
|
|
|
212
303
|
const value = row.cells[column.key];
|
|
213
304
|
const missing = value === null || value === undefined;
|
|
214
305
|
return (_jsx("td", { className: alignClass(column.align), children: missing ? _jsx("span", { className: "nre-missing", children: MISSING_MARK }) : value }, column.key));
|
|
215
|
-
}), hasLocator ? (_jsx("td", { children: row.locator ? (_jsx("a", { className: "nre-locator", href: ctx.attemptHref(row.locator), children: row.locator })) : (_jsx("span", { className: "nre-missing", children: MISSING_MARK })) })) : null] }, row.key))) })] }));
|
|
306
|
+
}), hasLocator ? (_jsx("td", { children: row.locator ? (ctx.attemptHref ? (_jsx("a", { className: "nre-locator", href: ctx.attemptHref(row.locator), children: row.locator })) : (_jsx("span", { className: "nre-locator", children: row.locator }))) : (_jsx("span", { className: "nre-missing", children: MISSING_MARK })) })) : null] }, row.key))) })] }));
|
|
216
307
|
},
|
|
217
308
|
text(props, ctx) {
|
|
218
309
|
validateTableProps(props);
|