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,15 +1,19 @@
|
|
|
1
|
-
// 排版原语 Row / Col / Section / Text / Style / Tabs / Tab / Table
|
|
2
|
-
//
|
|
1
|
+
// 排版原语 Row / Col / Grid / Section / Stat / Text / Style / Tabs / Tab / Table:十个内置
|
|
2
|
+
// 双面组件,没有特殊机制(docs/feature/reports/library/layout.md)。web 面是普通 React 渲染;
|
|
3
3
|
// text 面用 ctx.render(child, 子宽) 显式传宽。Style 注入页级全局 CSS(树位置只决定声明
|
|
4
4
|
// 顺序),text 面渲染为空。Table 是自定义表的标准件,官方表状组件的 text 面也建在它上面。
|
|
5
|
+
// Grid / Stat 的语义层(normalizeGrid 展平校验、text 面的 TextGridPlan 排版)在
|
|
6
|
+
// ./grid-layout.ts(docs/feature/reports/architecture.md「排版原语的语义层与面内布局」);
|
|
7
|
+
// 本文件只声明两面适配,不重复那份算术。
|
|
5
8
|
|
|
6
|
-
import type { ReactNode } from "react";
|
|
7
|
-
import type { AttemptLocator } from "
|
|
9
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
10
|
+
import type { AttemptLocator } from "../../results/locator.ts";
|
|
8
11
|
import { COMPONENT_RAW_CHILDREN, COMPONENT_ROLE, defineComponent, type ReportNode } from "./tree.ts";
|
|
9
|
-
import { localeText, resolveLocalizedText, type LocalizedText, type ReportLocale } from "
|
|
10
|
-
import { indentBlock, joinColumns, stringWidth, wrapDisplay } from "
|
|
11
|
-
import type { ColumnAlign } from "
|
|
12
|
-
import { renderTableText } from "./text
|
|
12
|
+
import { localeText, resolveLocalizedText, type LocalizedText, type ReportLocale } from "../model/locale.ts";
|
|
13
|
+
import { indentBlock, joinColumns, padDisplay, stringWidth, wrapDisplay } from "../model/text-layout.ts";
|
|
14
|
+
import type { ColumnAlign } from "../model/text-layout.ts";
|
|
15
|
+
import { renderTableText } from "./table-text.ts";
|
|
16
|
+
import { normalizeGrid, planTextGrid, type GridDensity, type GridVariant } from "./grid-layout.ts";
|
|
13
17
|
|
|
14
18
|
function childArray(children: ReportNode): ReportNode[] {
|
|
15
19
|
if (children === null || children === undefined || typeof children === "boolean") return [];
|
|
@@ -20,6 +24,9 @@ function cx(...parts: (string | undefined)[]): string {
|
|
|
20
24
|
return parts.filter(Boolean).join(" ");
|
|
21
25
|
}
|
|
22
26
|
|
|
27
|
+
/** `null`/缺数据的统一显示符:Table 与 Stat 共用,不补成 0。 */
|
|
28
|
+
const MISSING_MARK = "—";
|
|
29
|
+
|
|
23
30
|
export interface LayoutProps {
|
|
24
31
|
children?: ReportNode;
|
|
25
32
|
className?: string;
|
|
@@ -76,27 +83,168 @@ export const Row = defineComponent<RowProps>({
|
|
|
76
83
|
});
|
|
77
84
|
Row.displayName = "Row";
|
|
78
85
|
|
|
86
|
+
// ───────────────────────── Grid / Stat ─────────────────────────
|
|
87
|
+
|
|
88
|
+
export interface GridProps extends LayoutProps {
|
|
89
|
+
/** 宽面最多摆几列;必须是有限正整数,运行时校验(docs/feature/reports/library/layout.md「Grid 与 Stat」)。 */
|
|
90
|
+
columns: number;
|
|
91
|
+
/** plain 无框;boxed 给每个 cell 完整四边框。默认 plain。 */
|
|
92
|
+
variant?: GridVariant;
|
|
93
|
+
/** 改变格内留白,并调整内置 Stat 的主值字号;不改变内容和分组。默认 regular。 */
|
|
94
|
+
density?: GridDensity;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** boxed 单个 cell 的完整边框:同一份 TextGridPlan 决定内容宽度,四边不因换行残缺。 */
|
|
98
|
+
function boxCellBlock(content: string, contentWidth: number): string {
|
|
99
|
+
const lines = content.length > 0 ? content.split("\n") : [""];
|
|
100
|
+
const top = `┌${"─".repeat(contentWidth + 2)}┐`;
|
|
101
|
+
const bottom = `└${"─".repeat(contentWidth + 2)}┘`;
|
|
102
|
+
const body = lines.map((line) => `│ ${padDisplay(line, contentWidth)} │`);
|
|
103
|
+
return [top, ...body, bottom].join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** 一个物理行内的 cell 并排:顶对齐、短 block 补空行到同高(joinColumns 已有语义)。 */
|
|
107
|
+
function renderGridRow(blocks: string[], contentWidths: number[], gutter: number, variant: GridVariant): string {
|
|
108
|
+
const separator = " ".repeat(gutter);
|
|
109
|
+
if (variant === "boxed") {
|
|
110
|
+
const boxed = blocks.map((block, i) => boxCellBlock(block, contentWidths[i]));
|
|
111
|
+
const boxWidths = contentWidths.map((w) => w + 4);
|
|
112
|
+
return joinColumns(boxed, boxWidths, separator);
|
|
113
|
+
}
|
|
114
|
+
return joinColumns(blocks, contentWidths, separator);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 自由摘要面板的格子容器:只负责呈现,不读取 Scope、不聚合 Metric。每个直接子节点
|
|
119
|
+
* (数组 / Fragment 先按 ReportNode 规则展平,空分支不占格)是一格;`Col` 把多个区块
|
|
120
|
+
* 归成一格。展平与 text 面排版的算术在 ./grid-layout.ts,这里只做两面结构适配。
|
|
121
|
+
*/
|
|
122
|
+
export const Grid = defineComponent<GridProps>({
|
|
123
|
+
web({ children, columns, variant, density, className }) {
|
|
124
|
+
const normalized = normalizeGrid({ children, columns, variant, density });
|
|
125
|
+
const style: CSSProperties = { "--nre-grid-max-columns": normalized.columns } as CSSProperties;
|
|
126
|
+
return (
|
|
127
|
+
<div
|
|
128
|
+
className={cx("nre", "nre-grid", `nre-grid--${normalized.variant}`, `nre-grid--${normalized.density}`, className)}
|
|
129
|
+
style={style}
|
|
130
|
+
>
|
|
131
|
+
{normalized.cells.map((cell) => (
|
|
132
|
+
<div className="nre-grid-cell" key={cell.key}>
|
|
133
|
+
{cell.node as ReactNode}
|
|
134
|
+
</div>
|
|
135
|
+
))}
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
},
|
|
139
|
+
text({ children, columns, variant, density }, ctx) {
|
|
140
|
+
const normalized = normalizeGrid({ children, columns, variant, density });
|
|
141
|
+
if (normalized.cells.length === 0) return "";
|
|
142
|
+
const plan = planTextGrid({
|
|
143
|
+
availableWidth: ctx.width,
|
|
144
|
+
cellCount: normalized.cells.length,
|
|
145
|
+
columns: normalized.columns,
|
|
146
|
+
density: normalized.density,
|
|
147
|
+
});
|
|
148
|
+
// 确定计划后才对每个 cell 调用一次 ctx.render——不为试探列数重复渲染。
|
|
149
|
+
const blocks = normalized.cells.map((cell, i) => ctx.render(cell.node, plan.contentWidths[i % plan.columns]));
|
|
150
|
+
const rows: string[] = [];
|
|
151
|
+
for (let start = 0; start < blocks.length; start += plan.columns) {
|
|
152
|
+
const rowBlocks = blocks.slice(start, start + plan.columns);
|
|
153
|
+
const rowWidths = plan.contentWidths.slice(0, rowBlocks.length);
|
|
154
|
+
rows.push(renderGridRow(rowBlocks, rowWidths, plan.gutter, normalized.variant));
|
|
155
|
+
}
|
|
156
|
+
return rows.join("\n\n");
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
Grid.displayName = "Grid";
|
|
160
|
+
|
|
161
|
+
export type StatTone = "neutral" | "positive" | "negative" | "warning";
|
|
162
|
+
|
|
163
|
+
export interface StatProps {
|
|
164
|
+
label: LocalizedText;
|
|
165
|
+
/** 已格式化的主值;null 明确渲染为 —,不补成 0。 */
|
|
166
|
+
value: LocalizedText | number | null;
|
|
167
|
+
/** 主值下面的短解释;省略时不留空行。 */
|
|
168
|
+
detail?: LocalizedText;
|
|
169
|
+
/** 主值的语义色;不从正负号、单位或 Metric.better 猜。默认 neutral。 */
|
|
170
|
+
tone?: StatTone;
|
|
171
|
+
className?: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Grid / Stat 共享的显示值规范化:LocalizedText 走 resolveLocalizedText,number 走当前
|
|
176
|
+
* locale 的 Intl.NumberFormat,null 变 —;两个面调同一份,不各写一套。
|
|
177
|
+
*/
|
|
178
|
+
function resolveStatDisplay(value: LocalizedText | number | null, locale: ReportLocale): string {
|
|
179
|
+
if (value === null) return MISSING_MARK;
|
|
180
|
+
if (typeof value === "number") return new Intl.NumberFormat(locale).format(value);
|
|
181
|
+
return resolveLocalizedText(value, locale);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** label / 主值 / 辅助信息的最小内容单元;可以脱离 Grid 单独使用。 */
|
|
185
|
+
export const Stat = defineComponent<StatProps>({
|
|
186
|
+
web({ label, value, detail, tone = "neutral", className }, ctx) {
|
|
187
|
+
return (
|
|
188
|
+
<div className={cx("nre", "nre-stat", `nre-stat--${tone}`, className)}>
|
|
189
|
+
<div className="nre-stat-label">{resolveLocalizedText(label, ctx.locale)}</div>
|
|
190
|
+
<div className="nre-stat-value">{resolveStatDisplay(value, ctx.locale)}</div>
|
|
191
|
+
{detail !== undefined ? <div className="nre-stat-detail">{resolveLocalizedText(detail, ctx.locale)}</div> : null}
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
},
|
|
195
|
+
text({ label, value, detail }, ctx) {
|
|
196
|
+
const lines = [resolveLocalizedText(label, ctx.locale), resolveStatDisplay(value, ctx.locale)];
|
|
197
|
+
if (detail !== undefined) lines.push(resolveLocalizedText(detail, ctx.locale));
|
|
198
|
+
return lines.flatMap((line) => wrapDisplay(line, ctx.width)).join("\n");
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
Stat.displayName = "Stat";
|
|
202
|
+
|
|
203
|
+
// ───────────────────────── Section ─────────────────────────
|
|
204
|
+
|
|
79
205
|
export interface SectionProps extends LayoutProps {
|
|
80
206
|
title: LocalizedText;
|
|
207
|
+
/** 标题行右侧的短元信息;text 面与标题同一行,空间不足时换到下一行。 */
|
|
208
|
+
meta?: LocalizedText;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** 标题行右侧同一行放不下 meta 时,退化成标题后两格缩进折行。 */
|
|
212
|
+
function sectionHeadingLine(heading: string, metaText: string | undefined, width: number): string {
|
|
213
|
+
if (metaText === undefined) return heading;
|
|
214
|
+
const gap = width - stringWidth(heading) - stringWidth(metaText);
|
|
215
|
+
if (gap >= 1) return heading + " ".repeat(gap) + metaText;
|
|
216
|
+
const wrapped = wrapDisplay(metaText, Math.max(1, width - 2)).join("\n");
|
|
217
|
+
return `${heading}\n${indentBlock(wrapped, " ")}`;
|
|
81
218
|
}
|
|
82
219
|
|
|
83
|
-
/**
|
|
220
|
+
/** 带标题的块:网页是标题层级(可选 meta 同行右对齐),终端是标题行加缩进。 */
|
|
84
221
|
export const Section = defineComponent<SectionProps>({
|
|
85
|
-
web({ title, children, className }, ctx) {
|
|
222
|
+
web({ title, meta, children, className }, ctx) {
|
|
223
|
+
const titleText = resolveLocalizedText(title, ctx.locale);
|
|
224
|
+
const metaText = meta !== undefined ? resolveLocalizedText(meta, ctx.locale) : undefined;
|
|
86
225
|
return (
|
|
87
226
|
<section className={cx("nre", "nre-section", className)}>
|
|
88
|
-
|
|
227
|
+
{metaText !== undefined ? (
|
|
228
|
+
<header className="nre-section-header">
|
|
229
|
+
<h2 className="nre-section-title">{titleText}</h2>
|
|
230
|
+
<p className="nre-section-meta">{metaText}</p>
|
|
231
|
+
</header>
|
|
232
|
+
) : (
|
|
233
|
+
<h2 className="nre-section-title">{titleText}</h2>
|
|
234
|
+
)}
|
|
89
235
|
{children as ReactNode}
|
|
90
236
|
</section>
|
|
91
237
|
);
|
|
92
238
|
},
|
|
93
|
-
text({ title, children }, ctx) {
|
|
239
|
+
text({ title, meta, children }, ctx) {
|
|
94
240
|
const heading = resolveLocalizedText(title, ctx.locale);
|
|
241
|
+
const metaText = meta !== undefined ? resolveLocalizedText(meta, ctx.locale) : undefined;
|
|
242
|
+
const headingLine = sectionHeadingLine(heading, metaText, ctx.width);
|
|
95
243
|
const body = childArray(children)
|
|
96
244
|
.map((child) => ctx.render(child, ctx.width - 2))
|
|
97
245
|
.filter((block) => block.length > 0)
|
|
98
246
|
.join("\n\n");
|
|
99
|
-
return body.length > 0 ? `${
|
|
247
|
+
return body.length > 0 ? `${headingLine}\n${indentBlock(body, " ")}` : headingLine;
|
|
100
248
|
},
|
|
101
249
|
});
|
|
102
250
|
Section.displayName = "Section";
|
|
@@ -269,8 +417,6 @@ export interface TableProps {
|
|
|
269
417
|
className?: string;
|
|
270
418
|
}
|
|
271
419
|
|
|
272
|
-
const MISSING_MARK = "—";
|
|
273
|
-
|
|
274
420
|
/** 列 key 唯一、行 cells 不携带未声明 key、空列拒绝——无类型 JS 输入在渲染前同样校验。 */
|
|
275
421
|
function validateTableProps(props: TableProps): void {
|
|
276
422
|
if (!Array.isArray(props.columns) || props.columns.length === 0) {
|
|
@@ -344,9 +490,13 @@ export const Table = defineComponent<TableProps>({
|
|
|
344
490
|
{hasLocator ? (
|
|
345
491
|
<td>
|
|
346
492
|
{row.locator ? (
|
|
347
|
-
|
|
348
|
-
{row.locator}
|
|
349
|
-
|
|
493
|
+
ctx.attemptHref ? (
|
|
494
|
+
<a className="nre-locator" href={ctx.attemptHref(row.locator)}>
|
|
495
|
+
{row.locator}
|
|
496
|
+
</a>
|
|
497
|
+
) : (
|
|
498
|
+
<span className="nre-locator">{row.locator}</span>
|
|
499
|
+
)
|
|
350
500
|
) : (
|
|
351
501
|
<span className="nre-missing">{MISSING_MARK}</span>
|
|
352
502
|
)}
|
|
@@ -5,26 +5,13 @@
|
|
|
5
5
|
// `extends` 在另一份报告上叠外壳——页归 base、外壳逐字段覆盖,合并在调用时折叠完成,
|
|
6
6
|
// 宿主装载看到的永远是已折叠的普通产物。
|
|
7
7
|
//
|
|
8
|
-
// renderReportToText
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
renderNodeToText,
|
|
16
|
-
resolveReportTree,
|
|
17
|
-
validateReportTree,
|
|
18
|
-
ResolveMemo,
|
|
19
|
-
type ReportNode,
|
|
20
|
-
type TextRenderOptions,
|
|
21
|
-
} from "./tree.ts";
|
|
22
|
-
import {
|
|
23
|
-
localizedTextEquals,
|
|
24
|
-
resolveLocalizedText,
|
|
25
|
-
type LocalizedText,
|
|
26
|
-
type ReportLocale,
|
|
27
|
-
} from "./locale.ts";
|
|
8
|
+
// text/web 两个宿主的渲染入口(renderReportToText / renderReportToStaticHtml)在 ../runtime/
|
|
9
|
+
// (text.ts / web.ts);这里只有 ReportDefinition 的类型体系、装载规范化与元数据折叠
|
|
10
|
+
// (buildReportMeta / resolveReportTitle),不做任何渲染。
|
|
11
|
+
|
|
12
|
+
import type { Scope } from "../../results/types.ts";
|
|
13
|
+
import type { ReportNode } from "./tree.ts";
|
|
14
|
+
import { localizedTextEquals, type LocalizedText } from "../model/locale.ts";
|
|
28
15
|
|
|
29
16
|
// ───────────────────────── 公开形状 ─────────────────────────
|
|
30
17
|
|
|
@@ -73,7 +60,7 @@ export interface ReportShell {
|
|
|
73
60
|
|
|
74
61
|
export type NonEmptyArray<T> = readonly [T, ...T[]];
|
|
75
62
|
|
|
76
|
-
export interface
|
|
63
|
+
export interface ReportPageBase {
|
|
77
64
|
/** 页面身份:`--page <id>` 的取值、web 路由 `#/page/<id>` 与导航锚。小写字母、数字与连字符。 */
|
|
78
65
|
id: string;
|
|
79
66
|
/** 导航中的页名。 */
|
|
@@ -82,6 +69,18 @@ export interface ReportPage {
|
|
|
82
69
|
content: ReportNode;
|
|
83
70
|
}
|
|
84
71
|
|
|
72
|
+
/**
|
|
73
|
+
* 页按输入分两种形态,仍是同一个类型族,走同一条 resolve → validate → render 管线
|
|
74
|
+
* (docs/feature/reports/architecture.md「Attempt 详情是一张参数化 page」):
|
|
75
|
+
* - `input` 省略或为 `"scope"`:消费宿主选择的 Scope;省略时规范化为 `navigation: true`。
|
|
76
|
+
* - `input: "attempt"`:以 locator 为参数,消费一份 AttemptEvidence;没有 locator 时不能打开,
|
|
77
|
+
* 必须显式 `navigation: false`,不进导航。
|
|
78
|
+
* 一份报告至多声明一张 attempt-input page。
|
|
79
|
+
*/
|
|
80
|
+
export type ReportPage =
|
|
81
|
+
| (ReportPageBase & { input?: "scope"; navigation?: boolean })
|
|
82
|
+
| (ReportPageBase & { input: "attempt"; navigation: false });
|
|
83
|
+
|
|
85
84
|
/** content / pages / extends 三选一由类型表达,不把非法状态留到运行期。 */
|
|
86
85
|
export type ReportDef = ReportShell &
|
|
87
86
|
(
|
|
@@ -92,7 +91,7 @@ export type ReportDef = ReportShell &
|
|
|
92
91
|
extends?: never;
|
|
93
92
|
}
|
|
94
93
|
| {
|
|
95
|
-
/**
|
|
94
|
+
/** 非空页列表;`navigation !== false` 的项按数组顺序显示。 */
|
|
96
95
|
pages: NonEmptyArray<ReportPage>;
|
|
97
96
|
content?: never;
|
|
98
97
|
extends?: never;
|
|
@@ -128,17 +127,27 @@ export interface ReportDefinition {
|
|
|
128
127
|
readonly pages: NonEmptyArray<ReportPage>;
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
/**
|
|
130
|
+
/** 规范化后页列表在 ctx.report 上的元数据形态(id / 导航页名 / 输入声明 / 导航资格)。 */
|
|
131
|
+
export interface ReportMetaPage {
|
|
132
|
+
id: string;
|
|
133
|
+
title: LocalizedText;
|
|
134
|
+
input: "scope" | "attempt";
|
|
135
|
+
navigation: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 规范化后的报告声明,经组合组件 ctx.report 只读可见(scripts / styles 是注入资产,不进)。
|
|
140
|
+
* 不携带"当前是哪一页"——那由 ctx.page(PageContext)表达,两者不是同一份状态
|
|
141
|
+
* (docs/feature/reports/library/shell.md「行为约束」)。
|
|
142
|
+
*/
|
|
132
143
|
export interface ReportMeta {
|
|
133
144
|
/** 走完回退链(声明 title → 唯一快照 name → 内置文案「Eval 运行结果 / Eval Results」)后的标题。 */
|
|
134
145
|
title: LocalizedText;
|
|
135
146
|
/** 页头外链;声明省略时为空数组。 */
|
|
136
147
|
links: readonly ReportLink[];
|
|
137
148
|
footer?: LocalizedText;
|
|
138
|
-
/**
|
|
139
|
-
pages: NonEmptyArray<
|
|
140
|
-
/** 当前渲染中的页 id。 */
|
|
141
|
-
pageId: string;
|
|
149
|
+
/** 规范化后的页列表,恒非空。 */
|
|
150
|
+
pages: NonEmptyArray<ReportMetaPage>;
|
|
142
151
|
}
|
|
143
152
|
|
|
144
153
|
/** 单页缩写展开出的唯一页 id 与内置页名。 */
|
|
@@ -332,6 +341,53 @@ function assertHeadTags(tags: unknown): HeadTag[] {
|
|
|
332
341
|
return tags as HeadTag[];
|
|
333
342
|
}
|
|
334
343
|
|
|
344
|
+
/**
|
|
345
|
+
* page 的 input / navigation 规范化(shell.md「page 显式声明输入」):省略或 "scope" 时补
|
|
346
|
+
* `input: "scope"`,`navigation` 缺省为 true;"attempt" 必须显式 `navigation: false`——
|
|
347
|
+
* 没有 locator 时不可打开,不能悄悄挤进导航,省略或传 true 都在装载期报错。
|
|
348
|
+
*/
|
|
349
|
+
function normalizePageInputAndNavigation(page: Record<string, unknown>): ReportPage {
|
|
350
|
+
const input = page.input;
|
|
351
|
+
if (input === undefined || input === "scope") {
|
|
352
|
+
return {
|
|
353
|
+
id: page.id as string,
|
|
354
|
+
title: page.title as LocalizedText,
|
|
355
|
+
content: page.content as ReportNode,
|
|
356
|
+
input: "scope",
|
|
357
|
+
navigation: page.navigation !== false,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (input === "attempt") {
|
|
361
|
+
if (page.navigation !== false) {
|
|
362
|
+
throw new Error(
|
|
363
|
+
`Report page "${page.id}" declares input: "attempt" but not navigation: false — an attempt-input page has no content without a locator, so it must not appear in navigation. Add navigation: false.`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
id: page.id as string,
|
|
368
|
+
title: page.title as LocalizedText,
|
|
369
|
+
content: page.content as ReportNode,
|
|
370
|
+
input: "attempt",
|
|
371
|
+
navigation: false,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Report page "${page.id}" input ${JSON.stringify(input)} is not valid — input is omitted, "scope", or "attempt".`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** 一份报告至多一张 attempt-input page,避免 show @<locator> 与 locator 链接出现多个目标。 */
|
|
380
|
+
function assertAtMostOneAttemptPage(pages: readonly ReportPage[]): void {
|
|
381
|
+
const attemptPages = pages.filter((p) => p.input === "attempt");
|
|
382
|
+
if (attemptPages.length > 1) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`A report can declare at most one input: "attempt" page — got ${attemptPages.length} (${attemptPages
|
|
385
|
+
.map((p) => `"${p.id}"`)
|
|
386
|
+
.join(", ")}). Keep one and remove the others, or fold their content into a single attempt-input page.`,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
335
391
|
export function defineReport(content: ReportNode): ReportDefinition;
|
|
336
392
|
export function defineReport(def: ReportDef): ReportDefinition;
|
|
337
393
|
export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
|
|
@@ -379,7 +435,9 @@ export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
|
|
|
379
435
|
pages = base.pages;
|
|
380
436
|
} else if (hasContent) {
|
|
381
437
|
assertNotDefinition(def.content, 'defineReport "content"');
|
|
382
|
-
pages = [
|
|
438
|
+
pages = [
|
|
439
|
+
{ id: DEFAULT_PAGE_ID, title: DEFAULT_PAGE_TITLE, input: "scope", navigation: true, content: def.content as ReportNode },
|
|
440
|
+
];
|
|
383
441
|
} else {
|
|
384
442
|
const raw = def.pages as unknown;
|
|
385
443
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
@@ -388,6 +446,7 @@ export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
|
|
|
388
446
|
);
|
|
389
447
|
}
|
|
390
448
|
const seen = new Set<string>();
|
|
449
|
+
const normalized: ReportPage[] = [];
|
|
391
450
|
for (const page of raw as Array<Record<string, unknown>>) {
|
|
392
451
|
if (typeof page?.id !== "string" || !PAGE_ID_PATTERN.test(page.id)) {
|
|
393
452
|
throw new Error(
|
|
@@ -402,9 +461,11 @@ export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
|
|
|
402
461
|
seen.add(page.id);
|
|
403
462
|
assertLocalizedText(page.title, `Report page "${page.id}" title`);
|
|
404
463
|
assertNotDefinition(page.content, `Report page "${page.id}" content`);
|
|
464
|
+
normalized.push(normalizePageInputAndNavigation(page));
|
|
405
465
|
}
|
|
406
|
-
pages =
|
|
466
|
+
pages = normalized;
|
|
407
467
|
}
|
|
468
|
+
assertAtMostOneAttemptPage(pages);
|
|
408
469
|
|
|
409
470
|
if (def.title !== undefined) assertLocalizedText(def.title, "defineReport title");
|
|
410
471
|
if (def.footer !== undefined) assertLocalizedText(def.footer, "defineReport footer");
|
|
@@ -482,145 +543,17 @@ export function resolveReportTitle(definition: ReportDefinition, scope: Scope):
|
|
|
482
543
|
return names.every((name) => localizedTextEquals(name, first)) ? first : FALLBACK_REPORT_TITLE;
|
|
483
544
|
}
|
|
484
545
|
|
|
485
|
-
/** 规范化声明 → 组合组件可见的 ReportMeta(scripts / styles
|
|
486
|
-
export function buildReportMeta(definition: ReportDefinition, scope: Scope
|
|
546
|
+
/** 规范化声明 → 组合组件可见的 ReportMeta(scripts / styles 是注入资产,不进;不携带当前页)。 */
|
|
547
|
+
export function buildReportMeta(definition: ReportDefinition, scope: Scope): ReportMeta {
|
|
487
548
|
return {
|
|
488
549
|
title: resolveReportTitle(definition, scope),
|
|
489
550
|
links: definition.links,
|
|
490
551
|
...(definition.footer !== undefined ? { footer: definition.footer } : {}),
|
|
491
|
-
pages: definition.pages.map((page) => ({
|
|
492
|
-
id:
|
|
493
|
-
title:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// ───────────────────────── 页选择与 text 宿主入口 ─────────────────────────
|
|
500
|
-
|
|
501
|
-
/** `--page` 未命中:宿主据此按用法错误退出并列出可用页 id。 */
|
|
502
|
-
export class ReportPageNotFoundError extends Error {
|
|
503
|
-
readonly pageId: string;
|
|
504
|
-
readonly available: string[];
|
|
505
|
-
constructor(pageId: string, available: string[]) {
|
|
506
|
-
super(`page "${pageId}" not found. Available pages: ${available.join(", ")}`);
|
|
507
|
-
this.pageId = pageId;
|
|
508
|
-
this.available = available;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
export function pickReportPage(definition: ReportDefinition, pageId?: string): ReportPage {
|
|
513
|
-
if (pageId === undefined) return definition.pages[0];
|
|
514
|
-
const page = definition.pages.find((p) => p.id === pageId);
|
|
515
|
-
if (!page) {
|
|
516
|
-
throw new ReportPageNotFoundError(
|
|
517
|
-
pageId,
|
|
518
|
-
definition.pages.map((p) => p.id),
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
return page;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/** 宿主注入的渲染上下文:官方口径挑好的 Scope 与结果根完整读取面。 */
|
|
525
|
-
export interface ReportHostContext {
|
|
526
|
-
scope: Scope;
|
|
527
|
-
/** 组合组件 ctx.results 的来源;历史视图从这里自行挑 Snapshot[]。 */
|
|
528
|
-
results: Results;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
export interface RenderReportTextOptions extends TextRenderOptions {
|
|
532
|
-
/** 渲染哪一页;缺省第一页。未命中抛 ReportPageNotFoundError。 */
|
|
533
|
-
pageId?: string;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* text 宿主的装载语义:选页 → resolve(组合展开 + spec 取数,唯一的 await 边界)→ 树校验 →
|
|
538
|
-
* 遍历渲染 text 面。不需要 react-dom。宿主不在报告树外另设警告通道——挑选警告的呈现件是
|
|
539
|
-
* `ScopeWarnings` 组件,内建报告每页都放它,自定义报告放不放是作者义务
|
|
540
|
-
* (docs/feature/reports/architecture.md「Scope 是计算入口」)。
|
|
541
|
-
*/
|
|
542
|
-
export async function renderReportToText(
|
|
543
|
-
definition: ReportDefinition,
|
|
544
|
-
ctx: ReportHostContext,
|
|
545
|
-
options?: RenderReportTextOptions,
|
|
546
|
-
): Promise<string> {
|
|
547
|
-
const page = pickReportPage(definition, options?.pageId);
|
|
548
|
-
const meta = buildReportMeta(definition, ctx.scope, page.id);
|
|
549
|
-
const resolved = await resolveReportTree(page.content, {
|
|
550
|
-
scope: ctx.scope,
|
|
551
|
-
results: ctx.results,
|
|
552
|
-
report: meta,
|
|
553
|
-
memo: new ResolveMemo(),
|
|
554
|
-
});
|
|
555
|
-
validateReportTree(resolved);
|
|
556
|
-
return renderNodeToText(resolved, createTextContext(options));
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/** 页索引标题行(show 多页索引 / view 导航共用的解析结果):按 locale 解析的标题字符串。 */
|
|
560
|
-
export function reportTitleText(definition: ReportDefinition, scope: Scope, locale: ReportLocale): string {
|
|
561
|
-
return resolveLocalizedText(resolveReportTitle(definition, scope), locale);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// ───────────────────────── 逐页(树)渲染入口:宿主联系面 ─────────────────────────
|
|
565
|
-
|
|
566
|
-
/** 宿主索引命令的完整上下文(docs/feature/reports/show/reports.md「索引命令携带完整上下文」)。 */
|
|
567
|
-
export interface HostCommandContext {
|
|
568
|
-
patterns: string[];
|
|
569
|
-
results?: string;
|
|
570
|
-
experiment?: string;
|
|
571
|
-
report?: string;
|
|
572
|
-
page?: string;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function quoteArg(value: string): string {
|
|
576
|
-
return /^[A-Za-z0-9._/@-]+$/.test(value) ? value : `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/** 按上下文拼组索引的可复制命令:`niceeval show <patterns> --experiment <id> [--results/--report/--page]`。 */
|
|
580
|
-
function experimentCommandFor(ctx: HostCommandContext): (experimentIdPrefix: string) => string {
|
|
581
|
-
return (prefix) => {
|
|
582
|
-
const parts = ["niceeval show", ...ctx.patterns.map(quoteArg), `--experiment ${quoteArg(prefix)}`];
|
|
583
|
-
if (ctx.results !== undefined) parts.push(`--results ${quoteArg(ctx.results)}`);
|
|
584
|
-
if (ctx.report !== undefined) parts.push(`--report ${quoteArg(ctx.report)}`);
|
|
585
|
-
if (ctx.page !== undefined) parts.push(`--page ${quoteArg(ctx.page)}`);
|
|
586
|
-
return parts.join(" ");
|
|
552
|
+
pages: definition.pages.map((page) => ({
|
|
553
|
+
id: page.id,
|
|
554
|
+
title: page.title,
|
|
555
|
+
input: page.input ?? "scope",
|
|
556
|
+
navigation: page.navigation ?? true,
|
|
557
|
+
})) as unknown as NonEmptyArray<ReportMetaPage>,
|
|
587
558
|
};
|
|
588
559
|
}
|
|
589
|
-
|
|
590
|
-
/** 逐页渲染的宿主上下文:官方口径的 Scope、结果根读取面与规范化声明(ctx.report)。 */
|
|
591
|
-
export interface ReportTreeHostContext {
|
|
592
|
-
scope: Scope;
|
|
593
|
-
results: Results;
|
|
594
|
-
report: ReportMeta;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
export interface RenderTreeTextOptions extends TextRenderOptions {
|
|
598
|
-
/** 组索引命令的完整上下文;给了就按它拼命令,experimentCommand 显式注入时以后者为准。 */
|
|
599
|
-
commandContext?: HostCommandContext;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* 渲染一页报告树的 text 面(宿主逐页调用;页选择归宿主):
|
|
604
|
-
* resolve(组合展开 + spec 取数)→ validate → render。宿主不在报告树外另设警告通道,
|
|
605
|
-
* 挑选警告由页内的 `ScopeWarnings` 组件呈现(内建报告每页都放它)。
|
|
606
|
-
*/
|
|
607
|
-
export async function renderReportTreeToText(
|
|
608
|
-
tree: ReportNode,
|
|
609
|
-
ctx: ReportTreeHostContext,
|
|
610
|
-
options?: RenderTreeTextOptions,
|
|
611
|
-
): Promise<string> {
|
|
612
|
-
const resolved = await resolveReportTree(tree, {
|
|
613
|
-
scope: ctx.scope,
|
|
614
|
-
results: ctx.results,
|
|
615
|
-
report: ctx.report,
|
|
616
|
-
memo: new ResolveMemo(),
|
|
617
|
-
});
|
|
618
|
-
validateReportTree(resolved);
|
|
619
|
-
const textCtx = createTextContext({
|
|
620
|
-
...options,
|
|
621
|
-
...(options?.experimentCommand === undefined && options?.commandContext !== undefined
|
|
622
|
-
? { experimentCommand: experimentCommandFor(options.commandContext) }
|
|
623
|
-
: {}),
|
|
624
|
-
});
|
|
625
|
-
return renderNodeToText(resolved, textCtx);
|
|
626
|
-
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { describe, expect, it } from "vitest";
|
|
7
7
|
|
|
8
|
-
import type { Scope } from "
|
|
8
|
+
import type { Scope } from "../../results/index.ts";
|
|
9
9
|
import { buildReportMeta, defineReport } from "./report.ts";
|
|
10
10
|
|
|
11
11
|
const emptyScope = { snapshots: [] } as unknown as Scope;
|
|
@@ -97,6 +97,6 @@ describe("defineReport head 通道(装载校验)", () => {
|
|
|
97
97
|
];
|
|
98
98
|
const definition = defineReport({ content: null, head });
|
|
99
99
|
expect(definition.head).toEqual(head);
|
|
100
|
-
expect(buildReportMeta(definition, emptyScope
|
|
100
|
+
expect(buildReportMeta(definition, emptyScope)).not.toHaveProperty("head");
|
|
101
101
|
});
|
|
102
102
|
});
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
// 列宽按显示宽度算(CJK 记 2 列);null 渲染 —,不补 0;超宽先折最宽的左对齐列,
|
|
7
7
|
// 压到下限仍放不下就从右侧丢列并如实报数(「截断报剩余」是既有契约,不在这里破例)。
|
|
8
8
|
|
|
9
|
-
import type { TableProps, TableRow } from "
|
|
10
|
-
import type { TextContext } from "
|
|
11
|
-
import { countText, localeText, resolveLocalizedText } from "../locale.ts";
|
|
12
|
-
import { charDisplayWidth, renderAlignedRows, stringWidth, wrapDisplay, type ColumnAlign } from "
|
|
9
|
+
import type { TableProps, TableRow } from "./primitives.tsx";
|
|
10
|
+
import type { TextContext } from "./tree.ts";
|
|
11
|
+
import { countText, localeText, resolveLocalizedText } from "../model/locale.ts";
|
|
12
|
+
import { charDisplayWidth, renderAlignedRows, stringWidth, wrapDisplay, type ColumnAlign } from "../model/text-layout.ts";
|
|
13
13
|
|
|
14
14
|
const MISSING_MARK = "—";
|
|
15
15
|
/** 列间距,与 renderAlignedRows 的 join(" ") 一致。 */
|