create-slide-deck 0.1.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.
Files changed (105) hide show
  1. package/dist/index.js +119 -0
  2. package/package.json +36 -0
  3. package/template-full/README.md +99 -0
  4. package/template-full/package.json +47 -0
  5. package/template-full/src/reveal/components/auto-layout.ts +229 -0
  6. package/template-full/src/reveal/components/charts.tsx +213 -0
  7. package/template-full/src/reveal/core/blocks.ts +172 -0
  8. package/template-full/src/reveal/core/deck-init.ts +60 -0
  9. package/template-full/src/reveal/core/design.ts +46 -0
  10. package/template-full/src/reveal/core/layout.ts +187 -0
  11. package/template-full/src/reveal/core/mount-registry.ts +41 -0
  12. package/template-full/src/reveal/core/presets.ts +189 -0
  13. package/template-full/src/reveal/core/runtime.ts +141 -0
  14. package/template-full/src/reveal/core/types.ts +114 -0
  15. package/template-full/src/reveal/data/algorithms.ts +78 -0
  16. package/template-full/src/reveal/data/benchmark.ts +79 -0
  17. package/template-full/src/reveal/decks/demo-showcase/components/demo-arc-progress.tsx +153 -0
  18. package/template-full/src/reveal/decks/demo-showcase/components/demo-before-after.tsx +164 -0
  19. package/template-full/src/reveal/decks/demo-showcase/components/demo-bigtext.tsx +70 -0
  20. package/template-full/src/reveal/decks/demo-showcase/components/demo-card-flip.tsx +118 -0
  21. package/template-full/src/reveal/decks/demo-showcase/components/demo-chat-bubbles.tsx +257 -0
  22. package/template-full/src/reveal/decks/demo-showcase/components/demo-code.tsx +136 -0
  23. package/template-full/src/reveal/decks/demo-showcase/components/demo-concept-map.tsx +336 -0
  24. package/template-full/src/reveal/decks/demo-showcase/components/demo-counter.tsx +194 -0
  25. package/template-full/src/reveal/decks/demo-showcase/components/demo-cover.tsx +188 -0
  26. package/template-full/src/reveal/decks/demo-showcase/components/demo-dark-dashboard.tsx +166 -0
  27. package/template-full/src/reveal/decks/demo-showcase/components/demo-eval-matrix.tsx +191 -0
  28. package/template-full/src/reveal/decks/demo-showcase/components/demo-force-graph.tsx +169 -0
  29. package/template-full/src/reveal/decks/demo-showcase/components/demo-fullbleed-bars.tsx +109 -0
  30. package/template-full/src/reveal/decks/demo-showcase/components/demo-fullbleed-flow.tsx +177 -0
  31. package/template-full/src/reveal/decks/demo-showcase/components/demo-heatmap.tsx +135 -0
  32. package/template-full/src/reveal/decks/demo-showcase/components/demo-icon-wall.tsx +143 -0
  33. package/template-full/src/reveal/decks/demo-showcase/components/demo-math.tsx +103 -0
  34. package/template-full/src/reveal/decks/demo-showcase/components/demo-number-morph.tsx +126 -0
  35. package/template-full/src/reveal/decks/demo-showcase/components/demo-path.tsx +185 -0
  36. package/template-full/src/reveal/decks/demo-showcase/components/demo-radar.tsx +124 -0
  37. package/template-full/src/reveal/decks/demo-showcase/components/demo-rough.tsx +169 -0
  38. package/template-full/src/reveal/decks/demo-showcase/components/demo-sankey.tsx +144 -0
  39. package/template-full/src/reveal/decks/demo-showcase/components/demo-screenshot-annotate.tsx +181 -0
  40. package/template-full/src/reveal/decks/demo-showcase/components/demo-stacked-cards.tsx +159 -0
  41. package/template-full/src/reveal/decks/demo-showcase/components/demo-tabs.tsx +206 -0
  42. package/template-full/src/reveal/decks/demo-showcase/components/demo-timeline.tsx +162 -0
  43. package/template-full/src/reveal/decks/demo-showcase/components/demo-treemap.tsx +161 -0
  44. package/template-full/src/reveal/decks/demo-showcase/components/demo-zoom-focus.tsx +223 -0
  45. package/template-full/src/reveal/decks/demo-showcase/components/registry.ts +63 -0
  46. package/template-full/src/reveal/decks/demo-showcase/demo.css +237 -0
  47. package/template-full/src/reveal/decks/demo-showcase/index.html +24 -0
  48. package/template-full/src/reveal/decks/demo-showcase/main.ts +7 -0
  49. package/template-full/src/reveal/decks/demo-showcase/slides.ts +271 -0
  50. package/template-full/src/reveal/decks/fse26-rca/components/aws-cascade.tsx +295 -0
  51. package/template-full/src/reveal/decks/fse26-rca/components/bench-compare.tsx +64 -0
  52. package/template-full/src/reveal/decks/fse26-rca/components/bench-deficiency.tsx +104 -0
  53. package/template-full/src/reveal/decks/fse26-rca/components/bench-loop.tsx +402 -0
  54. package/template-full/src/reveal/decks/fse26-rca/components/bench-needs.tsx +78 -0
  55. package/template-full/src/reveal/decks/fse26-rca/components/closing-takeaway.tsx +165 -0
  56. package/template-full/src/reveal/decks/fse26-rca/components/cloud-incidents.tsx +88 -0
  57. package/template-full/src/reveal/decks/fse26-rca/components/failure-modes.tsx +59 -0
  58. package/template-full/src/reveal/decks/fse26-rca/components/fault-heatmap.tsx +85 -0
  59. package/template-full/src/reveal/decks/fse26-rca/components/hierarchy-tree.tsx +93 -0
  60. package/template-full/src/reveal/decks/fse26-rca/components/incident-hard.tsx +72 -0
  61. package/template-full/src/reveal/decks/fse26-rca/components/rca-pipeline.tsx +193 -0
  62. package/template-full/src/reveal/decks/fse26-rca/components/registry.ts +37 -0
  63. package/template-full/src/reveal/decks/fse26-rca/components/simple-rca.tsx +216 -0
  64. package/template-full/src/reveal/decks/fse26-rca/components/sota-collapse.tsx +63 -0
  65. package/template-full/src/reveal/decks/fse26-rca/components/srca-results.tsx +115 -0
  66. package/template-full/src/reveal/decks/fse26-rca/images/aws-outage-2025-deployflow.png +0 -0
  67. package/template-full/src/reveal/decks/fse26-rca/images/aws-post-event-summary.png +0 -0
  68. package/template-full/src/reveal/decks/fse26-rca/images/bbc-crowdstrike.png +0 -0
  69. package/template-full/src/reveal/decks/fse26-rca/images/cnn-meta-outage-2021.png +0 -0
  70. package/template-full/src/reveal/decks/fse26-rca/images/cover.png +0 -0
  71. package/template-full/src/reveal/decks/fse26-rca/images/nyt-facebook-2021.png +0 -0
  72. package/template-full/src/reveal/decks/fse26-rca/images/qr-repo.png +0 -0
  73. package/template-full/src/reveal/decks/fse26-rca/images/verge-crowdstrike-2024.png +0 -0
  74. package/template-full/src/reveal/decks/fse26-rca/images/wiki-meta-outage-2021.png +0 -0
  75. package/template-full/src/reveal/decks/fse26-rca/index.html +30 -0
  76. package/template-full/src/reveal/decks/fse26-rca/main.ts +8 -0
  77. package/template-full/src/reveal/decks/fse26-rca/slides.ts +175 -0
  78. package/template-full/src/reveal/env.d.ts +38 -0
  79. package/template-full/src/reveal/theme.css +762 -0
  80. package/template-full/src/reveal/tools/dev.mjs +120 -0
  81. package/template-full/src/reveal/tools/export-pdf.mjs +86 -0
  82. package/template-full/src/reveal/tools/preview.mjs +132 -0
  83. package/template-full/tsconfig.json +19 -0
  84. package/template-full/vite.config.ts +95 -0
  85. package/template-minimal/package.json +42 -0
  86. package/template-minimal/src/reveal/components/auto-layout.ts +229 -0
  87. package/template-minimal/src/reveal/components/charts.tsx +213 -0
  88. package/template-minimal/src/reveal/core/blocks.ts +172 -0
  89. package/template-minimal/src/reveal/core/deck-init.ts +60 -0
  90. package/template-minimal/src/reveal/core/design.ts +46 -0
  91. package/template-minimal/src/reveal/core/layout.ts +187 -0
  92. package/template-minimal/src/reveal/core/mount-registry.ts +41 -0
  93. package/template-minimal/src/reveal/core/presets.ts +189 -0
  94. package/template-minimal/src/reveal/core/runtime.ts +141 -0
  95. package/template-minimal/src/reveal/core/types.ts +114 -0
  96. package/template-minimal/src/reveal/data/.gitkeep +0 -0
  97. package/template-minimal/src/reveal/decks/my-deck/components/example-component.tsx +28 -0
  98. package/template-minimal/src/reveal/decks/my-deck/components/registry.ts +9 -0
  99. package/template-minimal/src/reveal/decks/my-deck/index.html +14 -0
  100. package/template-minimal/src/reveal/decks/my-deck/main.ts +5 -0
  101. package/template-minimal/src/reveal/decks/my-deck/slides.ts +34 -0
  102. package/template-minimal/src/reveal/env.d.ts +38 -0
  103. package/template-minimal/src/reveal/theme.css +762 -0
  104. package/template-minimal/tsconfig.json +19 -0
  105. package/template-minimal/vite.config.ts +95 -0
