@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.
Files changed (210) hide show
  1. package/.github/copilot-instructions.md +11 -0
  2. package/.github/skills/trace-analyzer-extension/SKILL.md +3 -3
  3. package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +1 -1
  4. package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +1 -1
  5. package/.github/skills/trace-audit-reporting/SKILL.md +3 -3
  6. package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +1 -1
  7. package/package.json +19 -13
  8. package/plans/Flappy_Bird_Folder_Documentation_Pass.md +4 -4
  9. package/plans/README.md +24 -0
  10. package/plans/Roadmap.md +62 -40
  11. package/plans/analyze-trace-solid-split.plans.md +66 -0
  12. package/plans/architecture-solid-split.plans.md +9 -15
  13. package/plans/asciiMaze-typescript-repair.plans.md +1 -1
  14. package/plans/generate-docs-solid-split.plans.md +87 -0
  15. package/plans/methods-docs.plans.md +25 -1
  16. package/plans/methods-solid-split.plans.md +14 -14
  17. package/plans/neat-docs.plans.md +9 -1
  18. package/plans/neat-test-surface-repair.plans.md +1 -1
  19. package/plans/render-docs-html-solid-split.plans.md +68 -0
  20. package/plans/src-no-explicit-any-cleanup.plans.md +1 -1
  21. package/plans/utils-docs.plans.md +6 -1
  22. package/scripts/analyze-trace/analyze-trace.analysis.ts +479 -0
  23. package/scripts/analyze-trace/analyze-trace.constants.ts +35 -0
  24. package/scripts/analyze-trace/analyze-trace.io.ts +69 -0
  25. package/scripts/analyze-trace/analyze-trace.report.ts +100 -0
  26. package/scripts/analyze-trace/analyze-trace.shared.ts +116 -0
  27. package/scripts/analyze-trace/analyze-trace.ts +45 -0
  28. package/scripts/analyze-trace/analyze-trace.types.ts +72 -0
  29. package/scripts/assets/theme.css +80 -23
  30. package/scripts/copy-examples.ts +239 -0
  31. package/scripts/export-onnx.ts +223 -0
  32. package/scripts/generate-bench-tables.ts +378 -37
  33. package/scripts/generate-docs/generate-docs.constants.ts +107 -0
  34. package/scripts/generate-docs/generate-docs.order.ts +355 -0
  35. package/scripts/generate-docs/generate-docs.state.ts +31 -0
  36. package/scripts/generate-docs/generate-docs.targets.ts +165 -0
  37. package/scripts/generate-docs/generate-docs.ts +63 -0
  38. package/scripts/generate-docs/generate-docs.types.ts +112 -0
  39. package/scripts/generate-docs/output/generate-docs.output.folder-index.utils.ts +167 -0
  40. package/scripts/generate-docs/output/generate-docs.output.ordering.utils.ts +353 -0
  41. package/scripts/generate-docs/output/generate-docs.output.readme.utils.ts +420 -0
  42. package/scripts/generate-docs/output/generate-docs.output.ts +123 -0
  43. package/scripts/generate-docs/output/generate-docs.output.warnings.utils.ts +219 -0
  44. package/scripts/generate-docs/symbols/generate-docs.symbols.collection.utils.ts +365 -0
  45. package/scripts/generate-docs/symbols/generate-docs.symbols.jsdoc.utils.ts +373 -0
  46. package/scripts/generate-docs/symbols/generate-docs.symbols.normalize.utils.ts +155 -0
  47. package/scripts/generate-docs/symbols/generate-docs.symbols.render.utils.ts +149 -0
  48. package/scripts/generate-docs/symbols/generate-docs.symbols.signature.utils.ts +289 -0
  49. package/scripts/generate-docs/symbols/generate-docs.symbols.ts +11 -0
  50. package/scripts/mermaid-cli.mjs +102 -22
  51. package/scripts/mermaid-cli.ts +736 -0
  52. package/scripts/render-docs-html/render-docs-html.assets.ts +54 -0
  53. package/scripts/render-docs-html/render-docs-html.mermaid.ts +245 -0
  54. package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts} +141 -144
  55. package/scripts/render-docs-html/render-docs-html.pages.ts +333 -0
  56. package/scripts/render-docs-html/render-docs-html.shared.ts +333 -0
  57. package/scripts/render-docs-html/render-docs-html.types.ts +42 -0
  58. package/scripts/render-docs-html.ts +23 -587
  59. package/scripts/run-docs.ts +238 -0
  60. package/scripts/write-dist-docs-pkg.ts +40 -0
  61. package/src/README.md +75 -75
  62. package/src/architecture/connection/README.md +5 -5
  63. package/src/architecture/layer/README.md +508 -508
  64. package/src/architecture/network/README.md +1458 -1458
  65. package/src/architecture/network/activate/README.md +694 -694
  66. package/src/architecture/network/bootstrap/README.md +77 -77
  67. package/src/architecture/network/connect/README.md +74 -74
  68. package/src/architecture/network/deterministic/README.md +135 -135
  69. package/src/architecture/network/evolve/README.md +364 -364
  70. package/src/architecture/network/gating/README.md +130 -130
  71. package/src/architecture/network/genetic/README.md +399 -399
  72. package/src/architecture/network/mutate/README.md +897 -897
  73. package/src/architecture/network/onnx/README.md +720 -720
  74. package/src/architecture/network/onnx/export/README.md +728 -728
  75. package/src/architecture/network/onnx/export/layers/README.md +450 -450
  76. package/src/architecture/network/onnx/import/README.md +618 -618
  77. package/src/architecture/network/onnx/schema/README.md +32 -32
  78. package/src/architecture/network/prune/README.md +245 -245
  79. package/src/architecture/network/remove/README.md +135 -135
  80. package/src/architecture/network/runtime/README.md +106 -106
  81. package/src/architecture/network/serialize/README.md +542 -542
  82. package/src/architecture/network/slab/README.md +608 -608
  83. package/src/architecture/network/standalone/README.md +212 -212
  84. package/src/architecture/network/stats/README.md +84 -84
  85. package/src/architecture/network/topology/README.md +465 -465
  86. package/src/architecture/network/training/README.md +200 -200
  87. package/src/architecture/node/README.md +5 -5
  88. package/src/architecture/nodePool/README.md +14 -14
  89. package/src/methods/README.md +99 -99
  90. package/src/methods/activation/README.md +189 -189
  91. package/src/methods/cost/README.md +131 -131
  92. package/src/methods/rate/README.md +86 -86
  93. package/src/multithreading/README.md +77 -77
  94. package/src/multithreading/workers/browser/README.md +8 -8
  95. package/src/multithreading/workers/node/README.md +8 -8
  96. package/src/neat/README.md +148 -148
  97. package/src/neat/adaptive/README.md +120 -120
  98. package/src/neat/adaptive/acceptance/README.md +40 -40
  99. package/src/neat/adaptive/complexity/README.md +137 -137
  100. package/src/neat/adaptive/core/README.md +197 -197
  101. package/src/neat/adaptive/lineage/README.md +90 -90
  102. package/src/neat/adaptive/mutation/README.md +284 -284
  103. package/src/neat/compat/README.md +43 -43
  104. package/src/neat/compat/core/README.md +90 -90
  105. package/src/neat/diversity/README.md +35 -35
  106. package/src/neat/diversity/core/README.md +88 -88
  107. package/src/neat/evaluate/README.md +85 -85
  108. package/src/neat/evaluate/auto-distance/README.md +75 -75
  109. package/src/neat/evaluate/entropy-compat/README.md +37 -37
  110. package/src/neat/evaluate/entropy-sharing/README.md +43 -43
  111. package/src/neat/evaluate/fitness/README.md +23 -23
  112. package/src/neat/evaluate/novelty/README.md +120 -120
  113. package/src/neat/evaluate/objectives/README.md +17 -17
  114. package/src/neat/evaluate/shared/README.md +94 -94
  115. package/src/neat/evolve/README.md +96 -96
  116. package/src/neat/evolve/adaptive/README.md +60 -60
  117. package/src/neat/evolve/objectives/README.md +63 -63
  118. package/src/neat/evolve/offspring/README.md +56 -56
  119. package/src/neat/evolve/population/README.md +171 -171
  120. package/src/neat/evolve/runtime/README.md +79 -79
  121. package/src/neat/evolve/speciation/README.md +74 -74
  122. package/src/neat/evolve/warnings/README.md +10 -10
  123. package/src/neat/export/README.md +114 -114
  124. package/src/neat/helpers/README.md +50 -50
  125. package/src/neat/init/README.md +9 -9
  126. package/src/neat/lineage/core/README.md +101 -101
  127. package/src/neat/multiobjective/category/README.md +74 -74
  128. package/src/neat/multiobjective/crowding/README.md +272 -272
  129. package/src/neat/multiobjective/dominance/README.md +171 -171
  130. package/src/neat/multiobjective/fronts/README.md +68 -68
  131. package/src/neat/multiobjective/metrics/README.md +43 -43
  132. package/src/neat/multiobjective/objectives/README.md +31 -31
  133. package/src/neat/multiobjective/shared/README.md +27 -27
  134. package/src/neat/mutation/README.md +97 -97
  135. package/src/neat/mutation/add-conn/README.md +115 -115
  136. package/src/neat/mutation/add-node/README.md +126 -126
  137. package/src/neat/mutation/flow/README.md +149 -149
  138. package/src/neat/mutation/repair/README.md +185 -185
  139. package/src/neat/mutation/select/README.md +117 -117
  140. package/src/neat/mutation/shared/README.md +32 -32
  141. package/src/neat/objectives/README.md +25 -25
  142. package/src/neat/objectives/core/README.md +67 -67
  143. package/src/neat/pruning/README.md +40 -40
  144. package/src/neat/pruning/core/README.md +171 -171
  145. package/src/neat/pruning/facade/README.md +32 -32
  146. package/src/neat/rng/README.md +104 -104
  147. package/src/neat/rng/core/README.md +137 -137
  148. package/src/neat/rng/facade/README.md +50 -50
  149. package/src/neat/selection/README.md +111 -111
  150. package/src/neat/selection/core/README.md +227 -227
  151. package/src/neat/selection/facade/README.md +61 -61
  152. package/src/neat/shared/README.md +163 -163
  153. package/src/neat/speciation/README.md +31 -31
  154. package/src/neat/speciation/threshold/README.md +35 -35
  155. package/src/neat/species/README.md +25 -25
  156. package/src/neat/species/core/README.md +20 -20
  157. package/src/neat/species/core/shared/README.md +18 -18
  158. package/src/neat/species/history/context/README.md +22 -22
  159. package/src/neat/telemetry/accessors/README.md +58 -58
  160. package/src/neat/telemetry/exports/README.md +233 -233
  161. package/src/neat/telemetry/facade/README.md +252 -252
  162. package/src/neat/telemetry/facade/archive/README.md +57 -57
  163. package/src/neat/telemetry/facade/buffer/README.md +43 -43
  164. package/src/neat/telemetry/facade/lineage/README.md +12 -12
  165. package/src/neat/telemetry/facade/objectives/README.md +44 -44
  166. package/src/neat/telemetry/facade/runtime/README.md +26 -26
  167. package/src/neat/telemetry/facade/species/README.md +27 -27
  168. package/src/neat/telemetry/metrics/README.md +696 -696
  169. package/src/neat/telemetry/recorder/README.md +57 -57
  170. package/src/neat/telemetry/types/README.md +32 -32
  171. package/src/neat/topology-intent/README.md +75 -75
  172. package/src/utils/README.md +193 -193
  173. package/test/examples/asciiMaze/browser-entry/README.md +92 -92
  174. package/test/examples/asciiMaze/dashboardManager/README.md +109 -109
  175. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +28 -28
  176. package/test/examples/asciiMaze/evolutionEngine/README.md +1527 -1527
  177. package/test/examples/asciiMaze/mazeMovement/README.md +105 -105
  178. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +16 -16
  179. package/test/examples/asciiMaze/mazeMovement/policy/README.md +57 -57
  180. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +52 -52
  181. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +46 -46
  182. package/test/examples/flappy_bird/browser-entry/README.md +508 -508
  183. package/test/examples/flappy_bird/browser-entry/host/README.md +101 -101
  184. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +144 -144
  185. package/test/examples/flappy_bird/browser-entry/network-view/README.md +194 -194
  186. package/test/examples/flappy_bird/browser-entry/playback/README.md +278 -278
  187. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +129 -129
  188. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +502 -502
  189. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +139 -139
  190. package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +10 -10
  191. package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +43 -43
  192. package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +30 -30
  193. package/test/examples/flappy_bird/browser-entry/runtime/README.md +59 -59
  194. package/test/examples/flappy_bird/browser-entry/visualization/README.md +276 -276
  195. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +16 -16
  196. package/test/examples/flappy_bird/constants/README.md +1070 -1070
  197. package/test/examples/flappy_bird/environment/README.md +22 -22
  198. package/test/examples/flappy_bird/evaluation/README.md +32 -32
  199. package/test/examples/flappy_bird/evaluation/rollout/README.md +141 -141
  200. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +425 -425
  201. package/test/examples/flappy_bird/simulation-shared/README.md +170 -170
  202. package/test/examples/flappy_bird/simulation-shared/observation/README.md +109 -109
  203. package/test/examples/flappy_bird/trainer/README.md +325 -325
  204. package/test/examples/flappy_bird/trainer/evaluation/README.md +74 -74
  205. package/scripts/analyze-trace.ts +0 -590
  206. package/scripts/copy-examples.mjs +0 -114
  207. package/scripts/export-onnx.mjs +0 -86
  208. package/scripts/generate-bench-tables.mjs +0 -182
  209. package/scripts/generate-docs.ts +0 -2900
  210. package/scripts/write-dist-docs-pkg.mjs +0 -16
