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,271 @@
1
+ import { blockSlide, reactSlide } from "../../core/layout.ts";
2
+ import type { ComponentName } from "./components/registry.ts";
3
+ import type { Slide } from "../../core/types.ts";
4
+
5
+ function cover(): Slide {
6
+ return reactSlide<ComponentName>({
7
+ title: "",
8
+ component: "DemoCover",
9
+ steps: 2,
10
+ notes: "Demo deck showcasing all available component patterns, libraries, and animation techniques.",
11
+ });
12
+ }
13
+
14
+ function forceGraph(): Slide {
15
+ return reactSlide<ComponentName>({
16
+ title: "Force-Directed Graph",
17
+ component: "DemoForceGraph",
18
+ steps: 3,
19
+ notes: "d3-force for auto-layout network graphs. Nodes repel, links attract. Drag to interact.",
20
+ });
21
+ }
22
+
23
+ function sankeyFlow(): Slide {
24
+ return reactSlide<ComponentName>({
25
+ title: "Sankey Flow Diagram",
26
+ component: "DemoSankey",
27
+ steps: 2,
28
+ notes: "d3-sankey for flow/energy diagrams. Width encodes magnitude.",
29
+ });
30
+ }
31
+
32
+ function animatedCounter(): Slide {
33
+ return reactSlide<ComponentName>({
34
+ title: "Animated Counters",
35
+ component: "DemoCounter",
36
+ steps: 3,
37
+ notes: "animejs number animation, progress bars, and stagger effects.",
38
+ });
39
+ }
40
+
41
+ function handDrawn(): Slide {
42
+ return reactSlide<ComponentName>({
43
+ title: "Hand-Drawn Style",
44
+ component: "DemoRough",
45
+ steps: 3,
46
+ notes: "roughjs for sketch-style diagrams. Breaks visual monotony.",
47
+ });
48
+ }
49
+
50
+ function iconWall(): Slide {
51
+ return reactSlide<ComponentName>({
52
+ title: "Icon Showcase",
53
+ component: "DemoIconWall",
54
+ steps: 2,
55
+ notes: "lucide-react icons with stagger wave entrance via animejs.",
56
+ });
57
+ }
58
+
59
+ function codeWalkthrough(): Slide {
60
+ return reactSlide<ComponentName>({
61
+ title: "Code Walkthrough",
62
+ component: "DemoCode",
63
+ steps: 4,
64
+ notes: "prismjs syntax highlighting with progressive line reveal.",
65
+ });
66
+ }
67
+
68
+ function radarChart(): Slide {
69
+ return reactSlide<ComponentName>({
70
+ title: "Radar Chart",
71
+ component: "DemoRadar",
72
+ steps: 2,
73
+ notes: "d3-scale + d3-shape for multi-axis comparison.",
74
+ });
75
+ }
76
+
77
+ function timeline(): Slide {
78
+ return reactSlide<ComponentName>({
79
+ title: "Animated Timeline",
80
+ component: "DemoTimeline",
81
+ steps: 5,
82
+ notes: "SVG timeline with animejs sequential node activation and path drawing.",
83
+ });
84
+ }
85
+
86
+ function cardFlip(): Slide {
87
+ return reactSlide<ComponentName>({
88
+ title: "Card Flip",
89
+ component: "DemoCardFlip",
90
+ steps: 3,
91
+ notes: "CSS 3D perspective transforms for card flip effect.",
92
+ });
93
+ }
94
+
95
+ function mathFormula(): Slide {
96
+ return reactSlide<ComponentName>({
97
+ title: "Math Formulas",
98
+ component: "DemoMath",
99
+ steps: 3,
100
+ notes: "KaTeX rendering for academic formulas with progressive reveal.",
101
+ });
102
+ }
103
+
104
+ function treemap(): Slide {
105
+ return reactSlide<ComponentName>({
106
+ title: "Treemap",
107
+ component: "DemoTreemap",
108
+ steps: 2,
109
+ notes: "d3-hierarchy treemap with stagger zoom entrance.",
110
+ });
111
+ }
112
+
113
+ function interactiveTabs(): Slide {
114
+ return reactSlide<ComponentName>({
115
+ title: "Interactive Tabs",
116
+ component: "DemoTabs",
117
+ steps: 3,
118
+ notes: "framer-motion layout animation for smooth tab switching.",
119
+ });
120
+ }
121
+
122
+ function pathAnimation(): Slide {
123
+ return reactSlide<ComponentName>({
124
+ title: "Path Animation",
125
+ component: "DemoPath",
126
+ steps: 3,
127
+ notes: "animejs SVG path tracing, particle motion, and trail effects.",
128
+ });
129
+ }
130
+
131
+ function heatmap(): Slide {
132
+ return reactSlide<ComponentName>({
133
+ title: "Heatmap Grid",
134
+ component: "DemoHeatmap",
135
+ steps: 2,
136
+ notes: "d3-scale color mapping with animejs wave fill animation.",
137
+ });
138
+ }
139
+
140
+ function fullbleedBars(): Slide {
141
+ return reactSlide<ComponentName>({
142
+ title: "",
143
+ component: "DemoFullbleedBars",
144
+ steps: 2,
145
+ notes: "Full-bleed horizontal bars — no cards, data IS the content.",
146
+ });
147
+ }
148
+
149
+ function dashboard(): Slide {
150
+ return reactSlide<ComponentName>({
151
+ title: "Dashboard Layout",
152
+ component: "DemoDashboard",
153
+ steps: 2,
154
+ notes: "Asymmetric 2-column dashboard with chart + metrics.",
155
+ });
156
+ }
157
+
158
+ function bigText(): Slide {
159
+ return reactSlide<ComponentName>({
160
+ title: "",
161
+ component: "DemoBigText",
162
+ steps: 2,
163
+ notes: "Big typography + whitespace. Less is more.",
164
+ });
165
+ }
166
+
167
+ function beforeAfter(): Slide {
168
+ return reactSlide<ComponentName>({
169
+ title: "",
170
+ component: "DemoBeforeAfter",
171
+ steps: 3,
172
+ notes: "Before/After split comparison with color contrast.",
173
+ });
174
+ }
175
+
176
+ function screenshotAnnotate(): Slide {
177
+ return reactSlide<ComponentName>({
178
+ title: "Screenshot + Annotation",
179
+ component: "DemoScreenshotAnnotate",
180
+ steps: 3,
181
+ notes: "Fake terminal screenshot with roughjs hand-drawn annotations.",
182
+ });
183
+ }
184
+
185
+ function fullbleedFlow(): Slide {
186
+ return reactSlide<ComponentName>({
187
+ title: "",
188
+ component: "DemoFullbleedFlow",
189
+ steps: 3,
190
+ notes: "Full-bleed chevron flow — edge to edge, no margins.",
191
+ });
192
+ }
193
+
194
+ function zoomFocus(): Slide {
195
+ return reactSlide<ComponentName>({
196
+ title: "Zoom Focus",
197
+ component: "DemoZoomFocus",
198
+ steps: 3,
199
+ notes: "Prezi-like zoom into different regions of a diagram.",
200
+ });
201
+ }
202
+
203
+ function chatBubbles(): Slide {
204
+ return reactSlide<ComponentName>({
205
+ title: "Chat / Dialogue",
206
+ component: "DemoChatBubbles",
207
+ steps: 4,
208
+ notes: "Conversation-style slide showing interactive dialogue.",
209
+ });
210
+ }
211
+
212
+ function stackedCards(): Slide {
213
+ return reactSlide<ComponentName>({
214
+ title: "Stacked Cards",
215
+ component: "DemoStackedCards",
216
+ steps: 4,
217
+ notes: "Cards fan out from a stack like playing cards.",
218
+ });
219
+ }
220
+
221
+ function conceptMap(): Slide {
222
+ return reactSlide<ComponentName>({
223
+ title: "Concept Map",
224
+ component: "DemoConceptMap",
225
+ steps: 4,
226
+ notes: "Mind map with animated branches and connections.",
227
+ });
228
+ }
229
+
230
+ function numberMorph(): Slide {
231
+ return reactSlide<ComponentName>({
232
+ title: "",
233
+ component: "DemoNumberMorph",
234
+ steps: 3,
235
+ notes: "Dramatic number morphing from old to new value.",
236
+ });
237
+ }
238
+
239
+ function arcProgress(): Slide {
240
+ return reactSlide<ComponentName>({
241
+ title: "Arc Progress Gauges",
242
+ component: "DemoArcProgress",
243
+ steps: 2,
244
+ notes: "Circular progress indicators with animated fill.",
245
+ });
246
+ }
247
+
248
+ function evalMatrix(): Slide {
249
+ return reactSlide<ComponentName>({
250
+ title: "Evaluation Matrix",
251
+ component: "DemoEvalMatrix",
252
+ steps: 3,
253
+ notes: "Visual comparison matrix with proportional bars.",
254
+ });
255
+ }
256
+
257
+ export function buildSlides(): Slide[] {
258
+ const builders = [
259
+ cover, forceGraph, sankeyFlow, animatedCounter, handDrawn,
260
+ iconWall, codeWalkthrough, radarChart, timeline, cardFlip,
261
+ mathFormula, treemap, interactiveTabs, pathAnimation, heatmap,
262
+ fullbleedBars, dashboard, bigText, beforeAfter, screenshotAnnotate, fullbleedFlow,
263
+ zoomFocus, chatBubbles, stackedCards, conceptMap, numberMorph, arcProgress, evalMatrix,
264
+ ];
265
+ const slides = builders.map((b) => b());
266
+ slides.forEach((slide, i) => {
267
+ const pageDiv = `\n<div class="page-no">${String(i + 1).padStart(2, "0")}</div>`;
268
+ if (!slide.html.includes('class="page-no"')) slide.html += pageDiv;
269
+ });
270
+ return slides;
271
+ }
@@ -0,0 +1,295 @@
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, TAG, EDGE, CANVAS, annotationStyle, illustrationStyle, annotationTitleStyle, vis, visExact, SlideCanvas } from "../../../core/presets.ts";
5
+
6
+ const CASCADE = [
7
+ { id: "dns", label: "DNS Race", sub: "root cause", color: "#E8912D", bg: "#FFF8F0", col: 0, row: 0, step: 0 },
8
+ { id: "dynamo", label: "DynamoDB", sub: "API failure", color: "#C00000", bg: "#FFF3F3", col: 1, row: 0, step: 1 },
9
+ { id: "dwfm", label: "DWFM", sub: "lease collapse", color: "#1B556B", bg: "#F2F8FB", col: 2, row: 0, step: 2 },
10
+ { id: "ec2", label: "EC2", sub: "launch failure", color: "#00A6D6", bg: "#F0F9FD", col: 3, row: 0, step: 3 },
11
+ { id: "net", label: "Network Mgr", sub: "state backlog", color: "#7B61FF", bg: "#F6F3FF", col: 3, row: 1, step: 4 },
12
+ { id: "nlb", label: "NLB", sub: "health flip-flop", color: "#C00000", bg: "#FFF3F3", col: 2, row: 1, step: 5 },
13
+ { id: "users", label: "Users", sub: "service down", color: "#900", bg: "#FFF0F0", col: 1, row: 1, step: 6 },
14
+ ];
15
+
16
+ const ICON_PATHS = {
17
+ dns: "M12 9v4m0 4h.01M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0z",
18
+ dynamo: "M4 7c0-1.66 3.58-3 8-3s8 1.34 8 3M4 7v10c0 1.66 3.58 3 8 3s8-1.34 8-3V7M4 12c0 1.66 3.58 3 8 3s8-1.34 8-3",
19
+ dwfm: "M2 5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm0 10a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zM6 7h.01M6 19h.01",
20
+ ec2: "M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z",
21
+ net: "M9 2h6v6H9zM1 16h6v6H1zm16 0h6v6h-6zM12 8v4M4 16l8-4 8 4",
22
+ nlb: "M12 2v4M6.34 6.34l2.83 2.83M2 12h4M18 12h4M17.66 6.34l-2.83 2.83M12 12a4 4 0 1 0 0 .01",
23
+ users: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8zM22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75",
24
+ };
25
+
26
+ const DETAIL_W = DETAIL.width;
27
+ const DETAIL_H = DETAIL.height;
28
+
29
+ const DETAILS = [
30
+ { vb: "0 0 130 56",
31
+ illustration: (
32
+ <g>
33
+ <circle cx="22" cy="22" r="16" fill="none" stroke="#E8912D" strokeWidth="2" strokeDasharray="5 3" />
34
+ <text x="22" y="27" textAnchor="middle" fontSize="14" fill="#E8912D" fontWeight="900">A</text>
35
+ <circle cx="68" cy="22" r="16" fill="none" stroke="#E8912D" strokeWidth="2" strokeDasharray="5 3" />
36
+ <text x="68" y="27" textAnchor="middle" fontSize="14" fill="#E8912D" fontWeight="900">B</text>
37
+ <line x1="38" y1="22" x2="52" y2="22" stroke="#C00000" strokeWidth="3" />
38
+ <text x="45" y="15" textAnchor="middle" fontSize="16" fill="#C00000" fontWeight="900">!</text>
39
+ <text x="45" y="46" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">DNS Enactor</text>
40
+ </g>
41
+ ),
42
+ lines: ["Two DNS Enactors race on the same endpoint"],
43
+ },
44
+ { vb: "0 0 170 65",
45
+ illustration: (
46
+ <g>
47
+ <rect x="4" y="2" width="142" height="60" rx="6" fill="none" stroke="#C00000" strokeWidth="2" />
48
+ <text x="75" y="18" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">dynamodb.us-east-1</text>
49
+ <text x="75" y="30" textAnchor="middle" fontSize="14" fill="#bbb" fontWeight="600">.amazonaws.com</text>
50
+ <line x1="14" y1="36" x2="132" y2="36" stroke="#eee" strokeWidth="1.5" />
51
+ <text x="75" y="52" textAnchor="middle" fontSize="14" fill="#C00000" fontWeight="900">IPs: (empty)</text>
52
+ <line x1="40" y1="42" x2="110" y2="56" stroke="#C00000" strokeWidth="2.5" />
53
+ <line x1="110" y1="42" x2="40" y2="56" stroke="#C00000" strokeWidth="2.5" />
54
+ </g>
55
+ ),
56
+ lines: ["All IPs removed from regional DNS endpoint"],
57
+ },
58
+ { vb: "0 0 160 90",
59
+ illustration: (
60
+ <g>
61
+ <rect x="4" y="4" width="28" height="34" rx="3" fill="none" stroke="#1B556B" strokeWidth="2" />
62
+ <rect x="8" y="10" width="20" height="3" rx="1" fill="#1B556B" opacity="0.3" />
63
+ <rect x="8" y="16" width="20" height="3" rx="1" fill="#1B556B" opacity="0.3" />
64
+ <rect x="8" y="22" width="20" height="3" rx="1" fill="#1B556B" opacity="0.3" />
65
+ <text x="18" y="54" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">hosts</text>
66
+ <line x1="36" y1="20" x2="54" y2="20" stroke="#1B556B" strokeWidth="1.5" strokeDasharray="3 2" />
67
+ <circle cx="80" cy="20" r="14" fill="none" stroke="#C00000" strokeWidth="2" />
68
+ <line x1="80" y1="10" x2="80" y2="20" stroke="#C00000" strokeWidth="2" />
69
+ <line x1="80" y1="20" x2="88" y2="25" stroke="#C00000" strokeWidth="2" />
70
+ <line x1="70" y1="10" x2="90" y2="30" stroke="#C00000" strokeWidth="2.5" />
71
+ <text x="80" y="54" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">expired</text>
72
+ </g>
73
+ ),
74
+ lines: ["Leases expire, congestive collapse on recovery"],
75
+ },
76
+ { vb: "-10 -6 120 68",
77
+ illustration: (
78
+ <g>
79
+ <path d="M56 30h-32a12 12 0 0 1-1-24 16 16 0 0 1 30 4 10 10 0 0 1-1 20z" fill="none" stroke="#00A6D6" strokeWidth="2" />
80
+ <line x1="24" y1="12" x2="40" y2="28" stroke="#C00000" strokeWidth="3" />
81
+ <line x1="40" y1="12" x2="24" y2="28" stroke="#C00000" strokeWidth="3" />
82
+ <text x="32" y="44" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">new instances</text>
83
+ </g>
84
+ ),
85
+ lines: ["No available hosts for any new instance launches"],
86
+ },
87
+ { vb: "0 0 140 76",
88
+ illustration: (
89
+ <g>
90
+ <rect x="8" y="38" width="14" height="16" rx="2" fill="#7B61FF" opacity="0.2" />
91
+ <rect x="26" y="28" width="14" height="26" rx="2" fill="#7B61FF" opacity="0.4" />
92
+ <rect x="44" y="18" width="14" height="36" rx="2" fill="#7B61FF" opacity="0.6" />
93
+ <rect x="62" y="8" width="14" height="46" rx="2" fill="#7B61FF" opacity="0.8" />
94
+ <rect x="80" y="2" width="14" height="52" rx="2" fill="#7B61FF" />
95
+ <line x1="4" y1="56" x2="98" y2="56" stroke="#7B61FF" strokeWidth="2" />
96
+ <text x="52" y="66" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">propagation backlog</text>
97
+ </g>
98
+ ),
99
+ lines: ["Network state changes queue faster than processed"],
100
+ },
101
+ { vb: "0 0 110 60",
102
+ illustration: (
103
+ <g>
104
+ <circle cx="12" cy="18" r="10" fill="#76B82A" opacity="0.5" />
105
+ <text x="12" y="22" textAnchor="middle" fontSize="14" fill="white" fontWeight="900">OK</text>
106
+ <circle cx="38" cy="18" r="10" fill="#C00000" opacity="0.5" />
107
+ <text x="38" y="22" textAnchor="middle" fontSize="14" fill="white" fontWeight="900">X</text>
108
+ <circle cx="64" cy="18" r="10" fill="#76B82A" opacity="0.5" />
109
+ <text x="64" y="22" textAnchor="middle" fontSize="14" fill="white" fontWeight="900">OK</text>
110
+ <path d="M6 40 L16 34 L26 42 L36 32 L46 42 L56 34 L66 40" fill="none" stroke="#C00000" strokeWidth="2" />
111
+ <text x="38" y="52" textAnchor="middle" fontSize="14" fill="#999" fontWeight="700">health checks</text>
112
+ </g>
113
+ ),
114
+ lines: ["Health checks flip between healthy and unhealthy"],
115
+ },
116
+ { vb: "0 0 140 50",
117
+ illustration: (
118
+ <g>
119
+ <rect x="4" y="4" width="26" height="18" rx="3" fill="none" stroke="#900" strokeWidth="2" />
120
+ <rect x="36" y="4" width="26" height="18" rx="3" fill="none" stroke="#900" strokeWidth="2" />
121
+ <rect x="68" y="4" width="26" height="18" rx="3" fill="none" stroke="#900" strokeWidth="2" />
122
+ <text x="17" y="17" textAnchor="middle" fontSize="14" fill="#900" fontWeight="900">503</text>
123
+ <text x="49" y="17" textAnchor="middle" fontSize="14" fill="#900" fontWeight="900">503</text>
124
+ <text x="81" y="17" textAnchor="middle" fontSize="14" fill="#900" fontWeight="900">503</text>
125
+ <text x="49" y="38" textAnchor="middle" fontSize="14" fill="#900" fontWeight="800">15h+ total outage</text>
126
+ </g>
127
+ ),
128
+ lines: ["Snapchat, Zoom, Slack ... connection timeouts, service down"],
129
+ },
130
+ ];
131
+
132
+ const COL_W = 210;
133
+ const ROW_H = 155;
134
+ const GAP_X = 70;
135
+ const GAP_Y = 30;
136
+ const PAD_X = 24;
137
+ const PAD_Y = 50;
138
+ const ICON_R = NODE.radius;
139
+
140
+ function nx(col: number) { return PAD_X + col * (COL_W + GAP_X) + COL_W / 2; }
141
+ function ny(row: number) { return PAD_Y + row * (ROW_H + GAP_Y) + ICON_R + 8; }
142
+
143
+ const LINKS = [
144
+ { from: 0, to: 1, dir: "h", step: 1 },
145
+ { from: 1, to: 2, dir: "h", step: 2 },
146
+ { from: 2, to: 3, dir: "h", step: 3 },
147
+ { from: 3, to: 4, dir: "v", step: 4 },
148
+ { from: 4, to: 5, dir: "h-rev", step: 5 },
149
+ { from: 5, to: 6, dir: "h-rev", step: 6 },
150
+ ];
151
+
152
+ const DURATION_LABELS = [
153
+ { text: "~3h", col: 1, row: 0, step: 1 },
154
+ { text: "~3h", col: 2, row: 0, step: 2 },
155
+ { text: "~8h", col: 3, row: 0, step: 3 },
156
+ { text: "~4h", col: 3, row: 1, step: 4 },
157
+ { text: "~9h", col: 2, row: 1, step: 5 },
158
+ { text: "15h+", col: 1, row: 1, step: 6 },
159
+ ];
160
+
161
+ // vis, visExact imported from presets
162
+
163
+ function arrowPath(x1: number, y1: number, x2: number, y2: number, vertical: boolean) {
164
+ if (vertical) { const m = (y1+y2)/2; return `M${x1},${y1} C${x1},${m} ${x2},${m} ${x2},${y2}`; }
165
+ const m = (x1+x2)/2; return `M${x1},${y1} C${m},${y1} ${m},${y2} ${x2},${y2}`;
166
+ }
167
+
168
+ function anchorPt(n: (typeof CASCADE)[number], side: string) {
169
+ const cx = nx(n.col), cy = ny(n.row);
170
+ const r = (n.id === "dns" || n.id === "users") ? NODE.radiusAccent : ICON_R;
171
+ switch (side) {
172
+ case "right": return [cx + r + 6, cy];
173
+ case "left": return [cx - r - 6, cy];
174
+ case "bottom": return [cx, cy + r + 48];
175
+ case "top": return [cx, cy - r - 14];
176
+ default: return [cx, cy];
177
+ }
178
+ }
179
+
180
+ function NodeIcon({ node, visible, instant }: { node: (typeof CASCADE)[number]; visible: number; instant: boolean }) {
181
+ const cx = nx(node.col), cy = ny(node.row);
182
+ const isAccent = node.id === "dns" || node.id === "users";
183
+ const r = isAccent ? NODE.radiusAccent : ICON_R;
184
+ const strokeW = isAccent ? NODE.strokeWidthAccent : NODE.strokeWidth;
185
+ const iSz = NODE.iconSize + 8;
186
+ return (
187
+ <g style={vis(visible, node.step, instant)}>
188
+ <circle cx={cx} cy={cy} r={r + 6} fill={node.bg} />
189
+ <circle cx={cx} cy={cy} r={r} fill="white" stroke={node.color} strokeWidth={strokeW} />
190
+ {isAccent && <circle cx={cx} cy={cy} r={r + 10} fill="none" stroke={node.color} strokeWidth="2" strokeDasharray="4 4" opacity="0.5" />}
191
+ <g transform={`translate(${cx - iSz/2}, ${cy - iSz/2})`}>
192
+ <svg width={iSz} height={iSz} viewBox="0 0 24 24" fill="none" stroke={node.color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
193
+ <path d={(ICON_PATHS as any)[node.id]} />
194
+ </svg>
195
+ </g>
196
+ <text x={cx} y={cy+r+24} textAnchor="middle" fontSize={NODE.labelFont} fontWeight="900" fill={NODE.labelColor} fontFamily={CANVAS.fontFamily}>{node.label}</text>
197
+ <text x={cx} y={cy+r+44} textAnchor="middle" fontSize={NODE.subFont} fontWeight="700" fill={node.color} fontFamily={CANVAS.fontFamily}>{node.sub}</text>
198
+ {node.id === "dns" && (
199
+ <g style={vis(visible, 0, instant)}>
200
+ <rect x={cx-60} y={cy-r-28} width="120" height={TAG.pillHeight} rx={TAG.pillRadius} fill={node.color} />
201
+ <text x={cx} y={cy-r-12} textAnchor="middle" fontSize={TAG.pillFont} fontWeight="800" fill="white" fontFamily={CANVAS.fontFamily} letterSpacing="0.5">ROOT CAUSE</text>
202
+ </g>
203
+ )}
204
+ {node.id === "users" && (
205
+ <g style={vis(visible, 6, instant)}>
206
+ <rect x={cx-74} y={cy-r-28} width="148" height={TAG.pillHeight} rx={TAG.pillRadius} fill={node.color} />
207
+ <text x={cx} y={cy-r-12} textAnchor="middle" fontSize={TAG.pillFont} fontWeight="800" fill="white" fontFamily={CANVAS.fontFamily} letterSpacing="0.5">LOUDEST SYMPTOM</text>
208
+ </g>
209
+ )}
210
+ </g>
211
+ );
212
+ }
213
+
214
+ function DetailAnnotation({ detail, step, currentStep, svgW, svgH, instant }: { detail: (typeof DETAILS)[number]; step: number; currentStep: number; svgW: number; svgH: number; instant: boolean }) {
215
+ const node = CASCADE[step];
216
+ const occupied = nodeOccupiedRects(CASCADE, step, { nx, ny, iconR: ICON_R, labelH: 54 });
217
+ const pos = findEmptySlot({
218
+ canvasW: svgW,
219
+ canvasH: svgH,
220
+ occupied,
221
+ slotW: DETAIL_W,
222
+ slotH: DETAIL_H,
223
+ margin: DETAIL.margin,
224
+ near: { x: nx(node.col), y: ny(node.row) },
225
+ step: DETAIL.scanStep,
226
+ });
227
+ return (
228
+ <g style={visExact(currentStep, step, instant)}>
229
+ <rect x={pos.x} y={pos.y} width={DETAIL_W} height={DETAIL_H} rx="12"
230
+ fill="white" fillOpacity={DETAIL.bgOpacity} stroke="none" />
231
+ <foreignObject x={pos.x} y={pos.y} width={DETAIL_W} height={DETAIL_H}>
232
+ <div style={annotationStyle()}>
233
+ <svg style={illustrationStyle()} viewBox={detail.vb}>
234
+ {detail.illustration}
235
+ </svg>
236
+ <div style={{ flex: "1 1 auto", display: "flex", flexDirection: "column", justifyContent: "center", gap: 4 }}>
237
+ <div style={annotationTitleStyle()}>
238
+ {detail.lines[0]}
239
+ </div>
240
+ {detail.lines[1] && (
241
+ <div style={{ fontSize: DETAIL.bodyFont, fontWeight: 700, color: DETAIL.bodyColor, lineHeight: 1.3 }}>
242
+ {detail.lines[1]}
243
+ </div>
244
+ )}
245
+ </div>
246
+ </div>
247
+ </foreignObject>
248
+ </g>
249
+ );
250
+ }
251
+
252
+ const SVG_W = 4 * COL_W + 3 * GAP_X + 2 * PAD_X;
253
+ const SVG_H = 2 * ROW_H + GAP_Y + 2 * PAD_Y + 240;
254
+
255
+ // getStep imported from presets
256
+
257
+ export default function AwsCascade({ Reveal }: SlideComponentProps) {
258
+ return (
259
+ <SlideCanvas Reveal={Reveal} W={SVG_W} H={SVG_H}>{(step, instant) => <>
260
+ <defs>
261
+ <marker id="cascade-arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
262
+ <path d="M 0 0 L 10 5 L 0 10 z" fill="#C00000" />
263
+ </marker>
264
+ <filter id="detail-shadow" x="-4%" y="-4%" width="108%" height="116%">
265
+ <feDropShadow dx="0" dy="2" stdDeviation="6" floodOpacity="0.1" />
266
+ </filter>
267
+ </defs>
268
+
269
+ {LINKS.map((link, i) => {
270
+ const from = CASCADE[link.from], to = CASCADE[link.to];
271
+ let p1, p2, vert = false;
272
+ if (link.dir === "h") { p1 = anchorPt(from, "right"); p2 = anchorPt(to, "left"); }
273
+ else if (link.dir === "h-rev") { p1 = anchorPt(from, "left"); p2 = anchorPt(to, "right"); }
274
+ else { p1 = anchorPt(from, "bottom"); p2 = anchorPt(to, "top"); vert = true; }
275
+ return <path key={i} d={arrowPath(p1[0], p1[1], p2[0], p2[1], vert)}
276
+ fill="none" stroke="#C00000" strokeWidth="2.5" strokeLinecap="round"
277
+ strokeDasharray={vert ? "6 4" : "none"} markerEnd="url(#cascade-arr)"
278
+ style={vis(step, link.step, instant)} />;
279
+ })}
280
+
281
+ {CASCADE.map((n) => <NodeIcon key={n.id} node={n} visible={step} instant={instant} />)}
282
+
283
+ {DURATION_LABELS.map((d, i) => {
284
+ const cx = nx(d.col), cy = ny(d.row);
285
+ const node = CASCADE.find(n => n.col === d.col && n.row === d.row);
286
+ const r = (node && (node.id === "dns" || node.id === "users")) ? ICON_R + 4 : ICON_R;
287
+ return <text key={i} x={cx+r+8} y={cy-r+6} fontSize={TAG.font} fontWeight="800"
288
+ fill={node?.color || "#999"} fontFamily="Aptos, Arial, sans-serif" opacity="0.7"
289
+ style={vis(step, d.step, instant)}>{d.text}</text>;
290
+ })}
291
+
292
+ {DETAILS.map((d, i) => <DetailAnnotation key={i} detail={d} step={i} currentStep={step} svgW={SVG_W} svgH={SVG_H} instant={instant} />)}
293
+ </>}</SlideCanvas>
294
+ );
295
+ }
@@ -0,0 +1,64 @@
1
+ import React from "react";
2
+ import type { SlideComponentProps } from "../../../core/types.ts";
3
+ import { CANVAS, SlideCanvas, vis } from "../../../core/presets.ts";
4
+ import { BENCH_DIMS as DIMS } from "../../../data/benchmark.ts";
5
+
6
+ const W = 1000, H = 440;
7
+ const ROW_H = 42;
8
+ const BAR_MAX = 220;
9
+ const OURS_X = 260;
10
+ const BEST_X = 640;
11
+ const LABEL_X = 240;
12
+
13
+ export default function BenchCompare({ Reveal }: SlideComponentProps) {
14
+ let maxVal = 0;
15
+ for (let i = 0; i < DIMS.length; i++) {
16
+ const m = Math.max(DIMS[i].ours, DIMS[i].best);
17
+ if (m > maxVal) maxVal = m;
18
+ }
19
+
20
+ return (
21
+ <SlideCanvas Reveal={Reveal} W={W} H={H}>{(step, instant) => <>
22
+
23
+ <g style={vis(step, -1, instant)}>
24
+ <text x={OURS_X + BAR_MAX / 2} y="20" textAnchor="middle" fontSize="16" fontWeight="900" fill="#C00000" fontFamily={CANVAS.fontFamily}>Ours</text>
25
+ <text x={BEST_X + BAR_MAX / 2} y="20" textAnchor="middle" fontSize="16" fontWeight="900" fill="#767676" fontFamily={CANVAS.fontFamily}>Best Existing</text>
26
+ <line x1={OURS_X} y1="28" x2={OURS_X + BAR_MAX} y2="28" stroke="#C00000" strokeWidth="2" />
27
+ <line x1={BEST_X} y1="28" x2={BEST_X + BAR_MAX} y2="28" stroke="#ddd" strokeWidth="2" />
28
+ </g>
29
+
30
+ {DIMS.map(function (d, i) {
31
+ const y = 40 + i * ROW_H;
32
+ const localMax = Math.max(d.ours, d.best);
33
+ const oursW = (d.ours / localMax) * BAR_MAX;
34
+ const bestW = (d.best / localMax) * BAR_MAX;
35
+ const oursColor = d.higher ? "#C00000" : "#E8912D";
36
+ const bestColor = "#bbb";
37
+ const revealStep = Math.floor(i / 2);
38
+
39
+ return (
40
+ <g key={i} style={vis(step, revealStep, instant)}>
41
+ <text x={LABEL_X} y={y + ROW_H / 2 + 1} textAnchor="end" dominantBaseline="middle"
42
+ fontSize="14" fontWeight="800" fill="#313131" fontFamily={CANVAS.fontFamily}>{d.label}</text>
43
+
44
+ <rect x={OURS_X} y={y + 6} width={oursW} height={ROW_H - 16} rx="4" fill={oursColor} fillOpacity="0.15" stroke={oursColor} strokeWidth="1.5" />
45
+ <text x={OURS_X + oursW + 8} y={y + ROW_H / 2 + 1} dominantBaseline="middle"
46
+ fontSize="15" fontWeight="900" fill={oursColor} fontFamily={CANVAS.fontFamily}>{d.oursF}</text>
47
+
48
+ <rect x={BEST_X} y={y + 6} width={bestW} height={ROW_H - 16} rx="4" fill={bestColor} fillOpacity="0.15" stroke={bestColor} strokeWidth="1" />
49
+ <text x={BEST_X + bestW + 8} y={y + ROW_H / 2 - 3} dominantBaseline="middle"
50
+ fontSize="14" fontWeight="800" fill="#767676" fontFamily={CANVAS.fontFamily}>{d.bestF}</text>
51
+ <text x={BEST_X + bestW + 8} y={y + ROW_H / 2 + 11} dominantBaseline="middle"
52
+ fontSize="14" fontWeight="600" fill="#999" fontFamily={CANVAS.fontFamily}>{d.bestDs}</text>
53
+ </g>
54
+ );
55
+ })}
56
+
57
+ <g style={vis(step, 4, instant)}>
58
+ <text x={W / 2} y={H - 10} textAnchor="middle" fontSize="16" fontWeight="800" fill="#313131" fontFamily={CANVAS.fontFamily}>
59
+ Balanced realism across all dimensions
60
+ </text>
61
+ </g>
62
+ </>}</SlideCanvas>
63
+ );
64
+ }