@@ -0,0 +1,193 @@
1
+ import React from "react";
2
+ import type { SlideComponentProps } from "../../../core/types.ts";
3
+ import { findEmptySlot, nodeOccupiedRects } from "../../../components/auto-layout.ts";
4
+ import { NODE, DETAIL, EDGE, CANVAS, annotationStyle, illustrationStyle, annotationTitleStyle, vis, visExact, SlideCanvas } from "../../../core/presets.ts";
5
+
6
+ const NODES = [
7
+ { id: "failure", label: "Failure", sub: "user-visible", color: "#C00000", col: 0, row: 0, step: 0 },
8
+ { id: "metrics", label: "Metrics", sub: "time-series values", color: "#00A6D6", col: 1, row: 0, step: 1 },
9
+ { id: "logs", label: "Logs", sub: "event text", color: "#1B556B", col: 1, row: 1, step: 2 },
10
+ { id: "traces", label: "Traces", sub: "request spans", color: "#76B82A", col: 1, row: 2, step: 3 },
11
+ { id: "model", label: "RCA Model", sub: "graph / ML / causal",color: "#E8912D", col: 2, row: 1, step: 4 },
12
+ { id: "rank", label: "Top-K", sub: "ranked causes", color: "#C00000", col: 3, row: 1, step: 5 },
13
+ ];
14
+
15
+ const ICON_PATHS = {
16
+ failure: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4m0 4h.01",
17
+ metrics: "M3 3v18h18M7 16l4-8 4 4 4-6",
18
+ logs: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M8 13h8M8 17h8M8 9h2",
19
+ traces: "M3 12h4l3-9 4 18 3-9h4",
20
+ model: "M12 2a8 8 0 0 0-8 8c0 6 8 12 8 12s8-6 8-12a8 8 0 0 0-8-8zm0 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6z",
21
+ rank: "M9 11l3 3 8-8M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11",
22
+ };
23
+
24
+ const DETAILS = [
25
+ null,
26
+ { vb: "0 0 280 100",
27
+ illustration: (
28
+ <g>
29
+ <line x1="10" y1="70" x2="190" y2="70" stroke="#ddd" strokeWidth="1.5" />
30
+ <line x1="10" y1="70" x2="10" y2="10" stroke="#ddd" strokeWidth="1.5" />
31
+ <polyline points="20,55 50,52 80,48 100,30 110,15 130,35 160,40 180,45" fill="none" stroke="#00A6D6" strokeWidth="2.5" />
32
+ <circle cx="110" cy="15" r="4" fill="#C00000" />
33
+ <text x="110" y="8" textAnchor="middle" fontSize="14" fill="#C00000" fontWeight="800">spike</text>
34
+ <text x="100" y="78" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">time</text>
35
+ </g>
36
+ ),
37
+ lines: ["Numeric values over time: CPU, latency, error rate ..."],
38
+ },
39
+ { vb: "0 0 300 90",
40
+ illustration: (
41
+ <g>
42
+ <rect x="4" y="4" width="212" height="14" rx="3" fill="#F5F5F5" stroke="#ddd" strokeWidth="1" />
43
+ <text x="8" y="14" fontSize="14" fill="#999" fontWeight="600">10:03:21 INFO request completed 200</text>
44
+ <rect x="4" y="22" width="212" height="14" rx="3" fill="#F5F5F5" stroke="#ddd" strokeWidth="1" />
45
+ <text x="8" y="32" fontSize="14" fill="#999" fontWeight="600">10:03:22 WARN latency exceeded 500ms</text>
46
+ <rect x="4" y="40" width="212" height="14" rx="3" fill="#FFF3F3" stroke="#C00000" strokeWidth="1.5" />
47
+ <text x="8" y="50" fontSize="14" fill="#C00000" fontWeight="800">10:03:23 ERROR connection refused db-svc</text>
48
+ <rect x="4" y="58" width="212" height="14" rx="3" fill="#F5F5F5" stroke="#ddd" strokeWidth="1" />
49
+ <text x="8" y="68" fontSize="14" fill="#999" fontWeight="600">10:03:24 INFO retry attempt 1/3</text>
50
+ </g>
51
+ ),
52
+ lines: ["Structured event text: errors, warnings, exceptions ..."],
53
+ },
54
+ { vb: "0 0 300 90",
55
+ illustration: (
56
+ <g>
57
+ <rect x="4" y="8" width="80" height="12" rx="3" fill="#00A6D6" opacity="0.3" />
58
+ <text x="44" y="17" textAnchor="middle" fontSize="14" fill="#00739A" fontWeight="700">API gateway</text>
59
+ <rect x="30" y="26" width="70" height="12" rx="3" fill="#76B82A" opacity="0.3" />
60
+ <text x="65" y="35" textAnchor="middle" fontSize="14" fill="#4A7A10" fontWeight="700">order-svc</text>
61
+ <rect x="56" y="44" width="90" height="12" rx="3" fill="#C00000" opacity="0.3" />
62
+ <text x="101" y="53" textAnchor="middle" fontSize="14" fill="#C00000" fontWeight="700">db-svc (timeout)</text>
63
+ <line x1="84" y1="14" x2="100" y2="14" stroke="#ddd" strokeWidth="1" strokeDasharray="3 2" />
64
+ <line x1="100" y1="32" x2="146" y2="32" stroke="#ddd" strokeWidth="1" strokeDasharray="3 2" />
65
+ </g>
66
+ ),
67
+ lines: ["Request path across services: which span is slow or failing"],
68
+ },
69
+ null,
70
+ null,
71
+ ];
72
+
73
+ const COL_W = 180;
74
+ const ROW_H = 110;
75
+ const GAP_X = 120;
76
+ const GAP_Y = 16;
77
+ const PAD_X = 30;
78
+ const PAD_Y = 20;
79
+ const ICON_R = NODE.radius - 8;
80
+ const DETAIL_W = DETAIL.width + 40;
81
+ const DETAIL_H = DETAIL.height + 20;
82
+
83
+ function nx(col: number) { return PAD_X + col * (COL_W + GAP_X) + COL_W / 2; }
84
+ function ny(row: number) { return PAD_Y + row * (ROW_H + GAP_Y) + ICON_R + 8; }
85
+
86
+ const LINKS = [
87
+ { from: 0, to: 1, step: 1 },
88
+ { from: 0, to: 2, step: 2 },
89
+ { from: 0, to: 3, step: 3 },
90
+ { from: 1, to: 4, step: 4 },
91
+ { from: 2, to: 4, step: 4 },
92
+ { from: 3, to: 4, step: 4 },
93
+ { from: 4, to: 5, step: 5 },
94
+ ];
95
+
96
+ // vis, visExact imported from presets
97
+
98
+ function arrowPath(x1: number, y1: number, x2: number, y2: number) {
99
+ const mid = (x1 + x2) / 2;
100
+ return `M${x1},${y1} C${mid},${y1} ${mid},${y2} ${x2},${y2}`;
101
+ }
102
+
103
+ function anchorPt(n: (typeof NODES)[number], side: string) {
104
+ const cx = nx(n.col), cy = ny(n.row);
105
+ const r = ICON_R;
106
+ switch (side) {
107
+ case "right": return [cx + r + 6, cy];
108
+ case "left": return [cx - r - 6, cy];
109
+ default: return [cx, cy];
110
+ }
111
+ }
112
+
113
+ function NodeIcon({ node, step, instant }: { node: (typeof NODES)[number]; step: number; instant: boolean }) {
114
+ const cx = nx(node.col), cy = ny(node.row);
115
+ const r = ICON_R;
116
+ return (
117
+ <g style={vis(step, node.step, instant)}>
118
+ <circle cx={cx} cy={cy} r={r + 5} fill={node.color + "11"} />
119
+ <circle cx={cx} cy={cy} r={r} fill="white" stroke={node.color} strokeWidth={NODE.strokeWidth} />
120
+ <g transform={`translate(${cx - NODE.iconSize/2}, ${cy - NODE.iconSize/2})`}>
121
+ <svg width={NODE.iconSize} height={NODE.iconSize} viewBox="0 0 24 24" fill="none"
122
+ stroke={node.color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
123
+ <path d={(ICON_PATHS as any)[node.id]} />
124
+ </svg>
125
+ </g>
126
+ <text x={cx} y={cy + r + 20} textAnchor="middle" fontSize={NODE.labelFont} fontWeight="900"
127
+ fill={NODE.labelColor} fontFamily={CANVAS.fontFamily}>{node.label}</text>
128
+ <text x={cx} y={cy + r + 38} textAnchor="middle" fontSize={NODE.subFont} fontWeight="700"
129
+ fill={node.color} fontFamily={CANVAS.fontFamily}>{node.sub}</text>
130
+ </g>
131
+ );
132
+ }
133
+
134
+ function DetailAnnotation({ detail, step, currentStep, svgW, svgH, instant }: { detail: (typeof DETAILS)[number]; step: number; currentStep: number; svgW: number; svgH: number; instant: boolean }) {
135
+ if (!detail) return null;
136
+ const node = NODES[step];
137
+ const occupied = nodeOccupiedRects(NODES, step, { nx, ny, iconR: ICON_R, labelH: 44 });
138
+ const pos = findEmptySlot({
139
+ canvasW: svgW, canvasH: svgH,
140
+ occupied, slotW: DETAIL_W, slotH: DETAIL_H,
141
+ margin: DETAIL.margin,
142
+ near: { x: nx(node.col), y: ny(node.row) },
143
+ step: DETAIL.scanStep,
144
+ });
145
+ return (
146
+ <g style={visExact(currentStep, step, instant)}>
147
+ <rect x={pos.x} y={pos.y} width={DETAIL_W} height={DETAIL_H} rx="12"
148
+ fill="white" fillOpacity={DETAIL.bgOpacity} stroke="none" />
149
+ <foreignObject x={pos.x} y={pos.y} width={DETAIL_W} height={DETAIL_H}>
150
+ <div style={annotationStyle()}>
151
+ <svg style={illustrationStyle()} viewBox={detail.vb}>
152
+ {detail.illustration}
153
+ </svg>
154
+ <div style={{ flex: "1 1 auto", display: "flex", flexDirection: "column", justifyContent: "center" }}>
155
+ <div style={annotationTitleStyle()}>
156
+ {detail.lines[0]}
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </foreignObject>
161
+ </g>
162
+ );
163
+ }
164
+
165
+ const SVG_W = 4 * COL_W + 3 * GAP_X + 2 * PAD_X;
166
+ const SVG_H = 3 * ROW_H + 2 * GAP_Y + 2 * PAD_Y + 60;
167
+
168
+ // getStep imported from presets
169
+
170
+ export default function RcaPipeline({ Reveal }: SlideComponentProps) {
171
+ return (
172
+ <SlideCanvas Reveal={Reveal} W={SVG_W} H={SVG_H}>{(step, instant) => <>
173
+ <defs>
174
+ <marker id="pipe-arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
175
+ <path d="M 0 0 L 10 5 L 0 10 z" fill="#BFBFBF" />
176
+ </marker>
177
+ </defs>
178
+
179
+ {LINKS.map((link, i) => {
180
+ const from = NODES[link.from], to = NODES[link.to];
181
+ const p1 = anchorPt(from, "right");
182
+ const p2 = anchorPt(to, "left");
183
+ return <path key={i} d={arrowPath(p1[0], p1[1], p2[0], p2[1])}
184
+ fill="none" stroke="#BFBFBF" strokeWidth="2.5" strokeLinecap="round"
185
+ markerEnd="url(#pipe-arr)" style={vis(step, link.step, instant)} />;
186
+ })}
187
+
188
+ {NODES.map((n) => <NodeIcon key={n.id} node={n} step={step} instant={instant} />)}
189
+
190
+ {DETAILS.map((d, i) => d && <DetailAnnotation key={i} detail={d} step={i} currentStep={step} svgW={SVG_W} svgH={SVG_H} instant={instant} />)}
191
+ </>}</SlideCanvas>
192
+ );
193
+ }
@@ -0,0 +1,37 @@
1
+ import type { ComponentType } from "react";
2
+ import type { SlideComponentProps } from "../../../core/types.ts";
3
+ import AwsCascade from "./aws-cascade.tsx";
4
+ import RcaPipeline from "./rca-pipeline.tsx";
5
+ import SimpleRca from "./simple-rca.tsx";
6
+ import SrcaResults from "./srca-results.tsx";
7
+ import BenchDeficiency from "./bench-deficiency.tsx";
8
+ import BenchLoop from "./bench-loop.tsx";
9
+ import HierarchyTree from "./hierarchy-tree.tsx";
10
+ import BenchCompare from "./bench-compare.tsx";
11
+ import SotaCollapse from "./sota-collapse.tsx";
12
+ import FaultHeatmap from "./fault-heatmap.tsx";
13
+ import FailureModes from "./failure-modes.tsx";
14
+ import ClosingTakeaway from "./closing-takeaway.tsx";
15
+ import IncidentHard from "./incident-hard.tsx";
16
+ import BenchNeeds from "./bench-needs.tsx";
17
+ import CloudIncidents from "./cloud-incidents.tsx";
18
+
19
+ export const components = {
20
+ AwsCascade,
21
+ RcaPipeline,
22
+ SimpleRca,
23
+ SrcaResults,
24
+ BenchDeficiency,
25
+ BenchLoop,
26
+ HierarchyTree,
27
+ BenchCompare,
28
+ SotaCollapse,
29
+ FaultHeatmap,
30
+ FailureModes,
31
+ ClosingTakeaway,
32
+ IncidentHard,
33
+ BenchNeeds,
34
+ CloudIncidents,
35
+ } satisfies Record<string, ComponentType<SlideComponentProps>>;
36
+
37
+ export type ComponentName = keyof typeof components;
@@ -0,0 +1,216 @@
1
+ import React from "react";
2
+ import type { SlideComponentProps } from "../../../core/types.ts";
3
+ import { findEmptySlot, nodeOccupiedRects } from "../../../components/auto-layout.ts";
4
+ import { NODE, DETAIL, EDGE, CANVAS, annotationStyle, illustrationStyle, annotationTitleStyle, vis, visExact, SlideCanvas } from "../../../core/presets.ts";
5
+
6
+ const NODES = [
7
+ { id: "metrics", label: "Metrics", sub: "threshold check", color: "#00A6D6", col: 0, row: 0, step: 0 },
8
+ { id: "traces", label: "Traces", sub: "latency check", color: "#76B82A", col: 0, row: 1, step: 1 },
9
+ { id: "logs", label: "Logs", sub: "keyword match", color: "#1B556B", col: 0, row: 2, step: 2 },
10
+ { id: "count", label: "Count alerts", sub: "per service", color: "#E8912D", col: 1, row: 1, step: 3 },
11
+ { id: "rank", label: "Rank services", sub: "most alerts first", color: "#C00000", col: 2, row: 1, step: 4 },
12
+ ];
13
+
14
+ const ICON_PATHS = {
15
+ metrics: "M3 3v18h18M7 16l4-8 4 4 4-6",
16
+ traces: "M3 12h4l3-9 4 18 3-9h4",
17
+ logs: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M8 13h8M8 17h8M8 9h2",
18
+ count: "M3 3h18v18H3zM3 9h18M3 15h18M9 3v18",
19
+ rank: "M4 6h16M4 12h10M4 18h5",
20
+ };
21
+
22
+ const DETAILS = [
23
+ {
24
+ vb: "0 0 300 100",
25
+ illustration: (
26
+ <g>
27
+ <line x1="10" y1="70" x2="210" y2="70" stroke="#ddd" strokeWidth="1.5" />
28
+ <line x1="10" y1="70" x2="10" y2="10" stroke="#ddd" strokeWidth="1.5" />
29
+ <line x1="10" y1="28" x2="210" y2="28" stroke="#C00000" strokeWidth="1.5" strokeDasharray="4 3" />
30
+ <text x="210" y="22" textAnchor="end" fontSize="14" fill="#C00000" fontWeight="700">threshold</text>
31
+ <polyline points="20,55 50,52 80,48 100,24 120,16 140,42 160,50 180,48" fill="none" stroke="#00A6D6" strokeWidth="2.5" />
32
+ <circle cx="100" cy="24" r="4" fill="#C00000" />
33
+ <circle cx="120" cy="16" r="4" fill="#C00000" />
34
+ <text x="110" y="8" textAnchor="middle" fontSize="14" fill="#C00000" fontWeight="800">alert</text>
35
+ </g>
36
+ ),
37
+ lines: ["Metric values above threshold counted as alerts"],
38
+ },
39
+ {
40
+ vb: "0 0 300 90",
41
+ illustration: (
42
+ <g>
43
+ <rect x="4" y="6" width="100" height="13" rx="3" fill="#00A6D6" opacity="0.25" />
44
+ <text x="54" y="16" textAnchor="middle" fontSize="14" fill="#00739A" fontWeight="700">gateway 12ms</text>
45
+ <rect x="30" y="25" width="80" height="13" rx="3" fill="#76B82A" opacity="0.25" />
46
+ <text x="70" y="35" textAnchor="middle" fontSize="14" fill="#4A7A10" fontWeight="700">order-svc 8ms</text>
47
+ <rect x="56" y="44" width="155" height="13" rx="3" fill="#C00000" opacity="0.25" />
48
+ <text x="133" y="54" textAnchor="middle" fontSize="14" fill="#C00000" fontWeight="800">db-svc 580ms</text>
49
+ <text x="218" y="54" fontSize="14" fill="#C00000" fontWeight="900">!</text>
50
+ </g>
51
+ ),
52
+ lines: ["Span latency above threshold counted as alerts"],
53
+ },
54
+ {
55
+ vb: "0 0 300 96",
56
+ illustration: (
57
+ <g>
58
+ <rect x="4" y="4" width="212" height="14" rx="3" fill="#F5F5F5" stroke="#ddd" strokeWidth="1" />
59
+ <text x="8" y="14" fontSize="14" fill="#999" fontWeight="600">10:03:21 INFO request ok</text>
60
+ <rect x="4" y="22" width="212" height="14" rx="3" fill="#FFF3F3" stroke="#C00000" strokeWidth="1.5" />
61
+ <text x="8" y="32" fontSize="14" fill="#C00000" fontWeight="800">10:03:22 ERROR conn refused</text>
62
+ <rect x="4" y="40" width="212" height="14" rx="3" fill="#F5F5F5" stroke="#ddd" strokeWidth="1" />
63
+ <text x="8" y="50" fontSize="14" fill="#999" fontWeight="600">10:03:23 INFO retry 1/3</text>
64
+ <rect x="4" y="58" width="212" height="14" rx="3" fill="#FFF3F3" stroke="#C00000" strokeWidth="1.5" />
65
+ <text x="8" y="68" fontSize="14" fill="#C00000" fontWeight="800">10:03:24 ERROR timeout</text>
66
+ </g>
67
+ ),
68
+ lines: ["Lines matching error keywords counted as alerts"],
69
+ },
70
+ {
71
+ vb: "0 0 280 100",
72
+ illustration: (
73
+ <g>
74
+ <text x="16" y="14" fontSize="14" fill="#313131" fontWeight="800">Service</text>
75
+ <text x="140" y="14" fontSize="14" fill="#313131" fontWeight="800">Alerts</text>
76
+ <line x1="10" y1="20" x2="190" y2="20" stroke="#ddd" strokeWidth="1" />
77
+ <text x="16" y="36" fontSize="14" fill="#767676" fontWeight="700">ts-order</text>
78
+ <text x="155" y="36" fontSize="14" fill="#E8912D" fontWeight="900">12</text>
79
+ <text x="16" y="52" fontSize="14" fill="#767676" fontWeight="700">ts-travel</text>
80
+ <text x="155" y="52" fontSize="14" fill="#E8912D" fontWeight="900">5</text>
81
+ <text x="16" y="68" fontSize="14" fill="#767676" fontWeight="700">ts-auth</text>
82
+ <text x="155" y="68" fontSize="14" fill="#E8912D" fontWeight="900">2</text>
83
+ </g>
84
+ ),
85
+ lines: ["Sum metric + trace + log alerts per service"],
86
+ },
87
+ {
88
+ vb: "0 0 280 100",
89
+ illustration: (
90
+ <g>
91
+ <rect x="8" y="4" width="194" height="20" rx="4" fill="#C00000" fillOpacity="0.1" stroke="#C00000" strokeWidth="1.5" />
92
+ <text x="16" y="18" fontSize="14" fill="#C00000" fontWeight="900">#1 ts-order — 12 alerts</text>
93
+ <rect x="8" y="30" width="194" height="20" rx="4" fill="#F5F5F5" stroke="#ddd" strokeWidth="1" />
94
+ <text x="16" y="44" fontSize="14" fill="#767676" fontWeight="700">#2 ts-travel — 5 alerts</text>
95
+ <rect x="8" y="56" width="194" height="20" rx="4" fill="#F5F5F5" stroke="#ddd" strokeWidth="1" />
96
+ <text x="16" y="70" fontSize="14" fill="#767676" fontWeight="700">#3 ts-auth — 2 alerts</text>
97
+ </g>
98
+ ),
99
+ lines: ["Most-alerted service = predicted root cause"],
100
+ },
101
+ ];
102
+
103
+ const COL_W = 180;
104
+ const ROW_H = 110;
105
+ const GAP_X = 200;
106
+ const GAP_Y = 16;
107
+ const PAD_X = 40;
108
+ const PAD_Y = 20;
109
+ const ICON_R = NODE.radius - 8;
110
+ const DETAIL_W = DETAIL.width;
111
+ const DETAIL_H = DETAIL.height;
112
+
113
+ function nx(col: number) { return PAD_X + col * (COL_W + GAP_X) + COL_W / 2; }
114
+ function ny(row: number) { return PAD_Y + row * (ROW_H + GAP_Y) + ICON_R + 8; }
115
+
116
+ const LINKS = [
117
+ { from: 0, to: 3, step: 3 },
118
+ { from: 1, to: 3, step: 3 },
119
+ { from: 2, to: 3, step: 3 },
120
+ { from: 3, to: 4, step: 4 },
121
+ ];
122
+
123
+ function arrowPath(x1: number, y1: number, x2: number, y2: number) {
124
+ const mid = (x1 + x2) / 2;
125
+ return "M" + x1 + "," + y1 + " C" + mid + "," + y1 + " " + mid + "," + y2 + " " + x2 + "," + y2;
126
+ }
127
+
128
+ function anchorPt(n: any, side: string) {
129
+ const cx = nx(n.col), cy = ny(n.row);
130
+ const r = ICON_R;
131
+ if (side === "right") return [cx + r + 6, cy];
132
+ if (side === "left") return [cx - r - 6, cy];
133
+ return [cx, cy];
134
+ }
135
+
136
+ function NodeIcon({ node, step, instant }: { node: any; step: number; instant: boolean }) {
137
+ const cx = nx(node.col), cy = ny(node.row);
138
+ const r = ICON_R;
139
+ return (
140
+ <g style={vis(step, node.step, instant)}>
141
+ <circle cx={cx} cy={cy} r={r + 5} fill={node.color + "11"} />
142
+ <circle cx={cx} cy={cy} r={r} fill="white" stroke={node.color} strokeWidth={NODE.strokeWidth} />
143
+ <g transform={"translate(" + (cx - NODE.iconSize / 2) + ", " + (cy - NODE.iconSize / 2) + ")"}>
144
+ <svg width={NODE.iconSize} height={NODE.iconSize} viewBox="0 0 24 24" fill="none"
145
+ stroke={node.color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
146
+ <path d={ICON_PATHS[node.id as keyof typeof ICON_PATHS]} />
147
+ </svg>
148
+ </g>
149
+ <text x={cx} y={cy + r + 20} textAnchor="middle" fontSize={NODE.labelFont} fontWeight="900"
150
+ fill={NODE.labelColor} fontFamily={CANVAS.fontFamily}>{node.label}</text>
151
+ <text x={cx} y={cy + r + 38} textAnchor="middle" fontSize={NODE.subFont} fontWeight="700"
152
+ fill={node.color} fontFamily={CANVAS.fontFamily}>{node.sub}</text>
153
+ </g>
154
+ );
155
+ }
156
+
157
+ function DetailAnnotation({ detail, step, currentStep, svgW, svgH, instant }: { detail: any; step: number; currentStep: number; svgW: number; svgH: number; instant: boolean }) {
158
+ if (!detail) return null;
159
+ const node = NODES[step];
160
+ const occupied = nodeOccupiedRects(NODES, step, { nx: nx, ny: ny, iconR: ICON_R, labelH: 44 });
161
+ const pos = findEmptySlot({
162
+ canvasW: svgW, canvasH: svgH,
163
+ occupied: occupied, slotW: DETAIL_W, slotH: DETAIL_H,
164
+ margin: DETAIL.margin,
165
+ near: { x: nx(node.col), y: ny(node.row) },
166
+ step: DETAIL.scanStep,
167
+ });
168
+ return (
169
+ <g style={visExact(currentStep, step, instant)}>
170
+ <rect x={pos.x} y={pos.y} width={DETAIL_W} height={DETAIL_H} rx="12"
171
+ fill="white" fillOpacity={DETAIL.bgOpacity} stroke="none" />
172
+ <foreignObject x={pos.x} y={pos.y} width={DETAIL_W} height={DETAIL_H}>
173
+ <div style={annotationStyle()}>
174
+ <svg style={illustrationStyle()} viewBox={detail.vb}>
175
+ {detail.illustration}
176
+ </svg>
177
+ <div style={{ flex: "1 1 auto", display: "flex", flexDirection: "column", justifyContent: "center" }}>
178
+ <div style={annotationTitleStyle()}>
179
+ {detail.lines[0]}
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </foreignObject>
184
+ </g>
185
+ );
186
+ }
187
+
188
+ const SVG_W = 3 * COL_W + 2 * GAP_X + 2 * PAD_X;
189
+ const SVG_H = 3 * ROW_H + 2 * GAP_Y + 2 * PAD_Y + 200;
190
+
191
+ export default function SimpleRca({ Reveal }: SlideComponentProps) {
192
+ return (
193
+ <SlideCanvas Reveal={Reveal} W={SVG_W} H={SVG_H}>{(step, instant) => <>
194
+ <defs>
195
+ <marker id="srca-arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
196
+ <path d="M 0 0 L 10 5 L 0 10 z" fill="#BFBFBF" />
197
+ </marker>
198
+ </defs>
199
+
200
+ {LINKS.map(function (link, i) {
201
+ const from = NODES[link.from], to = NODES[link.to];
202
+ const p1 = anchorPt(from, "right");
203
+ const p2 = anchorPt(to, "left");
204
+ return <path key={i} d={arrowPath(p1[0], p1[1], p2[0], p2[1])}
205
+ fill="none" stroke="#BFBFBF" strokeWidth="2.5" strokeLinecap="round"
206
+ markerEnd="url(#srca-arr)" style={vis(step, link.step, instant)} />;
207
+ })}
208
+
209
+ {NODES.map(function (n) { return <NodeIcon key={n.id} node={n} step={step} instant={instant} />; })}
210
+
211
+ {DETAILS.map(function (d, i) {
212
+ return <DetailAnnotation key={i} detail={d} step={i} currentStep={step} svgW={SVG_W} svgH={SVG_H} instant={instant} />;
213
+ })}
214
+ </>}</SlideCanvas>
215
+ );
216
+ }
@@ -0,0 +1,63 @@
1
+ import React from "react";
2
+ import type { SlideComponentProps } from "../../../core/types.ts";
3
+ import { CANVAS, SlideCanvas, vis } from "../../../core/presets.ts";
4
+ import { ALGOS } from "../../../data/algorithms.ts";
5
+
6
+ const W = 1060, H = 440;
7
+ const BAR_X = 140;
8
+ const BAR_MAX = 360;
9
+ const TABLE_X = 560;
10
+ const ROW_H = 32;
11
+ const TOP_Y = 20;
12
+
13
+ export default function SotaCollapse({ Reveal }: SlideComponentProps) {
14
+ return (
15
+ <SlideCanvas Reveal={Reveal} W={W} H={H}>{(step, instant) => <>
16
+
17
+ <g style={vis(step, -1, instant)}>
18
+ <text x={BAR_X + BAR_MAX / 2} y={TOP_Y} textAnchor="middle" fontSize="14" fontWeight="800" fill="#999" fontFamily={CANVAS.fontFamily}>Top@1</text>
19
+
20
+ <text x={TABLE_X + 40} y={TOP_Y} textAnchor="middle" fontSize="14" fontWeight="800" fill="#999" fontFamily={CANVAS.fontFamily}>@1</text>
21
+ <text x={TABLE_X + 100} y={TOP_Y} textAnchor="middle" fontSize="14" fontWeight="800" fill="#999" fontFamily={CANVAS.fontFamily}>@3</text>
22
+ <text x={TABLE_X + 160} y={TOP_Y} textAnchor="middle" fontSize="14" fontWeight="800" fill="#999" fontFamily={CANVAS.fontFamily}>@5</text>
23
+ <text x={TABLE_X + 230} y={TOP_Y} textAnchor="middle" fontSize="14" fontWeight="800" fill="#999" fontFamily={CANVAS.fontFamily}>Time(s)</text>
24
+ </g>
25
+
26
+ {ALGOS.map(function (a, i) {
27
+ const y = TOP_Y + 10 + i * ROW_H;
28
+ const barW = (a.t1 / 0.40) * BAR_MAX;
29
+ const isSimple = a.name === "SimpleRCA";
30
+ const isBest = a.name === "MicroRCA";
31
+ const barColor = isSimple ? "#C00000" : isBest ? "#76B82A" : a.color;
32
+ const revealStep = i < 4 ? 0 : i < 8 ? 1 : 2;
33
+
34
+ return (
35
+ <g key={i} style={vis(step, revealStep, instant)}>
36
+ <text x={BAR_X - 8} y={y + ROW_H / 2 + 1} textAnchor="end" dominantBaseline="middle"
37
+ fontSize="14" fontWeight={isSimple || isBest ? 900 : 700} fill={isSimple ? "#C00000" : "#313131"} fontFamily={CANVAS.fontFamily}>{a.name}</text>
38
+
39
+ <rect x={BAR_X} y={y + 5} width={barW} height={ROW_H - 12} rx="3"
40
+ fill={barColor} fillOpacity="0.18" stroke={barColor} strokeWidth={isSimple || isBest ? 2 : 1} />
41
+ <text x={BAR_X + barW + 6} y={y + ROW_H / 2 + 1} dominantBaseline="middle"
42
+ fontSize="14" fontWeight="900" fill={barColor} fontFamily={CANVAS.fontFamily}>{a.t1.toFixed(2)}</text>
43
+
44
+ <text x={TABLE_X + 40} y={y + ROW_H / 2 + 1} textAnchor="middle" dominantBaseline="middle"
45
+ fontSize="14" fontWeight="700" fill="#767676" fontFamily={CANVAS.fontFamily}>{a.t1.toFixed(2)}</text>
46
+ <text x={TABLE_X + 100} y={y + ROW_H / 2 + 1} textAnchor="middle" dominantBaseline="middle"
47
+ fontSize="14" fontWeight="700" fill="#767676" fontFamily={CANVAS.fontFamily}>{a.t3.toFixed(2)}</text>
48
+ <text x={TABLE_X + 160} y={y + ROW_H / 2 + 1} textAnchor="middle" dominantBaseline="middle"
49
+ fontSize="14" fontWeight="700" fill="#767676" fontFamily={CANVAS.fontFamily}>{a.t5.toFixed(2)}</text>
50
+ <text x={TABLE_X + 230} y={y + ROW_H / 2 + 1} textAnchor="middle" dominantBaseline="middle"
51
+ fontSize="14" fontWeight="700" fill={parseFloat(a.time) > 100 ? "#C00000" : "#999"} fontFamily={CANVAS.fontFamily}>{a.time}</text>
52
+ </g>
53
+ );
54
+ })}
55
+
56
+ <g style={vis(step, 3, instant)}>
57
+ <text x={W / 2} y={H - 10} textAnchor="middle" fontSize="16" fontWeight="800" fill="#313131" fontFamily={CANVAS.fontFamily}>
58
+ Best Top@1 = 0.37 · SimpleRCA = 0.28 · The shortcut no longer dominates
59
+ </text>
60
+ </g>
61
+ </>}</SlideCanvas>
62
+ );
63
+ }
@@ -0,0 +1,115 @@
1
+ import React from "react";
2
+ import type { SlideComponentProps } from "../../../core/types.ts";
3
+ import { useRevealStep, CANVAS } from "../../../core/presets.ts";
4
+
5
+ const DATA = [
6
+ { ds: "RE2-OB", s: [0.47, 0.93, 1.00], m: "BARO", o: [0.14, 0.93, 0.98], win: true },
7
+ { ds: "RE2-TT", s: [0.80, 0.87, 0.93], m: "BARO", o: [0.67, 0.84, 0.89], win: true },
8
+ { ds: "RE2-SS", s: [0.24, 0.88, 0.97], m: "BARO", o: [0.14, 0.82, 0.94], win: true },
9
+ { ds: "RE3-OB", s: [0.30, 0.90, 0.90], m: "BARO", o: [0.00, 0.90, 0.90], win: true },
10
+ { ds: "RE3-TT", s: [0.83, 1.00, 1.00], m: "BARO", o: [0.50, 0.93, 1.00], win: true },
11
+ { ds: "RE3-SS", s: [0.83, 0.90, 0.90], m: "BARO", o: [0.00, 0.83, 0.93], win: true },
12
+ { ds: "Nezha-TT", s: [0.93, 0.98, 0.98], m: "Nezha", o: [0.87, 0.98, 0.98], win: true },
13
+ { ds: "Nezha-OB", s: [0.91, 0.91, 0.91], m: "Nezha", o: [0.93, 0.96, 0.96], win: false },
14
+ { ds: "Eadro-TT", s: [0.81, 0.83, 0.83], m: "Eadro", o: [0.99, 0.99, 0.99], win: false, note: true },
15
+ { ds: "Eadro-SN", s: [0.83, 0.83, 0.83], m: "Eadro", o: [0.97, 0.99, 0.99], win: false, note: true },
16
+ { ds: "AIOps-21", s: [0.63, 0.82, 0.91], m: "ART", o: [0.72, 0.89, null], win: false },
17
+ ];
18
+
19
+ const TH: React.CSSProperties = { padding: "8px 8px 2px", fontSize: 14, fontWeight: 900, color: "#313131", borderBottom: "none" };
20
+ const SUB: React.CSSProperties = { padding: "0 8px 8px", fontSize: 14, fontWeight: 800, color: "#999", textAlign: "right", borderBottom: "2px solid #313131" };
21
+ const DS_S: React.CSSProperties = { padding: "7px 10px", fontSize: 18, fontWeight: 800, color: "#313131", textAlign: "left", borderBottom: "1px solid #e8e8e8" };
22
+ const NUM_S: React.CSSProperties = { padding: "7px 10px", fontSize: 18, fontWeight: 700, textAlign: "right", borderBottom: "1px solid #e8e8e8" };
23
+ const M_S: React.CSSProperties = { padding: "7px 10px 7px 18px", fontSize: 14, fontWeight: 700, color: "#999", textAlign: "left", borderBottom: "1px solid #e8e8e8" };
24
+
25
+ function fmt(v: number | null) { return v !== null ? v.toFixed(2) : "—"; }
26
+ function wc(a: number | null, b: number | null) { return (b === null || (a as number) >= b) ? "#C00000" : "#999"; }
27
+ function ww(a: number | null, b: number | null) { return (b === null || (a as number) >= b) ? 900 : 700; }
28
+
29
+ export default function SrcaResults({ Reveal }: SlideComponentProps) {
30
+ const ref = useRevealStep(Reveal);
31
+ const step = ref[0], ready = ref[1], instant = ref[2];
32
+ const tr = instant ? "none" : "opacity 0.4s ease, background 0.4s ease";
33
+
34
+ function rowOp(win: boolean) {
35
+ if (step < 0 || step >= 2) return 1;
36
+ return (step === 0) === win ? 1 : 0.15;
37
+ }
38
+ function rowBg(win: boolean) {
39
+ if (step === 0 && win) return "rgba(192,0,0,0.04)";
40
+ if (step === 1 && !win) return "rgba(27,85,107,0.04)";
41
+ return "transparent";
42
+ }
43
+
44
+ return (
45
+ <div style={{ opacity: ready ? 1 : 0, transition: instant ? "none" : "opacity 0.2s ease", width: "100%", height: "100%", fontFamily: CANVAS.fontFamily, position: "relative" }}>
46
+ <table style={{ borderCollapse: "collapse", width: "100%" }}>
47
+ <thead>
48
+ <tr>
49
+ <th style={{ ...TH, textAlign: "left" }}></th>
50
+ <th colSpan={3} style={{ ...TH, textAlign: "center", borderBottom: "2px solid #C00000" }}>SimpleRCA</th>
51
+ <th style={TH}></th>
52
+ <th colSpan={3} style={{ ...TH, textAlign: "center" }}>Best SOTA</th>
53
+ </tr>
54
+ <tr>
55
+ <th style={{ ...SUB, textAlign: "left" }}>Dataset</th>
56
+ <th style={SUB}>@1</th><th style={SUB}>@3</th><th style={SUB}>@5</th>
57
+ <th style={{ ...SUB, textAlign: "left", paddingLeft: 18 }}>Method</th>
58
+ <th style={SUB}>@1</th><th style={SUB}>@3</th><th style={SUB}>@5</th>
59
+ </tr>
60
+ </thead>
61
+ <tbody>
62
+ {DATA.map(function (r) {
63
+ return (
64
+ <tr key={r.ds} style={{ opacity: rowOp(r.win), background: rowBg(r.win), transition: tr }}>
65
+ <td style={DS_S}>{r.ds}</td>
66
+ <td style={{ ...NUM_S, color: wc(r.s[0], r.o[0]), fontWeight: ww(r.s[0], r.o[0]) }}>{fmt(r.s[0])}</td>
67
+ <td style={{ ...NUM_S, color: wc(r.s[1], r.o[1]), fontWeight: ww(r.s[1], r.o[1]) }}>{fmt(r.s[1])}</td>
68
+ <td style={{ ...NUM_S, color: wc(r.s[2], r.o[2]), fontWeight: ww(r.s[2], r.o[2]) }}>{fmt(r.s[2])}</td>
69
+ <td style={M_S}>{r.m}{r.note ? " *" : ""}</td>
70
+ <td style={{ ...NUM_S, color: wc(r.o[0], r.s[0]), fontWeight: ww(r.o[0], r.s[0]) }}>{fmt(r.o[0])}</td>
71
+ <td style={{ ...NUM_S, color: wc(r.o[1], r.s[1]), fontWeight: ww(r.o[1], r.s[1]) }}>{fmt(r.o[1])}</td>
72
+ <td style={{ ...NUM_S, color: wc(r.o[2], r.s[2]), fontWeight: ww(r.o[2], r.s[2]) }}>{fmt(r.o[2])}</td>
73
+ </tr>
74
+ );
75
+ })}
76
+ </tbody>
77
+ </table>
78
+ <div style={{ marginTop: 6, fontSize: 14, color: "#999", fontWeight: 600 }}>* Eadro: training / test set overlap</div>
79
+
80
+ <div style={{
81
+ position: "absolute", bottom: 30, left: "50%", transform: "translateX(-50%)",
82
+ padding: "14px 36px", borderRadius: 10,
83
+ background: "#fff", boxShadow: "0 8px 28px rgba(49,49,49,0.15)",
84
+ opacity: step === 0 ? 1 : 0, transition: tr,
85
+ whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: 20,
86
+ }}>
87
+ <strong style={{ color: "#C00000", fontSize: 28, fontWeight: 900 }}>SimpleRCA wins</strong>
88
+ <span style={{ fontSize: 16, fontWeight: 700, color: "#767676" }}>matches or beats published SOTA on 7 datasets</span>
89
+ </div>
90
+
91
+ <div style={{
92
+ position: "absolute", bottom: 30, left: "50%", transform: "translateX(-50%)",
93
+ padding: "14px 36px", borderRadius: 10,
94
+ background: "#fff", boxShadow: "0 8px 28px rgba(49,49,49,0.15)",
95
+ opacity: step === 1 ? 1 : 0, transition: tr,
96
+ whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: 20,
97
+ }}>
98
+ <strong style={{ color: "#1B556B", fontSize: 28, fontWeight: 900 }}>Even when losing</strong>
99
+ <span style={{ fontSize: 16, fontWeight: 700, color: "#767676" }}>gap ≤ 0.18 · Eadro* has data leakage</span>
100
+ </div>
101
+
102
+ <div style={{
103
+ position: "absolute", bottom: 30, left: "50%", transform: "translateX(-50%)",
104
+ padding: "14px 36px", borderRadius: 10,
105
+ background: "#fff", boxShadow: "0 8px 28px rgba(49,49,49,0.15)",
106
+ opacity: step >= 2 ? 1 : 0, transition: tr,
107
+ whiteSpace: "nowrap", display: "flex", alignItems: "center", gap: 24,
108
+ }}>
109
+ <strong style={{ color: "#C00000", fontSize: 36, fontWeight: 900 }}>7 / 11</strong>
110
+ <span style={{ fontSize: 18, fontWeight: 800, color: "#313131" }}>A trivial probe should not match published models</span>
111
+ </div>
112
+
113
+ </div>
114
+ );
115
+ }