@reicek/neataptic-ts 0.1.25 → 0.1.26
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/.github/copilot-instructions.md +11 -0
- package/.github/skills/trace-analyzer-extension/SKILL.md +3 -3
- package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +1 -1
- package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +1 -1
- package/.github/skills/trace-audit-reporting/SKILL.md +3 -3
- package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +1 -1
- package/package.json +19 -13
- package/plans/Flappy_Bird_Folder_Documentation_Pass.md +4 -4
- package/plans/README.md +24 -0
- package/plans/Roadmap.md +62 -40
- package/plans/analyze-trace-solid-split.plans.md +66 -0
- package/plans/architecture-solid-split.plans.md +9 -15
- package/plans/asciiMaze-typescript-repair.plans.md +1 -1
- package/plans/generate-docs-solid-split.plans.md +87 -0
- package/plans/methods-docs.plans.md +25 -1
- package/plans/methods-solid-split.plans.md +14 -14
- package/plans/neat-docs.plans.md +9 -1
- package/plans/neat-test-surface-repair.plans.md +1 -1
- package/plans/render-docs-html-solid-split.plans.md +68 -0
- package/plans/src-no-explicit-any-cleanup.plans.md +1 -1
- package/plans/utils-docs.plans.md +6 -1
- package/scripts/analyze-trace/analyze-trace.analysis.ts +479 -0
- package/scripts/analyze-trace/analyze-trace.constants.ts +35 -0
- package/scripts/analyze-trace/analyze-trace.io.ts +69 -0
- package/scripts/analyze-trace/analyze-trace.report.ts +100 -0
- package/scripts/analyze-trace/analyze-trace.shared.ts +116 -0
- package/scripts/analyze-trace/analyze-trace.ts +45 -0
- package/scripts/analyze-trace/analyze-trace.types.ts +72 -0
- package/scripts/assets/theme.css +80 -23
- package/scripts/copy-examples.ts +239 -0
- package/scripts/export-onnx.ts +223 -0
- package/scripts/generate-bench-tables.ts +378 -37
- package/scripts/generate-docs/generate-docs.constants.ts +107 -0
- package/scripts/generate-docs/generate-docs.order.ts +355 -0
- package/scripts/generate-docs/generate-docs.state.ts +31 -0
- package/scripts/generate-docs/generate-docs.targets.ts +165 -0
- package/scripts/generate-docs/generate-docs.ts +63 -0
- package/scripts/generate-docs/generate-docs.types.ts +112 -0
- package/scripts/generate-docs/output/generate-docs.output.folder-index.utils.ts +167 -0
- package/scripts/generate-docs/output/generate-docs.output.ordering.utils.ts +353 -0
- package/scripts/generate-docs/output/generate-docs.output.readme.utils.ts +420 -0
- package/scripts/generate-docs/output/generate-docs.output.ts +123 -0
- package/scripts/generate-docs/output/generate-docs.output.warnings.utils.ts +219 -0
- package/scripts/generate-docs/symbols/generate-docs.symbols.collection.utils.ts +365 -0
- package/scripts/generate-docs/symbols/generate-docs.symbols.jsdoc.utils.ts +373 -0
- package/scripts/generate-docs/symbols/generate-docs.symbols.normalize.utils.ts +155 -0
- package/scripts/generate-docs/symbols/generate-docs.symbols.render.utils.ts +149 -0
- package/scripts/generate-docs/symbols/generate-docs.symbols.signature.utils.ts +289 -0
- package/scripts/generate-docs/symbols/generate-docs.symbols.ts +11 -0
- package/scripts/mermaid-cli.mjs +102 -22
- package/scripts/mermaid-cli.ts +736 -0
- package/scripts/render-docs-html/render-docs-html.assets.ts +54 -0
- package/scripts/render-docs-html/render-docs-html.mermaid.ts +245 -0
- package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts} +141 -144
- package/scripts/render-docs-html/render-docs-html.pages.ts +333 -0
- package/scripts/render-docs-html/render-docs-html.shared.ts +333 -0
- package/scripts/render-docs-html/render-docs-html.types.ts +42 -0
- package/scripts/render-docs-html.ts +23 -587
- package/scripts/run-docs.ts +238 -0
- package/scripts/write-dist-docs-pkg.ts +40 -0
- package/src/README.md +75 -75
- package/src/architecture/connection/README.md +5 -5
- package/src/architecture/layer/README.md +508 -508
- package/src/architecture/network/README.md +1458 -1458
- package/src/architecture/network/activate/README.md +694 -694
- package/src/architecture/network/bootstrap/README.md +77 -77
- package/src/architecture/network/connect/README.md +74 -74
- package/src/architecture/network/deterministic/README.md +135 -135
- package/src/architecture/network/evolve/README.md +364 -364
- package/src/architecture/network/gating/README.md +130 -130
- package/src/architecture/network/genetic/README.md +399 -399
- package/src/architecture/network/mutate/README.md +897 -897
- package/src/architecture/network/onnx/README.md +720 -720
- package/src/architecture/network/onnx/export/README.md +728 -728
- package/src/architecture/network/onnx/export/layers/README.md +450 -450
- package/src/architecture/network/onnx/import/README.md +618 -618
- package/src/architecture/network/onnx/schema/README.md +32 -32
- package/src/architecture/network/prune/README.md +245 -245
- package/src/architecture/network/remove/README.md +135 -135
- package/src/architecture/network/runtime/README.md +106 -106
- package/src/architecture/network/serialize/README.md +542 -542
- package/src/architecture/network/slab/README.md +608 -608
- package/src/architecture/network/standalone/README.md +212 -212
- package/src/architecture/network/stats/README.md +84 -84
- package/src/architecture/network/topology/README.md +465 -465
- package/src/architecture/network/training/README.md +200 -200
- package/src/architecture/node/README.md +5 -5
- package/src/architecture/nodePool/README.md +14 -14
- package/src/methods/README.md +99 -99
- package/src/methods/activation/README.md +189 -189
- package/src/methods/cost/README.md +131 -131
- package/src/methods/rate/README.md +86 -86
- package/src/multithreading/README.md +77 -77
- package/src/multithreading/workers/browser/README.md +8 -8
- package/src/multithreading/workers/node/README.md +8 -8
- package/src/neat/README.md +148 -148
- package/src/neat/adaptive/README.md +120 -120
- package/src/neat/adaptive/acceptance/README.md +40 -40
- package/src/neat/adaptive/complexity/README.md +137 -137
- package/src/neat/adaptive/core/README.md +197 -197
- package/src/neat/adaptive/lineage/README.md +90 -90
- package/src/neat/adaptive/mutation/README.md +284 -284
- package/src/neat/compat/README.md +43 -43
- package/src/neat/compat/core/README.md +90 -90
- package/src/neat/diversity/README.md +35 -35
- package/src/neat/diversity/core/README.md +88 -88
- package/src/neat/evaluate/README.md +85 -85
- package/src/neat/evaluate/auto-distance/README.md +75 -75
- package/src/neat/evaluate/entropy-compat/README.md +37 -37
- package/src/neat/evaluate/entropy-sharing/README.md +43 -43
- package/src/neat/evaluate/fitness/README.md +23 -23
- package/src/neat/evaluate/novelty/README.md +120 -120
- package/src/neat/evaluate/objectives/README.md +17 -17
- package/src/neat/evaluate/shared/README.md +94 -94
- package/src/neat/evolve/README.md +96 -96
- package/src/neat/evolve/adaptive/README.md +60 -60
- package/src/neat/evolve/objectives/README.md +63 -63
- package/src/neat/evolve/offspring/README.md +56 -56
- package/src/neat/evolve/population/README.md +171 -171
- package/src/neat/evolve/runtime/README.md +79 -79
- package/src/neat/evolve/speciation/README.md +74 -74
- package/src/neat/evolve/warnings/README.md +10 -10
- package/src/neat/export/README.md +114 -114
- package/src/neat/helpers/README.md +50 -50
- package/src/neat/init/README.md +9 -9
- package/src/neat/lineage/core/README.md +101 -101
- package/src/neat/multiobjective/category/README.md +74 -74
- package/src/neat/multiobjective/crowding/README.md +272 -272
- package/src/neat/multiobjective/dominance/README.md +171 -171
- package/src/neat/multiobjective/fronts/README.md +68 -68
- package/src/neat/multiobjective/metrics/README.md +43 -43
- package/src/neat/multiobjective/objectives/README.md +31 -31
- package/src/neat/multiobjective/shared/README.md +27 -27
- package/src/neat/mutation/README.md +97 -97
- package/src/neat/mutation/add-conn/README.md +115 -115
- package/src/neat/mutation/add-node/README.md +126 -126
- package/src/neat/mutation/flow/README.md +149 -149
- package/src/neat/mutation/repair/README.md +185 -185
- package/src/neat/mutation/select/README.md +117 -117
- package/src/neat/mutation/shared/README.md +32 -32
- package/src/neat/objectives/README.md +25 -25
- package/src/neat/objectives/core/README.md +67 -67
- package/src/neat/pruning/README.md +40 -40
- package/src/neat/pruning/core/README.md +171 -171
- package/src/neat/pruning/facade/README.md +32 -32
- package/src/neat/rng/README.md +104 -104
- package/src/neat/rng/core/README.md +137 -137
- package/src/neat/rng/facade/README.md +50 -50
- package/src/neat/selection/README.md +111 -111
- package/src/neat/selection/core/README.md +227 -227
- package/src/neat/selection/facade/README.md +61 -61
- package/src/neat/shared/README.md +163 -163
- package/src/neat/speciation/README.md +31 -31
- package/src/neat/speciation/threshold/README.md +35 -35
- package/src/neat/species/README.md +25 -25
- package/src/neat/species/core/README.md +20 -20
- package/src/neat/species/core/shared/README.md +18 -18
- package/src/neat/species/history/context/README.md +22 -22
- package/src/neat/telemetry/accessors/README.md +58 -58
- package/src/neat/telemetry/exports/README.md +233 -233
- package/src/neat/telemetry/facade/README.md +252 -252
- package/src/neat/telemetry/facade/archive/README.md +57 -57
- package/src/neat/telemetry/facade/buffer/README.md +43 -43
- package/src/neat/telemetry/facade/lineage/README.md +12 -12
- package/src/neat/telemetry/facade/objectives/README.md +44 -44
- package/src/neat/telemetry/facade/runtime/README.md +26 -26
- package/src/neat/telemetry/facade/species/README.md +27 -27
- package/src/neat/telemetry/metrics/README.md +696 -696
- package/src/neat/telemetry/recorder/README.md +57 -57
- package/src/neat/telemetry/types/README.md +32 -32
- package/src/neat/topology-intent/README.md +75 -75
- package/src/utils/README.md +193 -193
- package/test/examples/asciiMaze/browser-entry/README.md +92 -92
- package/test/examples/asciiMaze/dashboardManager/README.md +109 -109
- package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +28 -28
- package/test/examples/asciiMaze/evolutionEngine/README.md +1527 -1527
- package/test/examples/asciiMaze/mazeMovement/README.md +105 -105
- package/test/examples/asciiMaze/mazeMovement/finalization/README.md +16 -16
- package/test/examples/asciiMaze/mazeMovement/policy/README.md +57 -57
- package/test/examples/asciiMaze/mazeMovement/runtime/README.md +52 -52
- package/test/examples/asciiMaze/mazeMovement/shaping/README.md +46 -46
- package/test/examples/flappy_bird/browser-entry/README.md +508 -508
- package/test/examples/flappy_bird/browser-entry/host/README.md +101 -101
- package/test/examples/flappy_bird/browser-entry/host/resize/README.md +144 -144
- package/test/examples/flappy_bird/browser-entry/network-view/README.md +194 -194
- package/test/examples/flappy_bird/browser-entry/playback/README.md +278 -278
- package/test/examples/flappy_bird/browser-entry/playback/background/README.md +129 -129
- package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +502 -502
- package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +139 -139
- package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +10 -10
- package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +43 -43
- package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +30 -30
- package/test/examples/flappy_bird/browser-entry/runtime/README.md +59 -59
- package/test/examples/flappy_bird/browser-entry/visualization/README.md +276 -276
- package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +16 -16
- package/test/examples/flappy_bird/constants/README.md +1070 -1070
- package/test/examples/flappy_bird/environment/README.md +22 -22
- package/test/examples/flappy_bird/evaluation/README.md +32 -32
- package/test/examples/flappy_bird/evaluation/rollout/README.md +141 -141
- package/test/examples/flappy_bird/flappy-evolution-worker/README.md +425 -425
- package/test/examples/flappy_bird/simulation-shared/README.md +170 -170
- package/test/examples/flappy_bird/simulation-shared/observation/README.md +109 -109
- package/test/examples/flappy_bird/trainer/README.md +325 -325
- package/test/examples/flappy_bird/trainer/evaluation/README.md +74 -74
- package/scripts/analyze-trace.ts +0 -590
- package/scripts/copy-examples.mjs +0 -114
- package/scripts/export-onnx.mjs +0 -86
- package/scripts/generate-bench-tables.mjs +0 -182
- package/scripts/generate-docs.ts +0 -2900
- package/scripts/write-dist-docs-pkg.mjs +0 -16
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Static asset bootstrapping for generated HTML docs.
|
|
3
|
+
*
|
|
4
|
+
* Theme CSS and the landing-page hero image are stable assets that should be
|
|
5
|
+
* prepared before markdown pages are rendered. Keeping that work here prevents
|
|
6
|
+
* the root renderer from mixing file-copy policy with page assembly.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
|
|
12
|
+
import { DOCS_DIR } from './render-docs-html.shared.js';
|
|
13
|
+
|
|
14
|
+
const THEME_CSS_SOURCE_PATH = path.resolve('scripts', 'assets', 'theme.css');
|
|
15
|
+
const THEME_CSS_OUTPUT_PATH = path.join(DOCS_DIR, 'assets', 'theme.css');
|
|
16
|
+
const NN_IMAGE_SOURCE_PATH = path.resolve('nn.jpg');
|
|
17
|
+
const NN_IMAGE_FALLBACK_SOURCE_PATH = path.resolve(
|
|
18
|
+
'scripts',
|
|
19
|
+
'assets',
|
|
20
|
+
'nn.jpg',
|
|
21
|
+
);
|
|
22
|
+
const NN_IMAGE_OUTPUT_PATH = path.join(DOCS_DIR, 'nn.jpg');
|
|
23
|
+
|
|
24
|
+
/** Ensures static non-Mermaid assets exist under the generated docs tree. */
|
|
25
|
+
export async function ensureRenderDocsAssets(): Promise<void> {
|
|
26
|
+
await ensureThemeCss();
|
|
27
|
+
await ensureRootHeroImage();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Copies the docs theme stylesheet into the published assets tree. */
|
|
31
|
+
async function ensureThemeCss(): Promise<void> {
|
|
32
|
+
await fs.ensureDir(path.dirname(THEME_CSS_OUTPUT_PATH));
|
|
33
|
+
await fs.copyFile(THEME_CSS_SOURCE_PATH, THEME_CSS_OUTPUT_PATH);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Copies the root README hero image so `/docs/README.md` can keep using the
|
|
38
|
+
* same `nn.jpg` reference after publication.
|
|
39
|
+
*/
|
|
40
|
+
async function ensureRootHeroImage(): Promise<void> {
|
|
41
|
+
const hasRootImage = await fs.pathExists(NN_IMAGE_SOURCE_PATH);
|
|
42
|
+
const hasFallbackImage = await fs.pathExists(NN_IMAGE_FALLBACK_SOURCE_PATH);
|
|
43
|
+
const sourcePath = hasRootImage
|
|
44
|
+
? NN_IMAGE_SOURCE_PATH
|
|
45
|
+
: hasFallbackImage
|
|
46
|
+
? NN_IMAGE_FALLBACK_SOURCE_PATH
|
|
47
|
+
: undefined;
|
|
48
|
+
|
|
49
|
+
if (!sourcePath) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await fs.copyFile(sourcePath, NN_IMAGE_OUTPUT_PATH);
|
|
54
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Mermaid asset bootstrapping and validation for generated HTML docs.
|
|
3
|
+
*
|
|
4
|
+
* This chapter owns both sides of Mermaid handling: copying the browser bundle
|
|
5
|
+
* needed at runtime and treating invalid fenced diagrams as a build failure
|
|
6
|
+
* during docs generation.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { spawn } from 'node:child_process';
|
|
10
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import fs from 'fs-extra';
|
|
14
|
+
|
|
15
|
+
import { escapeHtml, DOCS_DIR } from './render-docs-html.shared.js';
|
|
16
|
+
import type { MermaidBlockReference } from './render-docs-html.types.js';
|
|
17
|
+
|
|
18
|
+
const MERMAID_MODULE_SOURCE_PATH = path.resolve(
|
|
19
|
+
'node_modules',
|
|
20
|
+
'mermaid',
|
|
21
|
+
'dist',
|
|
22
|
+
'mermaid.esm.min.mjs',
|
|
23
|
+
);
|
|
24
|
+
const MERMAID_DIST_SOURCE_DIR = path.resolve('node_modules', 'mermaid', 'dist');
|
|
25
|
+
const MERMAID_DIST_OUTPUT_DIR = path.join(DOCS_DIR, 'assets', 'vendor');
|
|
26
|
+
const MERMAID_CLI_SCRIPT_PATH = path.resolve(
|
|
27
|
+
'dist-docs',
|
|
28
|
+
'scripts',
|
|
29
|
+
'mermaid-cli.js',
|
|
30
|
+
);
|
|
31
|
+
const MERMAID_VALIDATION_CONCURRENCY = Math.max(
|
|
32
|
+
1,
|
|
33
|
+
Math.min(
|
|
34
|
+
4,
|
|
35
|
+
typeof os.availableParallelism === 'function'
|
|
36
|
+
? os.availableParallelism() - 1
|
|
37
|
+
: 4,
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
interface MermaidValidationTask {
|
|
42
|
+
mermaidBlock: MermaidBlockReference;
|
|
43
|
+
tempInputPath: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Ensures the Mermaid browser bundle is copied into the published docs tree. */
|
|
47
|
+
export async function ensureMermaidBrowserAssets(): Promise<void> {
|
|
48
|
+
const hasMermaidModule = await fs.pathExists(MERMAID_MODULE_SOURCE_PATH);
|
|
49
|
+
if (!hasMermaidModule) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await fs.ensureDir(MERMAID_DIST_OUTPUT_DIR);
|
|
54
|
+
await fs.copy(MERMAID_DIST_SOURCE_DIR, MERMAID_DIST_OUTPUT_DIR, {
|
|
55
|
+
overwrite: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Builds the client-side Mermaid bootstrap script for one generated page. */
|
|
60
|
+
export function buildMermaidBootstrapScript(relToRoot: string): string {
|
|
61
|
+
const mermaidModuleHref =
|
|
62
|
+
(relToRoot ? `${relToRoot}/` : '') + 'assets/vendor/mermaid.esm.min.mjs';
|
|
63
|
+
|
|
64
|
+
return `<script type="module">
|
|
65
|
+
import mermaid from "${mermaidModuleHref}";
|
|
66
|
+
|
|
67
|
+
mermaid.initialize({
|
|
68
|
+
startOnLoad: false,
|
|
69
|
+
securityLevel: 'loose',
|
|
70
|
+
theme: 'base',
|
|
71
|
+
themeVariables: {
|
|
72
|
+
darkMode: true,
|
|
73
|
+
background: '#121a24',
|
|
74
|
+
primaryColor: '#102131',
|
|
75
|
+
primaryTextColor: '#d7e4f3',
|
|
76
|
+
primaryBorderColor: '#5ec8ff',
|
|
77
|
+
secondaryColor: '#0f1722',
|
|
78
|
+
secondaryTextColor: '#d7e4f3',
|
|
79
|
+
secondaryBorderColor: '#5ec8ff',
|
|
80
|
+
tertiaryColor: '#16283a',
|
|
81
|
+
tertiaryTextColor: '#d7e4f3',
|
|
82
|
+
tertiaryBorderColor: '#ffbf69',
|
|
83
|
+
mainBkg: '#0f1722',
|
|
84
|
+
nodeBorder: '#5ec8ff',
|
|
85
|
+
clusterBkg: '#102131',
|
|
86
|
+
clusterBorder: '#5ec8ff',
|
|
87
|
+
lineColor: '#5ec8ff',
|
|
88
|
+
edgeLabelBackground: '#0f1722',
|
|
89
|
+
textColor: '#d7e4f3',
|
|
90
|
+
fontFamily: 'Open Sans, sans-serif'
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
await mermaid.run({ querySelector: '.mermaid-diagram' });
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('[docs] Mermaid render failed.', error);
|
|
98
|
+
}
|
|
99
|
+
</script>`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Collects Mermaid fenced blocks from one markdown page. */
|
|
103
|
+
export function collectMermaidBlocks(
|
|
104
|
+
markdown: string,
|
|
105
|
+
readmePath: string,
|
|
106
|
+
): MermaidBlockReference[] {
|
|
107
|
+
const mermaidBlocks: MermaidBlockReference[] = [];
|
|
108
|
+
const mermaidFencePattern = /^```mermaid[^\n]*\r?\n([\s\S]*?)^```\s*$/gm;
|
|
109
|
+
let mermaidMatch = mermaidFencePattern.exec(markdown);
|
|
110
|
+
|
|
111
|
+
while (mermaidMatch) {
|
|
112
|
+
mermaidBlocks.push({
|
|
113
|
+
readmePath,
|
|
114
|
+
blockNumber: mermaidBlocks.length + 1,
|
|
115
|
+
diagram: mermaidMatch[1].trim(),
|
|
116
|
+
});
|
|
117
|
+
mermaidMatch = mermaidFencePattern.exec(markdown);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return mermaidBlocks;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Validates every collected Mermaid block before HTML generation proceeds. */
|
|
124
|
+
export async function validateMermaidBlocks(
|
|
125
|
+
mermaidBlocks: readonly MermaidBlockReference[],
|
|
126
|
+
): Promise<void> {
|
|
127
|
+
if (mermaidBlocks.length === 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const uniqueMermaidBlocks = resolveUniqueMermaidBlocks(mermaidBlocks);
|
|
132
|
+
const tempDirectoryPath = await mkdtemp(
|
|
133
|
+
path.join(os.tmpdir(), 'neatapticts-docs-mermaid-'),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const mermaidValidationTasks = uniqueMermaidBlocks.map(
|
|
138
|
+
(mermaidBlock, blockIndex) => ({
|
|
139
|
+
mermaidBlock,
|
|
140
|
+
tempInputPath: path.join(
|
|
141
|
+
tempDirectoryPath,
|
|
142
|
+
`diagram-${blockIndex + 1}.mmd`,
|
|
143
|
+
),
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
await Promise.all(
|
|
148
|
+
mermaidValidationTasks.map((mermaidValidationTask) =>
|
|
149
|
+
fs.writeFile(
|
|
150
|
+
mermaidValidationTask.tempInputPath,
|
|
151
|
+
mermaidValidationTask.mermaidBlock.diagram,
|
|
152
|
+
'utf8',
|
|
153
|
+
),
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
await runMermaidValidationPool(mermaidValidationTasks);
|
|
157
|
+
} finally {
|
|
158
|
+
await rm(tempDirectoryPath, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Resolves one representative block for each unique Mermaid source string.
|
|
164
|
+
*
|
|
165
|
+
* Many generated README surfaces repeat the same diagrams, so validating each
|
|
166
|
+
* unique source only once avoids redundant parser work.
|
|
167
|
+
*
|
|
168
|
+
* @param mermaidBlocks - Mermaid blocks collected from generated docs surfaces.
|
|
169
|
+
* @returns Representative Mermaid blocks keyed by unique diagram text.
|
|
170
|
+
*/
|
|
171
|
+
function resolveUniqueMermaidBlocks(
|
|
172
|
+
mermaidBlocks: readonly MermaidBlockReference[],
|
|
173
|
+
): MermaidBlockReference[] {
|
|
174
|
+
const uniqueMermaidBlocksByDiagram = new Map<string, MermaidBlockReference>();
|
|
175
|
+
|
|
176
|
+
for (const mermaidBlock of mermaidBlocks) {
|
|
177
|
+
if (!uniqueMermaidBlocksByDiagram.has(mermaidBlock.diagram)) {
|
|
178
|
+
uniqueMermaidBlocksByDiagram.set(mermaidBlock.diagram, mermaidBlock);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return [...uniqueMermaidBlocksByDiagram.values()];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Validates Mermaid diagrams through a bounded parallel worker pool. */
|
|
186
|
+
async function runMermaidValidationPool(
|
|
187
|
+
mermaidValidationTasks: readonly MermaidValidationTask[],
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
let nextTaskIndex = 0;
|
|
190
|
+
const workerCount = Math.min(
|
|
191
|
+
MERMAID_VALIDATION_CONCURRENCY,
|
|
192
|
+
mermaidValidationTasks.length,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
await Promise.all(
|
|
196
|
+
Array.from({ length: workerCount }, async () => {
|
|
197
|
+
while (nextTaskIndex < mermaidValidationTasks.length) {
|
|
198
|
+
const mermaidValidationTask = mermaidValidationTasks[nextTaskIndex];
|
|
199
|
+
nextTaskIndex += 1;
|
|
200
|
+
await runMermaidValidation(mermaidValidationTask);
|
|
201
|
+
}
|
|
202
|
+
}),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Runs the Mermaid CLI validator for one diagram source file. */
|
|
207
|
+
async function runMermaidValidation(
|
|
208
|
+
mermaidValidationTask: MermaidValidationTask,
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
await new Promise<void>((resolve, reject) => {
|
|
211
|
+
const childProcess = spawn(
|
|
212
|
+
process.execPath,
|
|
213
|
+
[
|
|
214
|
+
MERMAID_CLI_SCRIPT_PATH,
|
|
215
|
+
'validate',
|
|
216
|
+
'--input',
|
|
217
|
+
mermaidValidationTask.tempInputPath,
|
|
218
|
+
],
|
|
219
|
+
{ stdio: 'inherit' },
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
childProcess.once('exit', (exitCode, signal) => {
|
|
223
|
+
if (exitCode === 0) {
|
|
224
|
+
resolve();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const relativeReadmePath = path.relative(
|
|
229
|
+
process.cwd(),
|
|
230
|
+
mermaidValidationTask.mermaidBlock.readmePath,
|
|
231
|
+
);
|
|
232
|
+
reject(
|
|
233
|
+
new Error(
|
|
234
|
+
`Invalid Mermaid diagram in ${relativeReadmePath} (block ${mermaidValidationTask.mermaidBlock.blockNumber}). Mermaid CLI exited with code ${exitCode ?? 'null'}${signal ? ` and signal ${signal}` : ''}.`,
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
childProcess.once('error', reject);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Renders Mermaid fenced blocks as safe placeholder HTML before client boot. */
|
|
243
|
+
export function renderMermaidDiagramPlaceholder(diagram: string): string {
|
|
244
|
+
return `<pre class="mermaid mermaid-diagram">${escapeHtml(diagram)}</pre>`;
|
|
245
|
+
}
|
package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts}
RENAMED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Owns the generated docs sidebar contract.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* reference reading, and a depth-aware rail that stays visually continuous as
|
|
8
|
-
* readers move between overview pages and nested deep dives.
|
|
4
|
+
* This chapter turns the published docs page set into the left-nav learning
|
|
5
|
+
* path: examples first for onboarding, grouped docs pages for reference
|
|
6
|
+
* reading, and a depth-aware rail that remains visually continuous.
|
|
9
7
|
*/
|
|
10
8
|
|
|
9
|
+
import type {
|
|
10
|
+
DocsSidebarRenderContext,
|
|
11
|
+
PageMeta,
|
|
12
|
+
SidebarRenderContext,
|
|
13
|
+
} from './render-docs-html.types.js';
|
|
14
|
+
|
|
11
15
|
interface ExampleNavLink {
|
|
12
16
|
dir: string;
|
|
13
17
|
label: string;
|
|
@@ -30,34 +34,6 @@ interface PipeNavEntry {
|
|
|
30
34
|
cssClass?: string;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
interface SidebarRenderHelpers {
|
|
34
|
-
generatedPageDirectories: ReadonlySet<string>;
|
|
35
|
-
hasPublishedDocsPage: (
|
|
36
|
-
relDir: string,
|
|
37
|
-
generatedPageDirectories: ReadonlySet<string>,
|
|
38
|
-
) => boolean;
|
|
39
|
-
buildRelativeDocsHref: (currentDir: string, targetDir: string) => string;
|
|
40
|
-
escapeHtml: (value: string) => string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Minimal page shape needed to assemble the docs sidebar groups. */
|
|
44
|
-
export interface SidebarPageMeta {
|
|
45
|
-
relDir: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Shared render context for sidebar fragments that need publication checks and
|
|
50
|
-
* stable relative links.
|
|
51
|
-
*/
|
|
52
|
-
export interface SidebarRenderContext extends SidebarRenderHelpers {
|
|
53
|
-
currentDir: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Render context for the full docs sidebar, including the grouped page set. */
|
|
57
|
-
export interface DocsSidebarRenderContext extends SidebarRenderContext {
|
|
58
|
-
pages: readonly SidebarPageMeta[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
37
|
const EXAMPLES_OVERVIEW_DIR = 'examples';
|
|
62
38
|
const SIDEBAR_GROUP_ORDER = [
|
|
63
39
|
'root',
|
|
@@ -216,16 +192,7 @@ const EXAMPLE_DEMOS: readonly ExampleNavDemo[] = [
|
|
|
216
192
|
},
|
|
217
193
|
];
|
|
218
194
|
|
|
219
|
-
/**
|
|
220
|
-
* Render the compact examples list used on the root-page table of contents.
|
|
221
|
-
*
|
|
222
|
-
* This keeps the landing page aligned with the learning-path plan: readers see
|
|
223
|
-
* the examples overview first, then the strongest demo entrypoints, without
|
|
224
|
-
* needing the full sidebar card treatment.
|
|
225
|
-
*
|
|
226
|
-
* @param context - Link and publication helpers for the page being rendered.
|
|
227
|
-
* @returns A compact pipe-nav list for published example docs, or an empty list.
|
|
228
|
-
*/
|
|
195
|
+
/** Renders the compact examples list used on the root-page table of contents. */
|
|
229
196
|
export function buildExamplesTocLinksHtml(
|
|
230
197
|
context: SidebarRenderContext,
|
|
231
198
|
): string {
|
|
@@ -262,25 +229,18 @@ export function buildExamplesTocLinksHtml(
|
|
|
262
229
|
return buildPipeNavListHtml(context, entries, 'pipe-nav-list-compact');
|
|
263
230
|
}
|
|
264
231
|
|
|
265
|
-
/**
|
|
266
|
-
* Render the full left docs sidebar with the examples-first learning path.
|
|
267
|
-
*
|
|
268
|
-
* The sidebar owns the teaching order for generated docs pages. Runnable
|
|
269
|
-
* examples stay at the top because they are the fastest route to first success,
|
|
270
|
-
* while the remaining pages are grouped into reference sections that make sense
|
|
271
|
-
* once a reader is already oriented.
|
|
272
|
-
*
|
|
273
|
-
* @param context - The published page set, current page, and link helpers.
|
|
274
|
-
* @returns The full sidebar section list for the current docs page.
|
|
275
|
-
*/
|
|
232
|
+
/** Renders the full left docs sidebar with the examples-first learning path. */
|
|
276
233
|
export function buildDocsSidebarHtml(
|
|
277
234
|
context: DocsSidebarRenderContext,
|
|
278
235
|
): string {
|
|
279
|
-
const groupsMap = new Map<string, { name: string; items:
|
|
236
|
+
const groupsMap = new Map<string, { name: string; items: PageMeta[] }>();
|
|
280
237
|
|
|
281
238
|
for (const page of context.pages) {
|
|
282
239
|
const segment = page.relDir.split('/')[0] || 'root';
|
|
283
|
-
if (segment === 'examples')
|
|
240
|
+
if (segment === 'examples') {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
284
244
|
if (!groupsMap.has(segment)) {
|
|
285
245
|
groupsMap.set(segment, { name: segment, items: [] });
|
|
286
246
|
}
|
|
@@ -298,8 +258,11 @@ export function buildDocsSidebarHtml(
|
|
|
298
258
|
rightGroup.name as (typeof SIDEBAR_GROUP_ORDER)[number],
|
|
299
259
|
);
|
|
300
260
|
const leftRank = leftOrder === -1 ? Number.MAX_SAFE_INTEGER : leftOrder;
|
|
301
|
-
const rightRank =
|
|
302
|
-
|
|
261
|
+
const rightRank =
|
|
262
|
+
rightOrder === -1 ? Number.MAX_SAFE_INTEGER : rightOrder;
|
|
263
|
+
return (
|
|
264
|
+
leftRank - rightRank || leftGroup.name.localeCompare(rightGroup.name)
|
|
265
|
+
);
|
|
303
266
|
})
|
|
304
267
|
.map((group) => {
|
|
305
268
|
const items = group.items.toSorted((leftPage, rightPage) =>
|
|
@@ -317,7 +280,7 @@ export function buildDocsSidebarHtml(
|
|
|
317
280
|
}
|
|
318
281
|
|
|
319
282
|
return `<li class="group"><div class="g-head">${context.escapeHtml(
|
|
320
|
-
group.name,
|
|
283
|
+
formatSidebarGroupName(group.name),
|
|
321
284
|
)}</div>${buildPipeNavListHtml(
|
|
322
285
|
context,
|
|
323
286
|
buildGroupPipeEntries(items, context.currentDir),
|
|
@@ -328,16 +291,7 @@ export function buildDocsSidebarHtml(
|
|
|
328
291
|
return `<ul class="sidebar-sections">${examplesSidebarHtml}${groupsHtml}</ul>`;
|
|
329
292
|
}
|
|
330
293
|
|
|
331
|
-
/**
|
|
332
|
-
* Render the example showcase cards that anchor the sidebar learning path.
|
|
333
|
-
*
|
|
334
|
-
* Each demo keeps a recommended reading order and a separate deep-dive list so
|
|
335
|
-
* the sidebar can teach progression without flattening every page into one long
|
|
336
|
-
* undifferentiated navigation block.
|
|
337
|
-
*
|
|
338
|
-
* @param context - Link and publication helpers for the page being rendered.
|
|
339
|
-
* @returns The examples-first sidebar section, or an empty string when hidden.
|
|
340
|
-
*/
|
|
294
|
+
/** Renders the example showcase cards that anchor the sidebar learning path. */
|
|
341
295
|
function buildExamplesSidebarHtml(context: SidebarRenderContext): string {
|
|
342
296
|
const examplesOverviewLink = buildPipeNavListHtml(
|
|
343
297
|
context,
|
|
@@ -358,64 +312,98 @@ function buildExamplesSidebarHtml(context: SidebarRenderContext): string {
|
|
|
358
312
|
'pipe-nav-list-priority',
|
|
359
313
|
);
|
|
360
314
|
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
315
|
+
const showExpandedExampleShowcase =
|
|
316
|
+
context.currentDir === '' ||
|
|
317
|
+
context.currentDir === EXAMPLES_OVERVIEW_DIR ||
|
|
318
|
+
context.currentDir.startsWith('examples/');
|
|
319
|
+
const compactExamplesLinksHtml = showExpandedExampleShowcase
|
|
320
|
+
? ''
|
|
321
|
+
: buildPipeNavListHtml(
|
|
322
|
+
context,
|
|
323
|
+
EXAMPLE_DEMOS.filter((demo) =>
|
|
324
|
+
context.hasPublishedDocsPage(
|
|
325
|
+
`${demo.dir}/docs`,
|
|
326
|
+
context.generatedPageDirectories,
|
|
327
|
+
),
|
|
328
|
+
).map((demo) => ({
|
|
329
|
+
label: demo.label,
|
|
330
|
+
targetDir: `${demo.dir}/docs`,
|
|
371
331
|
depth: 0,
|
|
372
|
-
isCurrent: context.currentDir ===
|
|
373
|
-
})),
|
|
374
|
-
);
|
|
375
|
-
const deepDiveLinksHtml = buildPipeNavListHtml(
|
|
376
|
-
context,
|
|
377
|
-
demo.deepDives
|
|
378
|
-
.filter((entry) =>
|
|
379
|
-
context.hasPublishedDocsPage(entry.dir, context.generatedPageDirectories),
|
|
380
|
-
)
|
|
381
|
-
.map((entry) => ({
|
|
382
|
-
label: entry.label,
|
|
383
|
-
targetDir: entry.dir,
|
|
384
|
-
depth: 1,
|
|
385
|
-
isCurrent: context.currentDir === entry.dir,
|
|
332
|
+
isCurrent: context.currentDir === `${demo.dir}/docs`,
|
|
386
333
|
})),
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
context.currentDir === demo.dir ||
|
|
390
|
-
context.currentDir.startsWith(`${demo.dir}/`) ||
|
|
391
|
-
context.currentDir.startsWith(`${demo.dir}/docs`);
|
|
392
|
-
|
|
393
|
-
if (!readingOrderLinksHtml && !deepDiveLinksHtml) {
|
|
394
|
-
return '';
|
|
395
|
-
}
|
|
334
|
+
'pipe-nav-list-compact',
|
|
335
|
+
);
|
|
396
336
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
337
|
+
const demoCardsHtml = showExpandedExampleShowcase
|
|
338
|
+
? EXAMPLE_DEMOS.map((demo) => {
|
|
339
|
+
const readingOrderLinksHtml = buildPipeNavListHtml(
|
|
340
|
+
context,
|
|
341
|
+
demo.readingOrder
|
|
342
|
+
.filter((entry) =>
|
|
343
|
+
context.hasPublishedDocsPage(
|
|
344
|
+
entry.dir,
|
|
345
|
+
context.generatedPageDirectories,
|
|
346
|
+
),
|
|
347
|
+
)
|
|
348
|
+
.map((entry) => ({
|
|
349
|
+
label: entry.label,
|
|
350
|
+
targetDir: entry.dir,
|
|
351
|
+
depth: 0,
|
|
352
|
+
isCurrent: context.currentDir === entry.dir,
|
|
353
|
+
})),
|
|
354
|
+
);
|
|
355
|
+
const deepDiveLinksHtml = buildPipeNavListHtml(
|
|
356
|
+
context,
|
|
357
|
+
demo.deepDives
|
|
358
|
+
.filter((entry) =>
|
|
359
|
+
context.hasPublishedDocsPage(
|
|
360
|
+
entry.dir,
|
|
361
|
+
context.generatedPageDirectories,
|
|
362
|
+
),
|
|
363
|
+
)
|
|
364
|
+
.map((entry) => ({
|
|
365
|
+
label: entry.label,
|
|
366
|
+
targetDir: entry.dir,
|
|
367
|
+
depth: 1,
|
|
368
|
+
isCurrent: context.currentDir === entry.dir,
|
|
369
|
+
})),
|
|
370
|
+
);
|
|
371
|
+
const isCurrentDemo =
|
|
372
|
+
context.currentDir === demo.dir ||
|
|
373
|
+
context.currentDir.startsWith(`${demo.dir}/`) ||
|
|
374
|
+
context.currentDir.startsWith(`${demo.dir}/docs`);
|
|
375
|
+
|
|
376
|
+
if (!readingOrderLinksHtml && !deepDiveLinksHtml) {
|
|
377
|
+
return '';
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return `<li class="nav-demo-card${isCurrentDemo ? ' is-current-demo' : ''}"><div class="nav-demo-meta"><div class="nav-demo-eyebrow">${context.escapeHtml(
|
|
381
|
+
demo.eyebrow,
|
|
382
|
+
)}</div><div class="nav-demo-title">${context.escapeHtml(
|
|
383
|
+
demo.label,
|
|
384
|
+
)}</div><p class="nav-demo-description">${context.escapeHtml(
|
|
385
|
+
demo.description,
|
|
386
|
+
)}</p></div><div class="nav-demo-cluster"><div class="nav-cluster-title">Recommended path</div>${readingOrderLinksHtml}</div>${
|
|
387
|
+
deepDiveLinksHtml
|
|
388
|
+
? `<div class="nav-demo-cluster nav-demo-cluster-secondary"><div class="nav-cluster-title">Deep dives</div>${deepDiveLinksHtml}</div>`
|
|
389
|
+
: ''
|
|
390
|
+
}</li>`;
|
|
391
|
+
})
|
|
392
|
+
.filter(Boolean)
|
|
393
|
+
.join('')
|
|
394
|
+
: '';
|
|
411
395
|
|
|
412
|
-
if (!examplesOverviewLink && !demoCardsHtml)
|
|
396
|
+
if (!examplesOverviewLink && !compactExamplesLinksHtml && !demoCardsHtml) {
|
|
397
|
+
return '';
|
|
398
|
+
}
|
|
413
399
|
|
|
414
|
-
return `<li class="group group-priority"><div class="g-head">Start With Examples</div><p class="group-copy">The demos are the fastest way to learn the library in the order a first-time reader can actually absorb.</p><div class="nav-priority-links">${examplesOverviewLink}</div
|
|
400
|
+
return `<li class="group group-priority"><div class="g-head">Start With Examples</div><p class="group-copy">The demos are the fastest way to learn the library in the order a first-time reader can actually absorb.</p><div class="nav-priority-links">${examplesOverviewLink}${compactExamplesLinksHtml}</div>${
|
|
401
|
+
demoCardsHtml ? `<ul class="nav-demo-showcase">${demoCardsHtml}</ul>` : ''
|
|
402
|
+
}</li>`;
|
|
415
403
|
}
|
|
416
404
|
|
|
417
405
|
function buildGroupPipeEntries(
|
|
418
|
-
items: readonly
|
|
406
|
+
items: readonly PageMeta[],
|
|
419
407
|
currentDir: string,
|
|
420
408
|
): PipeNavEntry[] {
|
|
421
409
|
return items
|
|
@@ -425,7 +413,7 @@ function buildGroupPipeEntries(
|
|
|
425
413
|
.map((page) => {
|
|
426
414
|
const relativeSegments = page.relDir.split('/').slice(1);
|
|
427
415
|
return {
|
|
428
|
-
label: relativeSegments
|
|
416
|
+
label: formatGroupEntryLabel(relativeSegments),
|
|
429
417
|
targetDir: page.relDir,
|
|
430
418
|
depth: Math.max(0, relativeSegments.length - 1),
|
|
431
419
|
isCurrent: page.relDir === currentDir,
|
|
@@ -434,17 +422,35 @@ function buildGroupPipeEntries(
|
|
|
434
422
|
}
|
|
435
423
|
|
|
436
424
|
/**
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
* Depth is represented by layout boxes rather than text glyph stacks. That
|
|
440
|
-
* gives CSS a stable column contract for the vertical rail and connector line,
|
|
441
|
-
* which prevents the left path from breaking when fonts, line-height, or
|
|
442
|
-
* example cluster boundaries change.
|
|
443
|
-
*
|
|
444
|
-
* @param depth - Logical nesting depth for the entry being rendered.
|
|
445
|
-
* @param isFooter - Whether the cells belong to the footer row that closes the list.
|
|
446
|
-
* @returns The rail-cell markup for one sidebar row.
|
|
425
|
+
* Formats sidebar labels so overview pages read clearly and deep pages are not
|
|
426
|
+
* reduced to ambiguous repeated leaf names.
|
|
447
427
|
*/
|
|
428
|
+
function formatGroupEntryLabel(relativeSegments: readonly string[]): string {
|
|
429
|
+
if (relativeSegments.length === 0) {
|
|
430
|
+
return 'Overview';
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const leafLabel = formatSidebarGroupName(
|
|
434
|
+
relativeSegments.at(-1) ?? 'Overview',
|
|
435
|
+
);
|
|
436
|
+
if (relativeSegments.length === 1) {
|
|
437
|
+
return leafLabel;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const parentLabel = formatSidebarGroupName(relativeSegments.at(-2) ?? '');
|
|
441
|
+
return `${parentLabel} / ${leafLabel}`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** Formats a sidebar group or segment name into a human-readable label. */
|
|
445
|
+
function formatSidebarGroupName(segment: string): string {
|
|
446
|
+
return segment
|
|
447
|
+
.split(/[-_]/g)
|
|
448
|
+
.filter(Boolean)
|
|
449
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
450
|
+
.join(' ');
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/** Renders the fixed-width rail cells that anchor one sidebar row. */
|
|
448
454
|
function buildPipeRailCellsHtml(depth: number, isFooter = false): string {
|
|
449
455
|
const railDepth = Math.max(0, depth);
|
|
450
456
|
|
|
@@ -462,24 +468,15 @@ function buildPipeRailCellsHtml(depth: number, isFooter = false): string {
|
|
|
462
468
|
}).join('');
|
|
463
469
|
}
|
|
464
470
|
|
|
465
|
-
/**
|
|
466
|
-
* Render one pipe-nav list using box-based rail columns for visual continuity.
|
|
467
|
-
*
|
|
468
|
-
* Entries provide a logical depth value rather than nested list markup. That
|
|
469
|
-
* keeps the DOM shallow while still giving the theme enough structure to draw a
|
|
470
|
-
* continuous rail through overview links, recommended paths, and deep dives.
|
|
471
|
-
*
|
|
472
|
-
* @param context - Link and publication helpers for the page being rendered.
|
|
473
|
-
* @param entries - Sidebar links with the depth contract already resolved.
|
|
474
|
-
* @param listClass - Optional modifier class for compact or priority variants.
|
|
475
|
-
* @returns One pipe-nav list plus a footer rail that finishes the visual track.
|
|
476
|
-
*/
|
|
471
|
+
/** Renders one rail-based pipe navigation list. */
|
|
477
472
|
function buildPipeNavListHtml(
|
|
478
473
|
context: SidebarRenderContext,
|
|
479
474
|
entries: readonly PipeNavEntry[],
|
|
480
475
|
listClass = '',
|
|
481
476
|
): string {
|
|
482
|
-
if (entries.length === 0)
|
|
477
|
+
if (entries.length === 0) {
|
|
478
|
+
return '';
|
|
479
|
+
}
|
|
483
480
|
|
|
484
481
|
const itemsHtml = entries
|
|
485
482
|
.map((entry) => {
|
|
@@ -510,4 +507,4 @@ function buildPipeNavListHtml(
|
|
|
510
507
|
lastEntryDepth,
|
|
511
508
|
true,
|
|
512
509
|
)}</span></li></ul>`;
|
|
513
|
-
}
|
|
510
|
+
}
|