@@ -0,0 +1,116 @@
1
+ import type { Primitive } from './analyze-trace.types.js';
2
+
3
+ /**
4
+ * Safely resolves a nested primitive value from a trace event args object.
5
+ *
6
+ * @param value - Candidate object tree.
7
+ * @param pathSegments - Nested path segments to follow.
8
+ * @returns Primitive value when the path resolves cleanly.
9
+ */
10
+ export function readNestedPrimitive(
11
+ value: unknown,
12
+ pathSegments: readonly string[],
13
+ ): Primitive {
14
+ let currentValue: unknown = value;
15
+
16
+ for (const pathSegment of pathSegments) {
17
+ if (!currentValue || typeof currentValue !== 'object') {
18
+ return undefined;
19
+ }
20
+
21
+ currentValue = (currentValue as Record<string, unknown>)[pathSegment];
22
+ }
23
+
24
+ if (
25
+ currentValue == null ||
26
+ typeof currentValue === 'string' ||
27
+ typeof currentValue === 'number' ||
28
+ typeof currentValue === 'boolean'
29
+ ) {
30
+ return currentValue;
31
+ }
32
+
33
+ return undefined;
34
+ }
35
+
36
+ /**
37
+ * Formats a duration in milliseconds for stable textual output.
38
+ *
39
+ * @param durationMs - Duration in milliseconds.
40
+ * @returns Fixed two-decimal duration string.
41
+ */
42
+ export function formatMs(durationMs: number): string {
43
+ return `${durationMs.toFixed(2)}ms`;
44
+ }
45
+
46
+ /**
47
+ * Computes a nearest-rank percentile from an already sorted numeric array.
48
+ *
49
+ * @param sortedDurations - Sorted numeric sample set.
50
+ * @param rank - Percentile rank in the inclusive range `[0, 1]`.
51
+ * @returns Percentile value or `0` when the list is empty.
52
+ */
53
+ export function percentile(
54
+ sortedDurations: readonly number[],
55
+ rank: number,
56
+ ): number {
57
+ if (sortedDurations.length === 0) {
58
+ return 0;
59
+ }
60
+
61
+ const clampedRank = Math.max(0, Math.min(1, rank));
62
+ const index = Math.min(
63
+ sortedDurations.length - 1,
64
+ Math.floor(clampedRank * (sortedDurations.length - 1)),
65
+ );
66
+
67
+ return sortedDurations[index];
68
+ }
69
+
70
+ /**
71
+ * Formats a compact percentile summary for one ascending-sorted duration collection.
72
+ *
73
+ * @param sortedDurationsMs - Ascending-sorted duration samples in milliseconds.
74
+ * @returns Human-readable summary string.
75
+ */
76
+ export function formatDistribution(
77
+ sortedDurationsMs: readonly number[],
78
+ ): string {
79
+ if (sortedDurationsMs.length === 0) {
80
+ return 'count=0';
81
+ }
82
+
83
+ return [
84
+ `count=${sortedDurationsMs.length.toLocaleString()}`,
85
+ `p50=${formatMs(percentile(sortedDurationsMs, 0.5))}`,
86
+ `p90=${formatMs(percentile(sortedDurationsMs, 0.9))}`,
87
+ `p99=${formatMs(percentile(sortedDurationsMs, 0.99))}`,
88
+ `max=${formatMs(sortedDurationsMs.at(-1) ?? 0)}`,
89
+ ].join(', ');
90
+ }
91
+
92
+ /**
93
+ * Creates a stable lookup key for one process id.
94
+ *
95
+ * @param pid - Process identifier from the trace.
96
+ * @returns Stable process key when the id exists.
97
+ */
98
+ export function createProcessKey(pid: number | undefined): string | undefined {
99
+ return typeof pid === 'number' ? `pid:${pid}` : undefined;
100
+ }
101
+
102
+ /**
103
+ * Creates a stable lookup key for one process/thread pair.
104
+ *
105
+ * @param pid - Process identifier from the trace.
106
+ * @param tid - Thread identifier from the trace.
107
+ * @returns Stable thread key when both ids exist.
108
+ */
109
+ export function createThreadKey(
110
+ pid: number | undefined,
111
+ tid: number | undefined,
112
+ ): string | undefined {
113
+ return typeof pid === 'number' && typeof tid === 'number'
114
+ ? `pid:${pid}:tid:${tid}`
115
+ : undefined;
116
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Reads a Chrome or Perfetto trace export and prints a compact performance
3
+ * report.
4
+ *
5
+ * The entrypoint stays intentionally small: CLI parsing, trace loading,
6
+ * analysis, and report rendering each live in focused sibling modules.
7
+ *
8
+ * Usage:
9
+ * npm run trace:analyze -- test/examples/flappy_bird/Trace-20260309T191949.json
10
+ * npm run docs:build-scripts && node dist-docs/scripts/analyze-trace/analyze-trace.js path/to/trace.json --top=20
11
+ */
12
+ import path from 'node:path';
13
+
14
+ import { analyzeTraceEvents } from './analyze-trace.analysis.js';
15
+ import { loadTrace, resolveCliOptions } from './analyze-trace.io.js';
16
+ import { printTraceReport } from './analyze-trace.report.js';
17
+
18
+ /**
19
+ * CLI entry point.
20
+ *
21
+ * @returns Nothing.
22
+ */
23
+ function main(): void {
24
+ // Step 1: Resolve the CLI request and load the raw trace.
25
+ const cliOptions = resolveCliOptions(process.argv.slice(2));
26
+ const traceFilePath = path.resolve(cliOptions.tracePath);
27
+ const traceFile = loadTrace(traceFilePath);
28
+ const traceEvents = traceFile.traceEvents ?? [];
29
+
30
+ if (traceEvents.length === 0) {
31
+ throw new Error(`Trace contains no events: ${traceFilePath}`);
32
+ }
33
+
34
+ // Step 2: Derive deterministic report data from the raw trace.
35
+ const traceAnalysis = analyzeTraceEvents({
36
+ traceFilePath,
37
+ traceEvents,
38
+ topCount: cliOptions.topCount,
39
+ });
40
+
41
+ // Step 3: Render the final textual report.
42
+ printTraceReport(traceAnalysis);
43
+ }
44
+
45
+ main();
@@ -0,0 +1,72 @@
1
+ /** Primitive trace metadata value supported by the nested args reader. */
2
+ export type Primitive = string | number | boolean | null | undefined;
3
+
4
+ /** Minimal Chrome or Perfetto trace event shape used by the analyzer. */
5
+ export interface TraceEvent {
6
+ pid?: number;
7
+ tid?: number;
8
+ ts?: number;
9
+ dur?: number;
10
+ tdur?: number;
11
+ ph?: string;
12
+ cat?: string;
13
+ name?: string;
14
+ args?: Record<string, unknown>;
15
+ }
16
+
17
+ /** Top-level trace file shape expected from exported JSON captures. */
18
+ export interface TraceFile {
19
+ traceEvents?: TraceEvent[];
20
+ }
21
+
22
+ /** Parsed CLI options for one analyzer invocation. */
23
+ export interface CliOptions {
24
+ tracePath: string;
25
+ topCount: number;
26
+ }
27
+
28
+ /** Summary metrics for one thread label in the trace. */
29
+ export interface ThreadSummary {
30
+ label: string;
31
+ totalDurationMs: number;
32
+ runTaskDurationMs: number;
33
+ longTaskCount16ms: number;
34
+ longTaskCount50ms: number;
35
+ maxTaskMs: number;
36
+ }
37
+
38
+ /** Aggregated duration rollup for one trace event or one script URL. */
39
+ export interface AggregatedDuration {
40
+ name: string;
41
+ count: number;
42
+ totalMs: number;
43
+ maxMs: number;
44
+ }
45
+
46
+ /** Longest single complete event entry used by the report. */
47
+ export interface LongEventSummary {
48
+ durationMs: number;
49
+ threadLabel: string;
50
+ eventName: string;
51
+ scriptUrl?: string;
52
+ }
53
+
54
+ /** Visible trace window spanned by the capture. */
55
+ export interface TraceTimeRange {
56
+ windowMs: number;
57
+ }
58
+
59
+ /** Fully analyzed trace payload used by the reporting layer. */
60
+ export interface TraceAnalysis {
61
+ traceFilePath: string;
62
+ traceEventCount: number;
63
+ timeRange: TraceTimeRange;
64
+ droppedFrameCount: number;
65
+ beginFrameCount: number;
66
+ threadSummaries: ThreadSummary[];
67
+ hottestEvents: AggregatedDuration[];
68
+ hottestFunctionCalls: AggregatedDuration[];
69
+ longestEvents: LongEventSummary[];
70
+ fireAnimationFrameDurations: number[];
71
+ functionCallDurations: number[];
72
+ }
@@ -22,14 +22,11 @@
22
22
  --double-border: 3px double rgba(15, 181, 255, 0.88);
23
23
  --double-border-soft: 3px double rgba(10, 142, 160, 0.58);
24
24
  --panel-glow:
25
- inset 0 0 0 1px rgba(15, 181, 255, 0.16),
26
- 0 0 18px rgba(15, 181, 255, 0.12);
25
+ inset 0 0 0 1px rgba(15, 181, 255, 0.16), 0 0 18px rgba(15, 181, 255, 0.12);
27
26
  --panel-glow-strong:
28
- inset 0 0 0 1px rgba(15, 181, 255, 0.2),
29
- 0 0 24px rgba(0, 229, 255, 0.15);
27
+ inset 0 0 0 1px rgba(15, 181, 255, 0.2), 0 0 24px rgba(0, 229, 255, 0.15);
30
28
  --focus-ring:
31
- 0 0 0 2px rgba(255, 154, 46, 0.9),
32
- 0 0 0 5px rgba(15, 181, 255, 0.2);
29
+ 0 0 0 2px rgba(255, 154, 46, 0.9), 0 0 0 5px rgba(15, 181, 255, 0.2);
33
30
  }
