niceeval 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INDEX.md +55 -0
- package/dist/agents/types.d.ts +317 -0
- package/dist/context/types.d.ts +335 -0
- package/dist/i18n/core.d.ts +11 -0
- package/dist/i18n/en.d.ts +181 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/zh-CN.d.ts +183 -0
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/parse.d.ts +3 -0
- package/dist/o11y/otlp/receiver.d.ts +21 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/o11y/otlp/turn-otel.d.ts +43 -0
- package/dist/o11y/types.d.ts +190 -0
- package/dist/report/aggregate.d.ts +57 -0
- package/dist/report/aggregate.js +212 -0
- package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +1 -0
- package/dist/report/built-ins/cost-pass-rate-comparison.js +17 -0
- package/dist/report/built-ins/index.d.ts +1 -0
- package/dist/report/built-ins/index.js +2 -0
- package/dist/report/components.d.ts +178 -0
- package/dist/report/components.js +132 -0
- package/dist/report/compute.d.ts +106 -0
- package/dist/report/compute.js +618 -0
- package/dist/report/flag.d.ts +6 -0
- package/dist/report/flag.js +8 -0
- package/dist/report/format.d.ts +32 -0
- package/dist/report/format.js +126 -0
- package/dist/report/index.d.ts +19 -0
- package/dist/report/index.js +30 -0
- package/dist/report/load.d.ts +12 -0
- package/dist/report/load.js +56 -0
- package/dist/report/locale.d.ts +83 -0
- package/dist/report/locale.js +160 -0
- package/dist/report/metrics.d.ts +21 -0
- package/dist/report/metrics.js +115 -0
- package/dist/report/primitives.d.ts +64 -0
- package/dist/report/primitives.js +113 -0
- package/dist/report/react/AttemptList.d.ts +23 -0
- package/dist/report/react/AttemptList.js +20 -0
- package/dist/report/react/DeltaTable.d.ts +8 -0
- package/dist/report/react/DeltaTable.js +24 -0
- package/dist/report/react/EvalList.d.ts +10 -0
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentList.d.ts +10 -0
- package/dist/report/react/ExperimentList.js +15 -0
- package/dist/report/react/GroupSummary.d.ts +8 -0
- package/dist/report/react/GroupSummary.js +8 -0
- package/dist/report/react/MetricBars.d.ts +10 -0
- package/dist/report/react/MetricBars.js +48 -0
- package/dist/report/react/MetricLine.d.ts +9 -0
- package/dist/report/react/MetricLine.js +68 -0
- package/dist/report/react/MetricMatrix.d.ts +10 -0
- package/dist/report/react/MetricMatrix.js +23 -0
- package/dist/report/react/MetricScatter.d.ts +9 -0
- package/dist/report/react/MetricScatter.js +100 -0
- package/dist/report/react/MetricTable.d.ts +11 -0
- package/dist/report/react/MetricTable.js +26 -0
- package/dist/report/react/RunOverview.d.ts +8 -0
- package/dist/report/react/RunOverview.js +12 -0
- package/dist/report/react/Scoreboard.d.ts +8 -0
- package/dist/report/react/Scoreboard.js +26 -0
- package/dist/report/react/cell.d.ts +9 -0
- package/dist/report/react/cell.js +9 -0
- package/dist/report/react/chart-math.d.ts +14 -0
- package/dist/report/react/chart-math.js +68 -0
- package/dist/report/react/colors.d.ts +14 -0
- package/dist/report/react/colors.js +45 -0
- package/dist/report/react/fixtures.d.ts +19 -0
- package/dist/report/react/fixtures.js +393 -0
- package/dist/report/react/format.d.ts +3 -0
- package/dist/report/react/format.js +7 -0
- package/dist/report/react/index.d.ts +6 -0
- package/dist/report/react/index.js +14 -0
- package/dist/report/report.d.ts +24 -0
- package/dist/report/report.js +49 -0
- package/dist/report/text/faces.d.ts +29 -0
- package/dist/report/text/faces.js +503 -0
- package/dist/report/text/layout.d.ts +75 -0
- package/dist/report/text/layout.js +173 -0
- package/dist/report/text/plot.d.ts +37 -0
- package/dist/report/text/plot.js +81 -0
- package/dist/report/text/table.d.ts +8 -0
- package/dist/report/text/table.js +86 -0
- package/dist/report/tree.d.ts +99 -0
- package/dist/report/tree.js +228 -0
- package/dist/report/types.d.ts +420 -0
- package/dist/report/types.js +4 -0
- package/dist/report/web.d.ts +15 -0
- package/dist/report/web.js +37 -0
- package/dist/results/annotated-source.d.ts +61 -0
- package/dist/results/attempt-evidence.d.ts +69 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/locator.d.ts +75 -0
- package/dist/results/locator.js +0 -0
- package/dist/results/select.d.ts +45 -0
- package/dist/results/select.js +230 -0
- package/dist/results/source-hash.d.ts +9 -0
- package/dist/results/types.d.ts +204 -0
- package/dist/runner/eval-source.d.ts +22 -0
- package/dist/runner/fingerprint.d.ts +23 -0
- package/dist/runner/types.d.ts +332 -0
- package/dist/sandbox/docker-stream.d.ts +24 -0
- package/dist/sandbox/docker.d.ts +84 -0
- package/dist/sandbox/e2b.d.ts +31 -0
- package/dist/sandbox/errors.d.ts +4 -0
- package/dist/sandbox/local-files.d.ts +6 -0
- package/dist/sandbox/paths.d.ts +4 -0
- package/dist/sandbox/registry.d.ts +13 -0
- package/dist/sandbox/resolve.d.ts +40 -0
- package/dist/sandbox/retry.d.ts +10 -0
- package/dist/sandbox/shell.d.ts +11 -0
- package/dist/sandbox/source-files.d.ts +13 -0
- package/dist/sandbox/types.d.ts +203 -0
- package/dist/sandbox/vercel.d.ts +37 -0
- package/dist/scoring/types.d.ts +70 -0
- package/dist/shared/aggregate.d.ts +29 -0
- package/dist/shared/aggregate.js +55 -0
- package/dist/shared/types.d.ts +27 -0
- package/dist/shared/verdict.d.ts +27 -0
- package/dist/shared/verdict.js +34 -0
- package/dist/tty-line.d.ts +6 -0
- package/dist/types.d.ts +7 -0
- package/dist/util.d.ts +22 -0
- package/docs-site/zh/concepts/adapter.mdx +9 -4
- package/docs-site/zh/concepts/assert.mdx +8 -8
- package/docs-site/zh/concepts/evals.mdx +5 -5
- package/docs-site/zh/concepts/experiment.mdx +3 -1
- package/docs-site/zh/concepts/judge.mdx +9 -9
- package/docs-site/zh/concepts/overview.mdx +2 -2
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/example/claude-code-codex-plugin.mdx +9 -5
- package/docs-site/zh/example/claude-code-codex-skill.mdx +12 -7
- package/docs-site/zh/guides/agent-feedback-loop.mdx +235 -78
- package/docs-site/zh/guides/connect-otel.mdx +3 -3
- package/docs-site/zh/guides/connect-your-agent.mdx +2 -2
- package/docs-site/zh/guides/custom-reports.mdx +196 -36
- package/docs-site/zh/guides/experiments.mdx +7 -0
- package/docs-site/zh/guides/fixtures.mdx +1 -1
- package/docs-site/zh/guides/official-adapters.mdx +31 -9
- package/docs-site/zh/guides/publish-report.mdx +4 -4
- package/docs-site/zh/guides/report-components.mdx +194 -55
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +35 -34
- package/docs-site/zh/guides/runner.mdx +2 -2
- package/docs-site/zh/guides/sandbox-agent.mdx +10 -5
- package/docs-site/zh/guides/sandbox-providers.mdx +102 -0
- package/docs-site/zh/guides/viewing-results.mdx +199 -79
- package/docs-site/zh/guides/write-experiment.mdx +23 -1
- package/docs-site/zh/guides/write-send.mdx +2 -2
- package/docs-site/zh/index.mdx +4 -3
- package/docs-site/zh/introduction.mdx +2 -1
- package/docs-site/zh/quickstart.mdx +5 -3
- package/docs-site/zh/reference/builtin-agents.mdx +38 -13
- package/docs-site/zh/reference/capabilities.mdx +1 -1
- package/docs-site/zh/reference/cli.mdx +36 -13
- package/docs-site/zh/reference/define-agent.mdx +24 -12
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +3 -3
- package/docs-site/zh/reference/events.mdx +7 -1
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/package.json +10 -6
- package/src/agents/bub.ts +134 -43
- package/src/agents/claude-code.test.ts +152 -0
- package/src/agents/claude-code.ts +127 -13
- package/src/agents/codex.test.ts +192 -0
- package/src/agents/codex.ts +151 -12
- package/src/agents/index.ts +11 -3
- package/src/agents/manifest.ts +41 -0
- package/src/agents/plugin-config.test.ts +94 -0
- package/src/agents/shared.ts +33 -2
- package/src/agents/skills.test.ts +214 -0
- package/src/agents/skills.ts +304 -0
- package/src/agents/streaming.ts +5 -5
- package/src/agents/types.ts +62 -2
- package/src/cli.ts +74 -47
- package/src/context/context.test.ts +20 -2
- package/src/context/context.ts +3 -0
- package/src/context/control-flow.ts +1 -1
- package/src/context/session.test.ts +92 -1
- package/src/context/session.ts +26 -1
- package/src/context/types.ts +4 -4
- package/src/define.test.ts +82 -0
- package/src/define.ts +72 -11
- package/src/expect/index.ts +5 -5
- package/src/i18n/en.ts +36 -15
- package/src/i18n/zh-CN.ts +39 -21
- package/src/index.ts +1 -1
- package/src/o11y/execution-tree.test.ts +452 -0
- package/src/o11y/execution-tree.ts +367 -0
- package/src/o11y/otlp/receiver.ts +1 -1
- package/src/o11y/otlp/select.ts +7 -3
- package/src/o11y/parsers/claude-code.test.ts +175 -0
- package/src/o11y/parsers/claude-code.ts +32 -0
- package/src/o11y/types.ts +6 -1
- package/src/report/aggregate.ts +26 -20
- package/src/report/built-in-user-parity.test.tsx +643 -0
- package/src/report/built-ins/cost-pass-rate-comparison.tsx +23 -0
- package/src/report/built-ins/index.ts +2 -0
- package/src/report/components.tsx +223 -50
- package/src/report/compute.ts +358 -53
- package/src/report/dual-render.test.tsx +1125 -0
- package/src/report/flag.ts +1 -1
- package/src/report/format.ts +50 -2
- package/src/report/index.ts +48 -14
- package/src/report/load.ts +1 -1
- package/src/report/locale.ts +206 -0
- package/src/report/metrics.ts +39 -15
- package/src/report/primitives.tsx +100 -2
- package/src/report/react/AttemptList.tsx +115 -0
- package/src/report/react/DeltaTable.tsx +9 -6
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentList.tsx +108 -0
- package/src/report/react/GroupSummary.tsx +66 -0
- package/src/report/react/MetricBars.tsx +24 -13
- package/src/report/react/MetricLine.tsx +21 -21
- package/src/report/react/MetricMatrix.tsx +8 -4
- package/src/report/react/MetricScatter.tsx +136 -96
- package/src/report/react/MetricTable.tsx +115 -15
- package/src/report/react/RunOverview.tsx +48 -26
- package/src/report/react/Scoreboard.tsx +15 -7
- package/src/report/react/cell.tsx +18 -9
- package/src/{view/app/lib/chart.ts → report/react/chart-math.ts} +2 -6
- package/src/report/react/colors.ts +24 -15
- package/src/report/react/enhance.js +129 -0
- package/src/report/react/fixtures.ts +163 -34
- package/src/report/react/format.ts +1 -1
- package/src/report/react/index.tsx +22 -6
- package/src/report/react/render.test.tsx +164 -31
- package/src/report/react/styles.css +405 -213
- package/src/report/report.test.ts +403 -94
- package/src/report/report.ts +29 -11
- package/src/report/text/faces.ts +303 -95
- package/src/report/text/layout.ts +76 -11
- package/src/report/text/table.ts +98 -0
- package/src/report/tree.ts +103 -20
- package/src/report/types.ts +196 -25
- package/src/report/web.ts +46 -19
- package/src/results/annotated-source.test.ts +146 -0
- package/src/results/annotated-source.ts +128 -0
- package/src/results/attempt-evidence.test.ts +158 -0
- package/src/results/attempt-evidence.ts +158 -0
- package/src/results/attempt-source.ts +48 -0
- package/src/results/copy.ts +154 -139
- package/src/results/format.ts +54 -47
- package/src/results/host-equivalence.test.ts +527 -0
- package/src/results/index.ts +56 -14
- package/src/results/locator.test.ts +198 -0
- package/src/results/locator.ts +0 -0
- package/src/results/open.ts +392 -175
- package/src/results/results.test.ts +943 -494
- package/src/results/select.ts +129 -23
- package/src/results/skipped-notice.ts +0 -0
- package/src/results/source-hash.ts +28 -0
- package/src/results/types.ts +98 -62
- package/src/results/writer.ts +304 -176
- package/src/runner/attempt.test.ts +170 -0
- package/src/runner/attempt.ts +97 -15
- package/src/runner/discover.test.ts +65 -0
- package/src/runner/discover.ts +6 -2
- package/src/runner/eval-source.test.ts +121 -0
- package/src/runner/eval-source.ts +45 -0
- package/src/runner/fingerprint.ts +54 -1
- package/src/runner/remote-sandbox.ts +1 -1
- package/src/runner/report.test.ts +6 -8
- package/src/runner/report.ts +5 -4
- package/src/runner/reporters/artifacts.ts +27 -41
- package/src/runner/reporters/braintrust.test.ts +3 -3
- package/src/runner/reporters/braintrust.ts +1 -1
- package/src/runner/reporters/console.ts +6 -6
- package/src/runner/reporters/json.ts +4 -4
- package/src/runner/reporters/live.test.ts +56 -0
- package/src/runner/reporters/live.ts +36 -10
- package/src/runner/reporters/quiet.test.ts +66 -0
- package/src/runner/reporters/quiet.ts +49 -0
- package/src/runner/reporters/shared.ts +5 -5
- package/src/runner/reporters/table.ts +17 -17
- package/src/runner/run.ts +61 -102
- package/src/runner/types.ts +43 -32
- package/src/sandbox/checkpoint.ts +1 -1
- package/src/sandbox/docker.ts +15 -2
- package/src/sandbox/e2b.ts +11 -5
- package/src/sandbox/errors.ts +13 -0
- package/src/sandbox/index.ts +3 -3
- package/src/sandbox/local-files.ts +1 -1
- package/src/sandbox/registry.ts +6 -5
- package/src/sandbox/resolve.ts +38 -23
- package/src/sandbox/retry.test.ts +68 -0
- package/src/sandbox/retry.ts +44 -0
- package/src/sandbox/types.ts +69 -28
- package/src/sandbox/vercel.ts +17 -3
- package/src/scoring/collector.ts +3 -3
- package/src/scoring/judge.ts +2 -2
- package/src/scoring/scoped.ts +13 -1
- package/src/scoring/types.ts +7 -7
- package/src/scoring/verdict.ts +4 -4
- package/src/shared/aggregate.ts +23 -3
- package/src/shared/types.ts +2 -2
- package/src/shared/{outcome.ts → verdict.ts} +15 -15
- package/src/show/compose.ts +12 -132
- package/src/show/index.ts +125 -94
- package/src/show/render.ts +660 -160
- package/src/show/show.test.ts +472 -253
- package/src/source-loc.ts +1 -1
- package/src/tty-line.ts +32 -0
- package/src/types.ts +2 -2
- package/src/util.test.ts +21 -1
- package/src/util.ts +5 -1
- package/src/view/app/App.tsx +33 -198
- package/src/view/app/components/AttemptModal.tsx +6 -6
- package/src/view/app/components/CodeView.tsx +2 -2
- package/src/view/app/components/CopyControls.tsx +32 -36
- package/src/view/app/components/SkippedRunsBanner.tsx +140 -0
- package/src/view/app/components/ui/badge.tsx +1 -1
- package/src/view/app/components/ui/dialog.tsx +2 -2
- package/src/view/app/i18n.ts +40 -31
- package/src/view/app/lib/artifact-url.ts +1 -1
- package/src/view/app/lib/attempt-route.test.ts +37 -47
- package/src/view/app/lib/attempt-route.ts +26 -26
- package/src/view/app/lib/rows.ts +9 -112
- package/src/view/app/lib/verdict.ts +25 -0
- package/src/view/app/main.tsx +9 -12
- package/src/view/app/pages/RunsPage.tsx +4 -4
- package/src/view/app/pages/TracesPage.tsx +3 -3
- package/src/view/app/shared.ts +0 -1
- package/src/view/app/types.ts +6 -51
- package/src/view/artifact-serving.test.ts +136 -0
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +21 -21
- package/src/view/data.test.ts +214 -143
- package/src/view/data.ts +166 -120
- package/src/view/index.ts +23 -11
- package/src/view/server.ts +52 -17
- package/src/view/shared/types.ts +22 -31
- package/src/view/styles.css +117 -327
- package/src/view/view-report.test.ts +146 -98
- package/docs-site/zh/guides/sandbox-backends.mdx +0 -75
- package/src/report/default-report.tsx +0 -222
- package/src/report/dual-face.test.tsx +0 -527
- package/src/report/react/CaseList.tsx +0 -70
- package/src/view/app/components/CostScoreChart.tsx +0 -147
- package/src/view/app/components/ExperimentTable.tsx +0 -284
- package/src/view/app/components/GroupSelector.tsx +0 -61
- package/src/view/app/components/primitives.tsx +0 -43
- package/src/view/app/lib/outcome.ts +0 -82
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// text 面的排版工具:显示宽度(CJK 记 2 列)、填充、折行、对齐列、字符条形。
|
|
2
2
|
// 全部纯函数、零依赖;niceeval show 的终端输出与 Row 分栏都建立在这几样上。
|
|
3
|
+
//
|
|
4
|
+
// 其中七个是公开面(`niceeval/report` 以 stringWidth / padEnd / padStart / wrapText /
|
|
5
|
+
// indent / bar / columns 的名字导出,见 ../index.ts):自定义组件的 text 面必须拿到与
|
|
6
|
+
// 官方组件同一把尺子,否则「用户组件与官方组件对等」是假的。它们的 TSDoc 就是公开文档,
|
|
7
|
+
// 改签名即破坏公开面。renderAlignedRows 不导出 —— 表格的能力由 <Table> 原语承担。
|
|
3
8
|
|
|
4
9
|
/** 终端显示宽度:CJK / 全角记 2 列,其余记 1。启发式覆盖常用区段,够对齐表格用。 */
|
|
5
10
|
export function charDisplayWidth(codePoint: number): number {
|
|
@@ -22,27 +27,55 @@ export function charDisplayWidth(codePoint: number): number {
|
|
|
22
27
|
return 1;
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
/**
|
|
31
|
+
* 一段文本在终端里占几列:CJK / 全角字符记 2 列,其余记 1 列。
|
|
32
|
+
*
|
|
33
|
+
* 对齐终端输出用它,不要用 `String.prototype.length`——`length` 数的是 UTF-16 码元,
|
|
34
|
+
* 一个汉字占 2 个显示列却只算 1 个码元,中文一进来列就错位。
|
|
35
|
+
*
|
|
36
|
+
* @param text 任意文本。
|
|
37
|
+
* @returns 显示列数。
|
|
38
|
+
*/
|
|
25
39
|
export function stringWidth(text: string): number {
|
|
26
40
|
let width = 0;
|
|
27
41
|
for (const ch of text) width += charDisplayWidth(ch.codePointAt(0)!);
|
|
28
42
|
return width;
|
|
29
43
|
}
|
|
30
44
|
|
|
31
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* 按显示宽度在右侧补空格(左对齐)。超宽不截断,如实溢出。
|
|
47
|
+
*
|
|
48
|
+
* `String.prototype.padEnd` 的显示宽度版:内容含中文时才对得齐。
|
|
49
|
+
*
|
|
50
|
+
* @param text 要补齐的文本。
|
|
51
|
+
* @param width 目标显示宽度。
|
|
52
|
+
* @returns 补齐后的文本。
|
|
53
|
+
*/
|
|
32
54
|
export function padDisplay(text: string, width: number): string {
|
|
33
55
|
const gap = width - stringWidth(text);
|
|
34
56
|
return gap > 0 ? text + " ".repeat(gap) : text;
|
|
35
57
|
}
|
|
36
58
|
|
|
37
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* 按显示宽度在左侧补空格(右对齐)。超宽不截断,如实溢出。
|
|
61
|
+
*
|
|
62
|
+
* 数字列靠它对齐小数点。
|
|
63
|
+
*
|
|
64
|
+
* @param text 要补齐的文本。
|
|
65
|
+
* @param width 目标显示宽度。
|
|
66
|
+
* @returns 补齐后的文本。
|
|
67
|
+
*/
|
|
38
68
|
export function padStartDisplay(text: string, width: number): string {
|
|
39
69
|
const gap = width - stringWidth(text);
|
|
40
70
|
return gap > 0 ? " ".repeat(gap) + text : text;
|
|
41
71
|
}
|
|
42
72
|
|
|
43
73
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
74
|
+
* 按显示宽度折行:优先在空格处断,单个词超宽(URL / 中文长句)按列宽硬断。
|
|
75
|
+
*
|
|
76
|
+
* @param text 要折行的文本(`\n` 分段)。
|
|
77
|
+
* @param width 每行的最大显示宽度。
|
|
78
|
+
* @returns 折好的行;至少一行(空串输入 → `[""]`)。
|
|
46
79
|
*/
|
|
47
80
|
export function wrapDisplay(text: string, width: number): string[] {
|
|
48
81
|
const max = Math.max(4, width);
|
|
@@ -80,7 +113,13 @@ export function wrapDisplay(text: string, width: number): string[] {
|
|
|
80
113
|
return lines.length > 0 ? lines : [""];
|
|
81
114
|
}
|
|
82
115
|
|
|
83
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* 给一整块文本的每行加同一段前缀(嵌套块缩进)。空行不加,不留尾随空格。
|
|
118
|
+
*
|
|
119
|
+
* @param block 多行文本。
|
|
120
|
+
* @param indent 每行的前缀,通常是若干空格。
|
|
121
|
+
* @returns 缩进后的文本块。
|
|
122
|
+
*/
|
|
84
123
|
export function indentBlock(block: string, indent: string): string {
|
|
85
124
|
return block
|
|
86
125
|
.split("\n")
|
|
@@ -88,8 +127,21 @@ export function indentBlock(block: string, indent: string): string {
|
|
|
88
127
|
.join("\n");
|
|
89
128
|
}
|
|
90
129
|
|
|
91
|
-
/**
|
|
92
|
-
export
|
|
130
|
+
/** 列对齐方向:`"left"` 是默认,`"right"` 按显示宽度右对齐(数字列用)。 */
|
|
131
|
+
export type ColumnAlign = "left" | "right";
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 对齐列渲染:每列宽 = 该列最宽格的显示宽度,列间 3 空格,行尾不留空白。首行是表头。
|
|
135
|
+
* `align[c]` 给第 c 列的对齐方向,缺省 `"left"`。
|
|
136
|
+
*
|
|
137
|
+
* 内部件:公开面是 <Table> 原语(见 ./table.ts 的 renderTableText,它先按 ctx.width
|
|
138
|
+
* 定好列宽再调这里)。不导出到 `niceeval/report` —— 两条并行的建表路径只会让作者选错。
|
|
139
|
+
*/
|
|
140
|
+
export function renderAlignedRows(
|
|
141
|
+
rows: string[][],
|
|
142
|
+
align: readonly ColumnAlign[] = [],
|
|
143
|
+
separator = " ",
|
|
144
|
+
): string {
|
|
93
145
|
const columnCount = Math.max(...rows.map((r) => r.length), 0);
|
|
94
146
|
const widths: number[] = [];
|
|
95
147
|
for (let c = 0; c < columnCount; c++) {
|
|
@@ -98,21 +150,34 @@ export function renderAlignedRows(rows: string[][]): string {
|
|
|
98
150
|
return rows
|
|
99
151
|
.map((row) =>
|
|
100
152
|
row
|
|
101
|
-
.map((cell, c) => (c ===
|
|
102
|
-
.join(
|
|
153
|
+
.map((cell, c) => (align[c] === "right" ? padStartDisplay(cell, widths[c]) : padDisplay(cell, widths[c])))
|
|
154
|
+
.join(separator)
|
|
103
155
|
.replace(/\s+$/, ""),
|
|
104
156
|
)
|
|
105
157
|
.join("\n");
|
|
106
158
|
}
|
|
107
159
|
|
|
108
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* 字符条形:比例 → `█` 填充、`░` 补齐到 `barWidth` 显示列。比例超出 [0, 1] 时钳住。
|
|
162
|
+
*
|
|
163
|
+
* @param ratio 填充比例,0 到 1。
|
|
164
|
+
* @param barWidth 整条的显示宽度。
|
|
165
|
+
* @returns 定宽的字符条。
|
|
166
|
+
*/
|
|
109
167
|
export function textBar(ratio: number, barWidth: number): string {
|
|
110
168
|
const clamped = Math.max(0, Math.min(1, ratio));
|
|
111
169
|
const filled = Math.round(clamped * barWidth);
|
|
112
170
|
return "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
113
171
|
}
|
|
114
172
|
|
|
115
|
-
/**
|
|
173
|
+
/**
|
|
174
|
+
* 多块文本并排成栏(`Row` 的 text 面):逐行拼接,各栏按显示宽度补齐,短栏补空行。
|
|
175
|
+
*
|
|
176
|
+
* @param blocks 每栏一块多行文本。
|
|
177
|
+
* @param columnWidths 每栏的显示宽度,与 `blocks` 一一对应。
|
|
178
|
+
* @param separator 栏间分隔符,默认 `" │ "`。
|
|
179
|
+
* @returns 并排后的文本块。
|
|
180
|
+
*/
|
|
116
181
|
export function joinColumns(blocks: string[], columnWidths: number[], separator = " │ "): string {
|
|
117
182
|
const columns = blocks.map((block) => block.split("\n"));
|
|
118
183
|
const height = Math.max(...columns.map((lines) => lines.length), 0);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// <Table> 原语的 text 面(../primitives.tsx 的 Table.text 就是它)。
|
|
2
|
+
// 官方的表状组件(MetricTable / MetricMatrix / Scoreboard / DeltaTable)的 text 面也直接
|
|
3
|
+
// 调它 —— 官方与自定义表共用同一个渲染器,是「用户组件与官方组件对等」的构造证明:
|
|
4
|
+
// 官方用不上的能力用户就拿不到,官方绕过它手搓它就一定会长歪。
|
|
5
|
+
//
|
|
6
|
+
// 列宽按显示宽度算(CJK 记 2 列);null 渲染 —,不补 0;超宽先折最宽的左对齐列,
|
|
7
|
+
// 压到下限仍放不下就从右侧丢列并如实报数(「截断报剩余」是既有契约,不在这里破例)。
|
|
8
|
+
|
|
9
|
+
import type { TableProps, TableRow } from "../primitives.tsx";
|
|
10
|
+
import type { TextContext } from "../tree.ts";
|
|
11
|
+
import { countText, localeText } from "../locale.ts";
|
|
12
|
+
import { renderAlignedRows, stringWidth, wrapDisplay, type ColumnAlign } from "./layout.ts";
|
|
13
|
+
|
|
14
|
+
const MISSING_MARK = "—";
|
|
15
|
+
/** 列间距,与 renderAlignedRows 的 join(" ") 一致。 */
|
|
16
|
+
const COLUMN_GAP = 3;
|
|
17
|
+
/** 左对齐列的压缩下限:再窄就读不成句,宁可丢列。 */
|
|
18
|
+
const MIN_TEXT_COLUMN = 8;
|
|
19
|
+
|
|
20
|
+
/** 格子的文本形态:缺这个键、或值是 null,都渲染 —(与 web 面同源)。 */
|
|
21
|
+
function cellText(row: TableRow, key: string): string {
|
|
22
|
+
const value = row.cells[key];
|
|
23
|
+
return value === null || value === undefined ? MISSING_MARK : value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function totalWidth(widths: readonly number[]): number {
|
|
27
|
+
return widths.reduce((sum, w) => sum + w, 0) + COLUMN_GAP * Math.max(0, widths.length - 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 自然列宽 → 放得进 available 的列宽。两步,顺序即优先级:
|
|
32
|
+
* 1. 压最宽的左对齐列(文本列)到下限 —— 右对齐列是数字,折行读不了,不压;
|
|
33
|
+
* 2. 仍放不下就从右侧丢列(至少留一列),丢了几列如实返回。
|
|
34
|
+
*/
|
|
35
|
+
function fitWidths(
|
|
36
|
+
natural: readonly number[],
|
|
37
|
+
align: readonly ColumnAlign[],
|
|
38
|
+
available: number,
|
|
39
|
+
): { widths: number[]; hidden: number } {
|
|
40
|
+
const widths = [...natural];
|
|
41
|
+
while (totalWidth(widths) > available) {
|
|
42
|
+
let widest = -1;
|
|
43
|
+
for (let c = 0; c < widths.length; c++) {
|
|
44
|
+
if (align[c] === "right" || widths[c] <= MIN_TEXT_COLUMN) continue;
|
|
45
|
+
if (widest === -1 || widths[c] > widths[widest]) widest = c;
|
|
46
|
+
}
|
|
47
|
+
if (widest === -1) break;
|
|
48
|
+
const over = totalWidth(widths) - available;
|
|
49
|
+
widths[widest] = Math.max(MIN_TEXT_COLUMN, widths[widest] - over);
|
|
50
|
+
}
|
|
51
|
+
let hidden = 0;
|
|
52
|
+
while (widths.length > 1 && totalWidth(widths) > available) {
|
|
53
|
+
widths.pop();
|
|
54
|
+
hidden += 1;
|
|
55
|
+
}
|
|
56
|
+
return { widths, hidden };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 逻辑行 → 物理行:每格折到自己的列宽,行高取最高的那格,矮格补空串。 */
|
|
60
|
+
function toPhysicalRows(cells: readonly string[], widths: readonly number[]): string[][] {
|
|
61
|
+
const wrapped = cells.map((cell, c) =>
|
|
62
|
+
// 放得下就原样保留:wrapDisplay 会把连续空格折成一个,而 DeltaTable 的
|
|
63
|
+
// "50% → 62% +12pp" 这类格子内含固定间距 —— 不折行时一个字节都不能动。
|
|
64
|
+
!cell.includes("\n") && stringWidth(cell) <= widths[c] ? [cell] : wrapDisplay(cell, widths[c]),
|
|
65
|
+
);
|
|
66
|
+
const height = Math.max(...wrapped.map((lines) => lines.length), 1);
|
|
67
|
+
const out: string[][] = [];
|
|
68
|
+
for (let i = 0; i < height; i++) out.push(wrapped.map((lines) => lines[i] ?? ""));
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* <Table> 的 text 面:columns × rows → 对齐的字符表。
|
|
74
|
+
* 有任一行带 locator 时追加一列 attempt(locator 本身就是 `niceeval show <locator>`
|
|
75
|
+
* 的位置参数;逐行重复整条命令会把表撑宽,与三个实体列表 text 面的既有取舍一致)。
|
|
76
|
+
*/
|
|
77
|
+
export function renderTableText(props: TableProps, ctx: TextContext): string {
|
|
78
|
+
const locale = props.locale ?? ctx.locale;
|
|
79
|
+
const hasLocator = props.rows.some((row) => row.locator !== undefined);
|
|
80
|
+
if (props.columns.length === 0 && !hasLocator) return "";
|
|
81
|
+
|
|
82
|
+
const header = props.columns.map((column) => column.header);
|
|
83
|
+
const align: ColumnAlign[] = props.columns.map((column) => column.align ?? "left");
|
|
84
|
+
const body = props.rows.map((row) => props.columns.map((column) => cellText(row, column.key)));
|
|
85
|
+
if (hasLocator) {
|
|
86
|
+
header.push(localeText(locale, "table.attempt"));
|
|
87
|
+
align.push("left");
|
|
88
|
+
props.rows.forEach((row, i) => body[i].push(row.locator ?? MISSING_MARK));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const matrix = [header, ...body];
|
|
92
|
+
const natural = header.map((_, c) => Math.max(...matrix.map((row) => stringWidth(row[c] ?? ""))));
|
|
93
|
+
const { widths, hidden } = fitWidths(natural, align, ctx.width);
|
|
94
|
+
|
|
95
|
+
const physical = matrix.flatMap((row) => toPhysicalRows(row.slice(0, widths.length), widths));
|
|
96
|
+
const table = renderAlignedRows(physical, align);
|
|
97
|
+
return hidden > 0 ? `${table}\n${countText(locale, "table.columnsHidden", hidden)}` : table;
|
|
98
|
+
}
|
package/src/report/tree.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
// 报告的元素树与双面组件基座(docs/reports.md
|
|
1
|
+
// 报告的元素树与双面组件基座(docs/feature/reports/architecture.md「报告树与两个宿主」)。
|
|
2
2
|
//
|
|
3
3
|
// 报告函数返回的树不是「React 树」,只是 { type, props } 节点 —— 标准 react
|
|
4
4
|
// jsx-runtime 产的元素恰好就是这个形状。本文件是基础实现:零 react 运行时依赖
|
|
5
5
|
// (只有类型层的 `import type`,编译后擦除);text 宿主遍历渲染不需要 react-dom,
|
|
6
6
|
// web 宿主(web.ts)才真正 import react。渲染面是纯同步函数:零 IO、零 await ——
|
|
7
|
-
// 计算全部发生在报告函数体里,可达百 MB
|
|
7
|
+
// 计算全部发生在报告函数体里,可达百 MB 的 artifact 永远不进渲染路径。
|
|
8
8
|
|
|
9
9
|
import type { ReactNode } from "react";
|
|
10
|
-
import type {
|
|
10
|
+
import type { AttemptLocator } from "../results/locator.ts";
|
|
11
|
+
import { DEFAULT_REPORT_LOCALE, type ReportLocale } from "./locale.ts";
|
|
11
12
|
|
|
12
13
|
// ───────────────────────── 节点形状 ─────────────────────────
|
|
13
14
|
|
|
@@ -43,29 +44,42 @@ export const COMPONENT_FACES: unique symbol = Symbol.for("niceeval.report.faces"
|
|
|
43
44
|
export interface TextContext {
|
|
44
45
|
/** 可用列宽;Row 分栏后变窄。 */
|
|
45
46
|
width: number;
|
|
47
|
+
/** chrome 文案的 locale(verdict 词、注脚、占位符);默认 "en",show 输出不变。 */
|
|
48
|
+
locale: ReportLocale;
|
|
46
49
|
/** 容器组件渲染 children 用,宽度显式传递。 */
|
|
47
50
|
render(node: ReportNode, width?: number): string;
|
|
48
|
-
/**
|
|
49
|
-
attemptCommand(
|
|
51
|
+
/** 下钻命令,通证据室:`niceeval show @<locator>`。 */
|
|
52
|
+
attemptCommand(locator: AttemptLocator): string;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
export interface WebContext {
|
|
53
|
-
/** 证据室深链,同 view 的 attempt
|
|
54
|
-
attemptHref(
|
|
56
|
+
/** 证据室深链,同 view 的 attempt 路由(`#/attempt/@<locator>`,单段、不透明)。 */
|
|
57
|
+
attemptHref(locator: AttemptLocator): string;
|
|
58
|
+
/** chrome 文案的 locale;官方组件渲染面经上下文读取,宿主外默认 "en"。 */
|
|
59
|
+
locale: ReportLocale;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
export interface ComponentFaces<P> {
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
export interface ComponentFaces<P, R = P> {
|
|
63
|
+
/**
|
|
64
|
+
* 可选的数据解析面:把声明式 props(selection + 计算选项)在渲染前解析成纯数据 props(R)。
|
|
65
|
+
* 宿主的 {@link resolveReportTree} 在 build 之后、validate / render 之前调用它——这是整条
|
|
66
|
+
* 管线里唯一允许 await / IO 的组件级步骤;渲染面(web / text)只看已解析的 R,保持同步、零 IO。
|
|
67
|
+
* 不实现 resolve 时 R = P,组件被当作纯数据组件直接渲染。
|
|
68
|
+
*/
|
|
69
|
+
resolve?(props: P): Promise<R>;
|
|
70
|
+
/** 真 React JSX 在这个面里;返回静态可渲染的 ReactNode。只看已解析的 R。 */
|
|
71
|
+
web(props: R, ctx: WebContext): ReactNode;
|
|
72
|
+
text(props: R, ctx: TextContext): string;
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
/**
|
|
64
76
|
* 双面组件的产物:可直接用于 JSX(React 把它当函数组件调用,走 web 面),
|
|
65
|
-
* text 宿主经 COMPONENT_FACES 调 text 面。
|
|
77
|
+
* text 宿主经 COMPONENT_FACES 调 text 面。R(解析后 props 形态)在存储边界抹成 any——
|
|
78
|
+
* 树遍历只按 ComponentFaces 的结构调用 resolve / web / text,不需要在类型层追踪 R。
|
|
66
79
|
*/
|
|
67
80
|
export type ReportComponent<P> = ((props: P) => ReactNode) & {
|
|
68
|
-
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
82
|
+
[COMPONENT_FACES]: ComponentFaces<P, any>;
|
|
69
83
|
displayName?: string;
|
|
70
84
|
};
|
|
71
85
|
|
|
@@ -73,8 +87,13 @@ export type ReportComponent<P> = ((props: P) => ReactNode) & {
|
|
|
73
87
|
// 用默认值 —— attemptHref 默认 view 的 attempt 路由格式(自定义组件显式调 ctx.attemptHref
|
|
74
88
|
// 时总有去处);官方组件的「宿主里自动接证据室」只在宿主上下文激活时发生,
|
|
75
89
|
// 宿主外不传 attemptHref 就是纯展示,不发明断链。
|
|
90
|
+
//
|
|
91
|
+
// URL 格式取 `#/attempt/${locator}`:AttemptLocator 本身已经是 `@` 前缀的不透明短串
|
|
92
|
+
// (如 "@1x7f3q9"),原样嵌进路径段就得到 `#/attempt/@1x7f3q9`——与 docs/feature/reports/view.md「用
|
|
93
|
+
// Reports 积木重建 view」定稿的单段路由逐字一致,不额外拆分或去掉 `@`。
|
|
76
94
|
const DEFAULT_WEB_CONTEXT: WebContext = {
|
|
77
|
-
attemptHref: (
|
|
95
|
+
attemptHref: (locator) => `#/attempt/${locator}`,
|
|
96
|
+
locale: DEFAULT_REPORT_LOCALE,
|
|
78
97
|
};
|
|
79
98
|
let activeWebContext: WebContext | null = null;
|
|
80
99
|
|
|
@@ -98,14 +117,18 @@ export function isHostWebContextActive(): boolean {
|
|
|
98
117
|
* 定义一个双面组件:faces 两键必填 —— 少实现一个面编译不过,配对是结构义务。
|
|
99
118
|
* 基础实现不 import react;产物以可调用组件的形状兼容 React 渲染。
|
|
100
119
|
*/
|
|
101
|
-
export function defineComponent<P>(faces: ComponentFaces<P>): ReportComponent<P> {
|
|
120
|
+
export function defineComponent<P, R = P>(faces: ComponentFaces<P, R>): ReportComponent<P> {
|
|
102
121
|
if (typeof faces?.web !== "function" || typeof faces?.text !== "function") {
|
|
103
122
|
throw new Error(
|
|
104
123
|
"defineComponent requires both faces: { web(props, ctx), text(props, ctx) }. " +
|
|
105
124
|
"Every report component must render in both hosts (niceeval view and niceeval show).",
|
|
106
125
|
);
|
|
107
126
|
}
|
|
108
|
-
|
|
127
|
+
// 直接调用路径:把组件当普通 React 组件嵌进用户自己的页面时走这里,web 面只接收数据形态
|
|
128
|
+
// props(R)。带 resolve 的组件若拿 selection 形态 props 走这条裸路径,web 面会缺 data ——
|
|
129
|
+
// 这类组件只有经宿主的 resolveReportTree 解析后才安全渲染;纯数据 props 一直可以裸嵌。
|
|
130
|
+
const component = ((props: P) =>
|
|
131
|
+
faces.web(props as unknown as R, activeWebContext ?? DEFAULT_WEB_CONTEXT)) as ReportComponent<P>;
|
|
109
132
|
component[COMPONENT_FACES] = faces;
|
|
110
133
|
return component;
|
|
111
134
|
}
|
|
@@ -115,6 +138,60 @@ export function facesOf(type: unknown): ComponentFaces<unknown> | undefined {
|
|
|
115
138
|
return (type as Partial<ReportComponent<unknown>>)[COMPONENT_FACES] as ComponentFaces<unknown> | undefined;
|
|
116
139
|
}
|
|
117
140
|
|
|
141
|
+
// ───────────────────────── 数据解析(渲染前唯一的 await 边界)─────────────────────────
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 报告 build 之后、树校验与 text/web 渲染之前的解析遍历:把声明式数据组件(实现了
|
|
145
|
+
* `faces.resolve` 的双面组件,如 selection 形态的 MetricScatter)的 props
|
|
146
|
+
* 就地换成算好的数据形态 props(三个实体列表没有 `resolve` 面,不经这一步——它们的 `items`
|
|
147
|
+
* 由报告作者在 `build()` 里直接 `await .data(selection)` 备好)。计算发生在这里(唯一允许
|
|
148
|
+
* await 的组件级步骤,连同报告函数体自己的 `build()`),两个渲染面
|
|
149
|
+
* 之后都只看已解析的树、保持同步零 IO。遍历形状与 {@link validateReportTree} /
|
|
150
|
+
* {@link renderNodeToText} 一致:
|
|
151
|
+
*
|
|
152
|
+
* - 同层数组兄弟并行解析(`Promise.all`),保持原始顺序 / keys;
|
|
153
|
+
* - 双面组件带 resolve 的:调 `faces.resolve(props)` 换 props(有 children 再递归解析);
|
|
154
|
+
* - 双面组件无 resolve 的(Row / Col / Section / RunOverview…):只递归 children,自身 props
|
|
155
|
+
* 原样保留(title / className 等不能动);
|
|
156
|
+
* - 普通函数组件:同步调用展开,展开结果继续解析(与 validate / render 对函数组件的处理一致);
|
|
157
|
+
* - 字符串 intrinsic(<div>):原样返回,交给随后的 validateReportTree 报同一条错误。
|
|
158
|
+
*
|
|
159
|
+
* resolver 跑完后,树里已没有函数组件 / 未解析 props;validate / render 里对这两者的分支在
|
|
160
|
+
* 正常管线下是 no-op,但保留着——手搭树直接调 validate / render(不过 resolver)的低层用法仍需要。
|
|
161
|
+
*/
|
|
162
|
+
export async function resolveReportTree(node: ReportNode): Promise<ReportNode> {
|
|
163
|
+
if (node === null || node === undefined || typeof node === "boolean") return node;
|
|
164
|
+
if (typeof node === "string" || typeof node === "number") return node;
|
|
165
|
+
if (Array.isArray(node)) return Promise.all(node.map(resolveReportTree));
|
|
166
|
+
if (!isReportElement(node)) return node;
|
|
167
|
+
const { type, props } = node;
|
|
168
|
+
// 字符串 intrinsic:不在这里报错,让 resolve 后的 validateReportTree 抛统一的那条。
|
|
169
|
+
if (typeof type === "string") return node;
|
|
170
|
+
if (type === REACT_FRAGMENT) {
|
|
171
|
+
const children = await resolveReportTree(props.children as ReportNode);
|
|
172
|
+
return { ...node, props: { ...props, children } };
|
|
173
|
+
}
|
|
174
|
+
const faces = facesOf(type);
|
|
175
|
+
if (faces) {
|
|
176
|
+
if (typeof faces.resolve === "function") {
|
|
177
|
+
const resolvedProps = { ...((await faces.resolve(props)) as Record<string, unknown>) };
|
|
178
|
+
if (resolvedProps.children !== undefined) {
|
|
179
|
+
resolvedProps.children = await resolveReportTree(resolvedProps.children as ReportNode);
|
|
180
|
+
}
|
|
181
|
+
return { ...node, props: resolvedProps };
|
|
182
|
+
}
|
|
183
|
+
// 无 resolve 的容器 / 数据组件:只解析 children,自身 props 原样保留。
|
|
184
|
+
const children = await resolveReportTree(props.children as ReportNode);
|
|
185
|
+
return { ...node, props: { ...props, children } };
|
|
186
|
+
}
|
|
187
|
+
if (typeof type === "function") {
|
|
188
|
+
// 普通函数组件:调用展开,展开结果整体替换本节点后继续解析。
|
|
189
|
+
const expanded = (type as (p: unknown) => ReportNode)(props);
|
|
190
|
+
return resolveReportTree(expanded);
|
|
191
|
+
}
|
|
192
|
+
return node;
|
|
193
|
+
}
|
|
194
|
+
|
|
118
195
|
// ───────────────────────── 树校验 ─────────────────────────
|
|
119
196
|
|
|
120
197
|
function componentLabel(type: unknown): string {
|
|
@@ -174,16 +251,22 @@ export function validateReportTree(node: ReportNode, path: string[] = []): void
|
|
|
174
251
|
export interface TextRenderOptions {
|
|
175
252
|
/** 终端可用列宽;默认 80。 */
|
|
176
253
|
width?: number;
|
|
177
|
-
/**
|
|
178
|
-
attemptCommand?: (
|
|
254
|
+
/** 下钻命令的生成;宿主注入,默认 `niceeval show @<locator>`(真实可跑的 CLI 语法)。 */
|
|
255
|
+
attemptCommand?: (locator: AttemptLocator) => string;
|
|
256
|
+
/** chrome 文案的 locale;默认 "en"(`niceeval show` 现有输出不变)。 */
|
|
257
|
+
locale?: ReportLocale;
|
|
179
258
|
}
|
|
180
259
|
|
|
181
260
|
export function createTextContext(options?: TextRenderOptions): TextContext {
|
|
182
261
|
const width = Math.max(20, options?.width ?? 80);
|
|
183
|
-
const
|
|
184
|
-
|
|
262
|
+
const locale = options?.locale ?? DEFAULT_REPORT_LOCALE;
|
|
263
|
+
// 默认下钻命令:AttemptLocator 是 `@` 前缀的不透明短串,`niceeval show @<locator>` 是
|
|
264
|
+
// show/index.ts 已实现的真实 CLI 语法(见该文件 `@<locator>` 位置参数解析),不需要
|
|
265
|
+
// 反查 eval id 再拼一条近似命令。
|
|
266
|
+
const attemptCommand = options?.attemptCommand ?? ((locator: AttemptLocator) => `niceeval show ${locator}`);
|
|
185
267
|
const make = (w: number): TextContext => ({
|
|
186
268
|
width: w,
|
|
269
|
+
locale,
|
|
187
270
|
attemptCommand,
|
|
188
271
|
render(node, childWidth) {
|
|
189
272
|
return renderNodeToText(node, childWidth === undefined ? this : make(Math.max(10, childWidth)));
|