34
31
  * {
35
32
  box-sizing: border-box;
@@ -60,13 +57,35 @@ body::before {
60
57
  inset: 0;
61
58
  pointer-events: none;
62
59
  background:
63
- radial-gradient(circle at 12% 18%, rgba(255, 255, 255, 0.2) 0 1px, transparent 1.5px),
64
- radial-gradient(circle at 78% 12%, rgba(159, 220, 255, 0.18) 0 1px, transparent 1.5px),
65
- radial-gradient(circle at 68% 42%, rgba(0, 229, 255, 0.14) 0 1px, transparent 1.5px),
66
- radial-gradient(circle at 28% 72%, rgba(255, 255, 255, 0.12) 0 1px, transparent 1.5px),
60
+ radial-gradient(
61
+ circle at 12% 18%,
62
+ rgba(255, 255, 255, 0.2) 0 1px,
63
+ transparent 1.5px
64
+ ),
65
+ radial-gradient(
66
+ circle at 78% 12%,
67
+ rgba(159, 220, 255, 0.18) 0 1px,
68
+ transparent 1.5px
69
+ ),
70
+ radial-gradient(
71
+ circle at 68% 42%,
72
+ rgba(0, 229, 255, 0.14) 0 1px,
73
+ transparent 1.5px
74
+ ),
75
+ radial-gradient(
76
+ circle at 28% 72%,
77
+ rgba(255, 255, 255, 0.12) 0 1px,
78
+ transparent 1.5px
79
+ ),
67
80
  linear-gradient(rgba(10, 142, 160, 0.05) 1px, transparent 1px),
68
81
  linear-gradient(90deg, rgba(10, 142, 160, 0.04) 1px, transparent 1px);
69
- background-size: auto, auto, auto, auto, 100% 28px, 28px 100%;
82
+ background-size:
83
+ auto,
84
+ auto,
85
+ auto,
86
+ auto,
87
+ 100% 28px,
88
+ 28px 100%;
70
89
  opacity: 0.45;
71
90
  z-index: -1;
72
91
  }
@@ -89,8 +108,11 @@ a:focus-visible {
89
108
  position: sticky;
90
109
  top: 0;
91
110
  z-index: 30;
92
- background:
93
- linear-gradient(180deg, rgba(0, 0, 0, 0.96), rgba(3, 8, 18, 0.94));
111
+ background: linear-gradient(
112
+ 180deg,
113
+ rgba(0, 0, 0, 0.96),
114
+ rgba(3, 8, 18, 0.94)
115
+ );
94
116
  backdrop-filter: blur(8px);
95
117
  border-bottom: var(--double-border-soft);
96
118
  box-shadow:
@@ -225,7 +247,11 @@ a:focus-visible {
225
247
  max-height: calc(100vh - 90px);
226
248
  overflow: auto;
227
249
  background:
228
- radial-gradient(circle at top left, rgba(0, 229, 255, 0.08), transparent 36%),
250
+ radial-gradient(
251
+ circle at top left,
252
+ rgba(0, 229, 255, 0.08),
253
+ transparent 36%
254
+ ),
229
255
  linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
230
256
  border: var(--double-border);
231
257
  border-radius: 0;
@@ -281,14 +307,21 @@ a:focus-visible {
281
307
  position: relative;
282
308
  padding: 12px;
283
309
  border: var(--double-border-soft);
284
- background:
285
- linear-gradient(180deg, rgba(3, 17, 31, 0.96), rgba(0, 0, 0, 0.98));
310
+ background: linear-gradient(
311
+ 180deg,
312
+ rgba(3, 17, 31, 0.96),
313
+ rgba(0, 0, 0, 0.98)
314
+ );
286
315
  box-shadow: var(--panel-glow);
287
316
  }
288
317
  .group-priority {
289
318
  padding: 12px;
290
319
  background:
291
- radial-gradient(circle at top left, rgba(255, 154, 46, 0.08), transparent 34%),
320
+ radial-gradient(
321
+ circle at top left,
322
+ rgba(255, 154, 46, 0.08),
323
+ transparent 34%
324
+ ),
292
325
  linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
293
326
  border: var(--double-border);
294
327
  box-shadow: var(--panel-glow-strong);
@@ -318,7 +351,11 @@ a:focus-visible {
318
351
  border-radius: 0;
319
352
  border: var(--double-border-soft);
320
353
  background:
321
- radial-gradient(circle at top left, rgba(0, 229, 255, 0.08), transparent 40%),
354
+ radial-gradient(
355
+ circle at top left,
356
+ rgba(0, 229, 255, 0.08),
357
+ transparent 40%
358
+ ),
322
359
  linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
323
360
  box-shadow: var(--panel-glow);
324
361
  }
@@ -432,7 +469,11 @@ a:focus-visible {
432
469
  bottom: -1px;
433
470
  width: var(--pipe-rail-thickness);
434
471
  transform: translateX(-50%);
435
- background: linear-gradient(180deg, rgba(15, 181, 255, 0.94), rgba(10, 142, 160, 0.78));
472
+ background: linear-gradient(
473
+ 180deg,
474
+ rgba(15, 181, 255, 0.94),
475
+ rgba(10, 142, 160, 0.78)
476
+ );
436
477
  box-shadow: 0 0 8px rgba(0, 229, 255, 0.16);
437
478
  transition:
438
479
  background 120ms ease,
@@ -446,7 +487,11 @@ a:focus-visible {
446
487
  top: 50%;
447
488
  height: var(--pipe-rail-thickness);
448
489
  transform: translateY(-50%);
449
- background: linear-gradient(90deg, rgba(15, 181, 255, 0.94), rgba(10, 142, 160, 0.78));
490
+ background: linear-gradient(
491
+ 90deg,
492
+ rgba(15, 181, 255, 0.94),
493
+ rgba(10, 142, 160, 0.78)
494
+ );
450
495
  box-shadow: 0 0 8px rgba(0, 229, 255, 0.16);
451
496
  transition:
452
497
  background 120ms ease,
@@ -479,7 +524,11 @@ a:focus-visible {
479
524
  .pipe-nav-item:hover .nav-rail-cell-last::after,
480
525
  .pipe-nav-item.current .nav-rail-cell::before,
481
526
  .pipe-nav-item.current .nav-rail-cell-last::after {
482
- background: linear-gradient(180deg, rgba(255, 154, 46, 0.98), rgba(255, 92, 255, 0.72));
527
+ background: linear-gradient(
528
+ 180deg,
529
+ rgba(255, 154, 46, 0.98),
530
+ rgba(255, 92, 255, 0.72)
531
+ );
483
532
  box-shadow: 0 0 10px rgba(255, 154, 46, 0.22);
484
533
  }
485
534
  .pipe-nav-item:hover .nav-link-label,
@@ -631,7 +680,11 @@ table {
631
680
  display: block;
632
681
  overflow: auto;
633
682
  border: var(--double-border-soft);
634
- background: linear-gradient(180deg, rgba(3, 17, 31, 0.98), rgba(0, 0, 0, 0.98));
683
+ background: linear-gradient(
684
+ 180deg,
685
+ rgba(3, 17, 31, 0.98),
686
+ rgba(0, 0, 0, 0.98)
687
+ );
635
688
  box-shadow: var(--panel-glow);
636
689
  }
637
690
  th,
@@ -649,7 +702,11 @@ blockquote {
649
702
  padding: 12px 14px;
650
703
  border-left: 3px double var(--accent-orange);
651
704
  background:
652
- radial-gradient(circle at top left, rgba(255, 92, 255, 0.08), transparent 36%),
705
+ radial-gradient(
706
+ circle at top left,
707
+ rgba(255, 92, 255, 0.08),
708
+ transparent 36%
709
+ ),
653
710
  linear-gradient(180deg, rgba(12, 8, 24, 0.98), rgba(0, 0, 0, 0.98));
654
711
  border-radius: 0;
655
712
  box-shadow: var(--panel-glow);
@@ -0,0 +1,239 @@
1
+ /*
2
+ * Copies browser-viewable examples into docs/examples/* so GitHub Pages can
3
+ * publish them alongside the generated documentation site.
4
+ *
5
+ * The script intentionally keeps one narrow contract: copy each example's
6
+ * browser entrypoint into its published docs folder, then rebuild the shared
7
+ * examples landing page from the demos that were actually published.
8
+ */
9
+
10
+ import { access, copyFile, mkdir, writeFile } from 'node:fs/promises';
11
+ import path from 'node:path';
12
+
13
+ const DOCS_EXAMPLES_LOG_PREFIX = '[docs:examples]';
14
+ const DOCS_EXAMPLES_DIR = path.resolve('docs', 'examples');
15
+ const EXAMPLE_ENTRY_FILE_NAME = 'index.html';
16
+ const DOCS_THEME_STYLESHEET_PATH = '../assets/theme.css';
17
+ const DOCS_HOME_PATH = '../index.html';
18
+ const EXAMPLES_HOME_PATH = './index.html';
19
+ const GITHUB_REPOSITORY_URL = 'https://github.com/reicek/NeatapticTS';
20
+
21
+ interface ExampleDefinition {
22
+ dirName: string;
23
+ label: string;
24
+ title: string;
25
+ sourceDir: string;
26
+ }
27
+
28
+ interface PublishedExample {
29
+ dirName: string;
30
+ label: string;
31
+ title: string;
32
+ }
33
+
34
+ const EXAMPLE_DEFINITIONS: readonly ExampleDefinition[] = [
35
+ {
36
+ dirName: 'asciiMaze',
37
+ label: 'asciiMaze',
38
+ title: 'ASCII Maze (NeatapticTS)',
39
+ sourceDir: path.resolve('test', 'examples', 'asciiMaze'),
40
+ },
41
+ {
42
+ dirName: 'flappy_bird',
43
+ label: 'flappy_bird',
44
+ title: 'Flappy Bird (NeatapticTS)',
45
+ sourceDir: path.resolve('test', 'examples', 'flappy_bird'),
46
+ },
47
+ ];
48
+
49
+ /**
50
+ * Runs the example-copy workflow.
51
+ *
52
+ * Step 1 copies published browser entrypoints for all known examples.
53
+ * Step 2 rebuilds the landing page from the demos that were actually copied.
54
+ *
55
+ * @returns Promise resolved when copying and landing-page generation complete.
56
+ */
57
+ async function main(): Promise<void> {
58
+ const publishedExamples = (
59
+ await Promise.all(EXAMPLE_DEFINITIONS.map(copyExampleEntryPoint))
60
+ ).filter(isPublishedExample);
61
+
62
+ await writeExamplesLandingPage(publishedExamples);
63
+ }
64
+
65
+ /**
66
+ * Copies one example entrypoint into `docs/examples/<name>/index.html`.
67
+ *
68
+ * @param exampleDefinition - Example publishing definition.
69
+ * @returns Published example metadata when the example entrypoint was copied,
70
+ * otherwise `null`.
71
+ */
72
+ async function copyExampleEntryPoint(
73
+ exampleDefinition: ExampleDefinition,
74
+ ): Promise<PublishedExample | null> {
75
+ const hasSourceDirectory = await pathExists(exampleDefinition.sourceDir);
76
+ if (!hasSourceDirectory) {
77
+ console.warn(
78
+ `${DOCS_EXAMPLES_LOG_PREFIX} ${exampleDefinition.dirName} source directory not found, skipping`,
79
+ );
80
+ return null;
81
+ }
82
+
83
+ const sourceIndexPath = path.join(
84
+ exampleDefinition.sourceDir,
85
+ EXAMPLE_ENTRY_FILE_NAME,
86
+ );
87
+ const hasSourceIndex = await pathExists(sourceIndexPath);
88
+ if (!hasSourceIndex) {
89
+ console.warn(
90
+ `${DOCS_EXAMPLES_LOG_PREFIX} ${exampleDefinition.dirName} ${EXAMPLE_ENTRY_FILE_NAME} missing`,
91
+ );
92
+ return null;
93
+ }
94
+
95
+ const destinationDirectoryPath = path.join(
96
+ DOCS_EXAMPLES_DIR,
97
+ exampleDefinition.dirName,
98
+ );
99
+ const destinationIndexPath = path.join(
100
+ destinationDirectoryPath,
101
+ EXAMPLE_ENTRY_FILE_NAME,
102
+ );
103
+
104
+ await mkdir(destinationDirectoryPath, { recursive: true });
105
+ await copyFile(sourceIndexPath, destinationIndexPath);
106
+
107
+ console.log(
108
+ `${DOCS_EXAMPLES_LOG_PREFIX} Copied ${exampleDefinition.dirName} ${EXAMPLE_ENTRY_FILE_NAME}`,
109
+ );
110
+
111
+ return {
112
+ dirName: exampleDefinition.dirName,
113
+ label: exampleDefinition.label,
114
+ title: exampleDefinition.title,
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Writes the shared docs/examples landing page.
120
+ *
121
+ * @param publishedExamples - Examples that were published in the current run.
122
+ * @returns Promise resolved when the landing page has been written.
123
+ */
124
+ async function writeExamplesLandingPage(
125
+ publishedExamples: readonly PublishedExample[],
126
+ ): Promise<void> {
127
+ await mkdir(DOCS_EXAMPLES_DIR, { recursive: true });
128
+
129
+ const examplesLandingPageHtml =
130
+ buildExamplesLandingPageHtml(publishedExamples);
131
+ await writeFile(
132
+ path.join(DOCS_EXAMPLES_DIR, EXAMPLE_ENTRY_FILE_NAME),
133
+ examplesLandingPageHtml,
134
+ 'utf8',
135
+ );
136
+
137
+ console.log(`${DOCS_EXAMPLES_LOG_PREFIX} Wrote examples landing page`);
138
+ }
139
+
140
+ /**
141
+ * Builds the shared examples landing page HTML.
142
+ *
143
+ * @param publishedExamples - Examples that were published in the current run.
144
+ * @returns Rendered landing page HTML.
145
+ */
146
+ function buildExamplesLandingPageHtml(
147
+ publishedExamples: readonly PublishedExample[],
148
+ ): string {
149
+ const examplesListMarkup = buildExamplesListMarkup(publishedExamples);
150
+
151
+ return `<!doctype html>
152
+ <html lang="en">
153
+ <head>
154
+ <meta charset="utf-8" />
155
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
156
+ <title>Examples • NeatapticTS Docs</title>
157
+ <link rel="stylesheet" href="${DOCS_THEME_STYLESHEET_PATH}" />
158
+ </head>
159
+ <body>
160
+ <header class="topbar">
161
+ <div class="inner">
162
+ <div class="brand"><a href="${DOCS_HOME_PATH}">NeatapticTS</a></div>
163
+ <nav class="main-nav">
164
+ <a href="${DOCS_HOME_PATH}">Home</a>
165
+ <a href="${DOCS_HOME_PATH}">Docs</a>
166
+ <a href="${EXAMPLES_HOME_PATH}" class="active">Examples</a>
167
+ <a href="${GITHUB_REPOSITORY_URL}" target="_blank" rel="noopener">GitHub</a>
168
+ </nav>
169
+ </div>
170
+ </header>
171
+ <div class="layout">
172
+ <main class="content">
173
+ <h1>Examples</h1>
174
+ <p>Interactive browser demos built from this repository:</p>
175
+ ${examplesListMarkup}
176
+ <footer class="site-footer">Generated from source JSDoc • <a href="${GITHUB_REPOSITORY_URL}">GitHub</a></footer>
177
+ </main>
178
+ </div>
179
+ </body>
180
+ </html>`;
181
+ }
182
+
183
+ /**
184
+ * Builds the published examples list markup.
185
+ *
186
+ * @param publishedExamples - Examples that were published in the current run.
187
+ * @returns HTML list markup.
188
+ */
189
+ function buildExamplesListMarkup(
190
+ publishedExamples: readonly PublishedExample[],
191
+ ): string {
192
+ if (publishedExamples.length === 0) {
193
+ return '<p>No interactive examples were published in this run.</p>';
194
+ }
195
+
196
+ const listItemsMarkup = publishedExamples
197
+ .toSorted((leftExample, rightExample) =>
198
+ leftExample.title.localeCompare(rightExample.title),
199
+ )
200
+ .map(
201
+ (publishedExample) =>
202
+ `<li><a href="./${publishedExample.dirName}/${EXAMPLE_ENTRY_FILE_NAME}">${publishedExample.title}</a> <span class="demo-path">(examples/${publishedExample.label})</span></li>`,
203
+ )
204
+ .join('');
205
+
206
+ return `<ul>${listItemsMarkup}</ul>`;
207
+ }
208
+
209
+ /**
210
+ * Resolves whether a filesystem path exists.
211
+ *
212
+ * @param targetPath - Filesystem path to check.
213
+ * @returns True when the path exists.
214
+ */
215
+ async function pathExists(targetPath: string): Promise<boolean> {
216
+ try {
217
+ await access(targetPath);
218
+ return true;
219
+ } catch {
220
+ return false;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Narrows `Promise.all` output to the published example shape.
226
+ *
227
+ * @param publishedExample - Candidate example result.
228
+ * @returns `true` when the example was published successfully.
229
+ */
230
+ function isPublishedExample(
231
+ publishedExample: PublishedExample | null,
232
+ ): publishedExample is PublishedExample {
233
+ return publishedExample !== null;
234
+ }
235
+
236
+ main().catch((error: unknown) => {
237
+ console.error(`${DOCS_EXAMPLES_LOG_PREFIX} Failed:`, error);
238
+ process.exit(1);
239
+ });