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,120 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import chokidar from "chokidar";
6
+
7
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ var revealDir = path.join(__dirname, "..");
9
+ var rootDir = path.resolve(revealDir, "..", "..");
10
+ var decksDir = path.join(revealDir, "decks");
11
+ var PORT = 8766;
12
+
13
+ var log = function (tag, msg) { console.log("[" + tag + "] " + msg); };
14
+
15
+ // --- Discover decks ---
16
+ var targetDeck = process.argv[2] || null;
17
+ var decks = [];
18
+
19
+ fs.readdirSync(decksDir).forEach(function (name) {
20
+ var dir = path.join(decksDir, name);
21
+ if (!fs.statSync(dir).isDirectory()) return;
22
+ var buildScript = path.join(dir, "build.mjs");
23
+ var bundleScript = path.join(dir, "bundle.mjs");
24
+ if (!fs.existsSync(buildScript)) return;
25
+ if (targetDeck && name !== targetDeck) return;
26
+ decks.push({
27
+ name: name,
28
+ dir: dir,
29
+ buildScript: buildScript,
30
+ bundleScript: fs.existsSync(bundleScript) ? bundleScript : null,
31
+ });
32
+ });
33
+
34
+ if (decks.length === 0) {
35
+ console.error("No decks found" + (targetDeck ? " matching '" + targetDeck + "'" : "") + " in " + decksDir);
36
+ process.exit(1);
37
+ }
38
+
39
+ log("dev", "Found " + decks.length + " deck(s): " + decks.map(function (d) { return d.name; }).join(", "));
40
+
41
+ // --- Debounced runner per deck ---
42
+ var runners = {};
43
+
44
+ function runScript(deck, scriptPath, label) {
45
+ var key = deck.name + ":" + label;
46
+ if (runners[key]) {
47
+ runners[key].pending = true;
48
+ return;
49
+ }
50
+ runners[key] = { pending: false };
51
+ log(deck.name, label + " building...");
52
+ var proc = spawn("node", [scriptPath], { cwd: rootDir, stdio: "inherit" });
53
+ proc.on("exit", function (code) {
54
+ var wasPending = runners[key].pending;
55
+ delete runners[key];
56
+ if (code === 0) {
57
+ log(deck.name, label + " done");
58
+ } else {
59
+ log(deck.name, label + " failed (exit " + code + ")");
60
+ }
61
+ if (wasPending) runScript(deck, scriptPath, label);
62
+ });
63
+ }
64
+
65
+ function buildAll(deck, reason) {
66
+ if (deck.bundleScript) runScript(deck, deck.bundleScript, "bundle");
67
+ runScript(deck, deck.buildScript, "html");
68
+ }
69
+
70
+ // --- Watch: components, core, data → rebuild all decks ---
71
+ var sharedPaths = [
72
+ path.join(revealDir, "components"),
73
+ path.join(revealDir, "core"),
74
+ path.join(revealDir, "data"),
75
+ ];
76
+
77
+ var sharedWatcher = chokidar.watch(sharedPaths, {
78
+ ignoreInitial: true,
79
+ ignored: /\.tmp\.[^/]*$/,
80
+ });
81
+ sharedWatcher.on("all", function (event, file) {
82
+ var rel = path.relative(rootDir, file);
83
+ log("watch", event + " " + rel);
84
+ decks.forEach(function (deck) { buildAll(deck, rel); });
85
+ });
86
+ log("dev", "watching components/, core/, data/");
87
+
88
+ // --- Watch: each deck's own directory → rebuild that deck ---
89
+ decks.forEach(function (deck) {
90
+ var deckWatcher = chokidar.watch(deck.dir, {
91
+ ignoreInitial: true,
92
+ ignored: /\.tmp\.[^/]*$/,
93
+ });
94
+ deckWatcher.on("all", function (event, file) {
95
+ var rel = path.relative(rootDir, file);
96
+ log("watch", event + " " + rel);
97
+ runScript(deck, deck.buildScript, "html");
98
+ });
99
+ });
100
+ log("dev", "watching deck directories");
101
+
102
+ // --- Initial build ---
103
+ decks.forEach(function (deck) { buildAll(deck, "startup"); });
104
+
105
+ // --- Static server ---
106
+ var server = spawn("npx", ["serve", "-l", String(PORT), "."], {
107
+ cwd: rootDir,
108
+ stdio: "inherit",
109
+ });
110
+ log("dev", "serving on http://localhost:" + PORT + "/");
111
+
112
+ // --- Shutdown ---
113
+ async function shutdown() {
114
+ log("dev", "shutting down");
115
+ try { await sharedWatcher.close(); } catch (e) {}
116
+ try { server.kill(); } catch (e) {}
117
+ process.exit(0);
118
+ }
119
+ process.on("SIGINT", shutdown);
120
+ process.on("SIGTERM", shutdown);
@@ -0,0 +1,86 @@
1
+ import { chromium } from "playwright";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ var SLIDE_W = 1280;
6
+ var SLIDE_H = 720;
7
+ var URL = process.argv[2] || "http://localhost:8766/dist/fse26-rca/";
8
+ var OUT = process.argv[3] || "dist/fse26-rca/deck.pdf";
9
+
10
+ async function main() {
11
+ console.log("Launching browser...");
12
+ var browser = await chromium.launch({ headless: true });
13
+ var page = await browser.newPage({
14
+ viewport: { width: SLIDE_W, height: SLIDE_H },
15
+ });
16
+
17
+ var printUrl = URL + (URL.includes("?") ? "&" : "?") + "print-pdf";
18
+ console.log("Loading " + printUrl);
19
+ await page.goto(printUrl, { waitUntil: "networkidle" });
20
+
21
+ await page.waitForFunction(function () {
22
+ return window.Reveal && document.querySelector(".reveal.ready");
23
+ }, { timeout: 15000 });
24
+ console.log("Reveal ready");
25
+
26
+ // Disable all transitions/animations so content renders instantly
27
+ await page.addStyleTag({ content: [
28
+ "*, *::before, *::after { transition: none !important; animation: none !important; }",
29
+ ".reveal .controls, .reveal .slide-number, .reveal .progress, .page-no { display: none !important; }",
30
+ ].join("\n") });
31
+
32
+ // Force-hydrate all React mounts with per-section mock Reveal
33
+ var mountCount = await page.evaluate(function () {
34
+ var sections = document.querySelectorAll("section");
35
+ var count = 0;
36
+ sections.forEach(function (section) {
37
+ var mounts = section.querySelectorAll(".block-mount[data-mount-library]");
38
+ if (!mounts.length) return;
39
+
40
+ // Create a mock Reveal scoped to this section so useRevealStep
41
+ // reads fragment state from the correct section, not the global current slide
42
+ var mockReveal = {
43
+ getCurrentSlide: function () { return section; },
44
+ on: function () {},
45
+ off: function () {},
46
+ getIndices: function () { return { h: 0, v: 0, f: 0 }; },
47
+ };
48
+ if (window.MountRegistry) {
49
+ window.MountRegistry.hydrate(section, {
50
+ anime: window.anime,
51
+ Reveal: mockReveal,
52
+ });
53
+ count += mounts.length;
54
+ }
55
+ });
56
+ return count;
57
+ });
58
+ console.log("Hydrated " + mountCount + " mount(s)");
59
+
60
+ // Wait for React to render all components
61
+ await page.waitForTimeout(3000);
62
+
63
+ var sectionCount = await page.evaluate(function () {
64
+ return document.querySelectorAll(".reveal .slides section").length;
65
+ });
66
+ console.log("Sections (pages): " + sectionCount);
67
+
68
+ // Generate vector PDF
69
+ console.log("Generating PDF...");
70
+ await page.emulateMedia({ media: "print" });
71
+ var pdfBytes = await page.pdf({
72
+ width: SLIDE_W + "px",
73
+ height: SLIDE_H + "px",
74
+ printBackground: true,
75
+ preferCSSPageSize: true,
76
+ margin: { top: "0", right: "0", bottom: "0", left: "0" },
77
+ });
78
+
79
+ await fs.mkdir(path.dirname(OUT), { recursive: true });
80
+ await fs.writeFile(OUT, pdfBytes);
81
+ console.log("Wrote " + OUT + " (" + Math.round(pdfBytes.length / 1024) + " KB)");
82
+
83
+ await browser.close();
84
+ }
85
+
86
+ main().catch(function (err) { console.error(err); process.exit(1); });
@@ -0,0 +1,132 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const outDir = path.join(__dirname, "..", "..", "..", "examples", "microservice-rca", "fse26-paper-talk", "reveal-summary");
7
+
8
+ const componentName = process.argv[2];
9
+ const maxSteps = parseInt(process.argv[3] || "20", 10);
10
+
11
+ if (!componentName) {
12
+ console.error("usage: node src/reveal/tools/preview.mjs <ComponentName> [maxSteps]");
13
+ console.error("example: node src/reveal/tools/preview.mjs FaultHeatmap");
14
+ process.exit(1);
15
+ }
16
+
17
+ // Relative path from reveal-summary/ up to project root node_modules + src.
18
+ const REL = "../../../../";
19
+
20
+ const html = `<!DOCTYPE html>
21
+ <html lang="en">
22
+ <head>
23
+ <meta charset="utf-8">
24
+ <title>Preview: ${componentName}</title>
25
+ <style>
26
+ html, body { margin: 0; height: 100%; background: #fff; font-family: Arial, sans-serif; }
27
+ #stage {
28
+ position: absolute; inset: 0 0 56px 0;
29
+ display: flex; align-items: center; justify-content: center;
30
+ overflow: hidden;
31
+ }
32
+ /* Mock reveal DOM so getStep()/getCurrentSlide() work. */
33
+ .reveal .slides section.present { width: 100%; height: 100%; }
34
+ .block-mount { width: 100%; height: 100%; }
35
+ #controls {
36
+ position: absolute; left: 0; right: 0; bottom: 0; height: 56px;
37
+ display: flex; align-items: center; gap: 16px;
38
+ padding: 0 20px; box-sizing: border-box;
39
+ background: #1e1e1e; color: #eee;
40
+ }
41
+ #controls input[type=range] { flex: 1; }
42
+ #controls .label { font-size: 14px; white-space: nowrap; }
43
+ #controls .name { font-weight: 700; color: #7fd; }
44
+ /* Hidden fragment markers — getStep reads these. */
45
+ #fragments { display: none; }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <div id="stage">
50
+ <div class="reveal"><div class="slides">
51
+ <section class="present">
52
+ <div class="block-mount" data-mount-library="react" data-mount-export="${componentName}"></div>
53
+ <div id="fragments"></div>
54
+ </section>
55
+ </div></div>
56
+ </div>
57
+
58
+ <div id="controls">
59
+ <span class="label">component: <span class="name">${componentName}</span></span>
60
+ <input id="slider" type="range" min="-1" max="${maxSteps}" value="-1" step="1">
61
+ <span class="label">step: <span id="stepval">-1</span> / ${maxSteps}</span>
62
+ </div>
63
+
64
+ <script src="${REL}node_modules/animejs/dist/bundles/anime.umd.min.js"></script>
65
+ <script src="${REL}src/reveal/core/mount-registry.js"></script>
66
+ <script src="${REL}src/reveal/mounts/react-mount.js"></script>
67
+ <script src="components.js"></script>
68
+ <script>
69
+ (function () {
70
+ var MAX = ${maxSteps};
71
+ var slide = document.querySelector("section.present");
72
+ var fragHost = document.getElementById("fragments");
73
+
74
+ // Build one fragment marker per step so getStep() can read data-fragment-index.
75
+ var frags = [];
76
+ for (var i = 0; i <= MAX; i++) {
77
+ var f = document.createElement("span");
78
+ f.className = "fragment";
79
+ f.setAttribute("data-fragment-index", String(i));
80
+ fragHost.appendChild(f);
81
+ frags.push(f);
82
+ }
83
+
84
+ // Minimal mock Reveal compatible with useRevealStep()/getStep().
85
+ var listeners = {};
86
+ var MockReveal = {
87
+ on: function (evt, cb) { (listeners[evt] = listeners[evt] || []).push(cb); },
88
+ off: function (evt, cb) {
89
+ var arr = listeners[evt]; if (!arr) return;
90
+ var k = arr.indexOf(cb); if (k >= 0) arr.splice(k, 1);
91
+ },
92
+ getCurrentSlide: function () { return slide; },
93
+ fire: function (evt, payload) {
94
+ (listeners[evt] || []).slice().forEach(function (cb) { cb(payload || {}); });
95
+ },
96
+ };
97
+ window.Reveal = MockReveal;
98
+
99
+ function setStep(step) {
100
+ for (var i = 0; i <= MAX; i++) {
101
+ frags[i].classList.toggle("visible", i <= step);
102
+ }
103
+ document.getElementById("stepval").textContent = String(step);
104
+ // fragmentshown clears "instant" and re-syncs; covers both directions.
105
+ MockReveal.fire("fragmentshown", {});
106
+ }
107
+
108
+ var slider = document.getElementById("slider");
109
+ slider.addEventListener("input", function () { setStep(parseInt(slider.value, 10)); });
110
+
111
+ // Hydrate the mount, then push the initial step.
112
+ var ctx = { anime: window.anime, Reveal: MockReveal };
113
+ if (window.MountRegistry) window.MountRegistry.hydrate(slide, ctx);
114
+ setStep(-1);
115
+
116
+ // Keyboard: left/right arrows step through.
117
+ document.addEventListener("keydown", function (e) {
118
+ var v = parseInt(slider.value, 10);
119
+ if (e.key === "ArrowRight") { slider.value = Math.min(MAX, v + 1); setStep(parseInt(slider.value, 10)); }
120
+ if (e.key === "ArrowLeft") { slider.value = Math.max(-1, v - 1); setStep(parseInt(slider.value, 10)); }
121
+ });
122
+ })();
123
+ </script>
124
+ </body>
125
+ </html>
126
+ `;
127
+
128
+ const outPath = path.join(outDir, "preview.html");
129
+ fs.writeFileSync(outPath, html);
130
+ console.log(`Wrote ${outPath}`);
131
+ console.log(`Open http://localhost:8766/examples/microservice-rca/fse26-paper-talk/reveal-summary/preview.html`);
132
+ console.log(`(start the server with: node src/reveal/tools/dev.mjs — or npx serve -l 8766 .)`);
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2021",
4
+ "lib": ["ES2021", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "allowImportingTsExtensions": true
16
+ },
17
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "vite.config.ts"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }
@@ -0,0 +1,95 @@
1
+ import { defineConfig, type Plugin, type ViteDevServer } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import path from "path";
4
+ import fs from "fs";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const decksDir = path.resolve(__dirname, "src/reveal/decks");
9
+
10
+ function findDecks(): string[] {
11
+ return fs.readdirSync(decksDir).filter(function (name) {
12
+ return fs.existsSync(path.join(decksDir, name, "index.html"));
13
+ });
14
+ }
15
+
16
+ function selfContainDecks(): Plugin {
17
+ return {
18
+ name: "self-contain-decks",
19
+ closeBundle: function () {
20
+ const outDir = path.resolve(__dirname, "dist");
21
+ const sharedAssets = path.join(outDir, "assets");
22
+ if (!fs.existsSync(sharedAssets)) return;
23
+
24
+ findDecks().forEach(function (deck) {
25
+ const deckDir = path.join(outDir, deck);
26
+ if (!fs.existsSync(deckDir)) return;
27
+
28
+ const srcImages = path.join(decksDir, deck, "images");
29
+ if (fs.existsSync(srcImages)) {
30
+ const dstImages = path.join(deckDir, "images");
31
+ fs.mkdirSync(dstImages, { recursive: true });
32
+ fs.readdirSync(srcImages).forEach(function (f) {
33
+ fs.copyFileSync(path.join(srcImages, f), path.join(dstImages, f));
34
+ });
35
+ }
36
+
37
+ const deckAssets = path.join(deckDir, "assets");
38
+ fs.mkdirSync(deckAssets, { recursive: true });
39
+ fs.readdirSync(sharedAssets).forEach(function (f) {
40
+ fs.copyFileSync(path.join(sharedAssets, f), path.join(deckAssets, f));
41
+ });
42
+
43
+ const htmlPath = path.join(deckDir, "index.html");
44
+ if (fs.existsSync(htmlPath)) {
45
+ let html = fs.readFileSync(htmlPath, "utf-8");
46
+ html = html.replace(/\.\.\/assets\//g, "./assets/");
47
+ fs.writeFileSync(htmlPath, html);
48
+ }
49
+ });
50
+ },
51
+ };
52
+ }
53
+
54
+ function deckIndex(): Plugin {
55
+ return {
56
+ name: "deck-index",
57
+ configureServer: function (server: ViteDevServer) {
58
+ server.middlewares.use(function (req, res, next) {
59
+ if (req.url === "/" || req.url === "/index.html") {
60
+ const links = findDecks().map(function (d) {
61
+ return '<li><a href="/' + d + '/">' + d + "</a></li>";
62
+ }).join("");
63
+ res.setHeader("Content-Type", "text/html");
64
+ res.end("<h2>Decks</h2><ul>" + links + "</ul>");
65
+ return;
66
+ }
67
+ next();
68
+ });
69
+ },
70
+ };
71
+ }
72
+
73
+ const input: Record<string, string> = {};
74
+ findDecks().forEach(function (deck) {
75
+ input[deck] = path.join(decksDir, deck, "index.html");
76
+ });
77
+
78
+ export default defineConfig({
79
+ root: decksDir,
80
+ base: "./",
81
+ plugins: [react(), deckIndex(), selfContainDecks()],
82
+ build: {
83
+ outDir: path.resolve(__dirname, "dist"),
84
+ emptyOutDir: true,
85
+ rollupOptions: {
86
+ input: input,
87
+ },
88
+ },
89
+ server: {
90
+ port: 8766,
91
+ fs: {
92
+ allow: [path.resolve(__dirname)],
93
+ },
94
+ },
95
+ });
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "vite build",
8
+ "preview": "vite preview",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "animejs": "^4.4.1",
13
+ "d3-force": "^3.0.0",
14
+ "d3-hierarchy": "^3.1.2",
15
+ "d3-interpolate": "^3.0.1",
16
+ "d3-sankey": "^0.12.3",
17
+ "d3-scale": "^4.0.2",
18
+ "d3-shape": "^3.2.0",
19
+ "framer-motion": "^12.40.0",
20
+ "katex": "^0.17.0",
21
+ "lucide-react": "^1.21.0",
22
+ "prismjs": "^1.30.0",
23
+ "react": "^19.2.7",
24
+ "react-dom": "^19.2.7",
25
+ "reveal.js": "^6.0.1",
26
+ "roughjs": "^4.6.6"
27
+ },
28
+ "devDependencies": {
29
+ "@types/d3-force": "^3.0.10",
30
+ "@types/d3-hierarchy": "^3.1.7",
31
+ "@types/d3-interpolate": "^3.0.4",
32
+ "@types/d3-sankey": "^0.12.5",
33
+ "@types/d3-scale": "^4.0.9",
34
+ "@types/d3-shape": "^3.1.8",
35
+ "@types/node": "^26.0.0",
36
+ "@types/react": "^19.2.17",
37
+ "@types/react-dom": "^19.2.3",
38
+ "@vitejs/plugin-react": "^6.0.2",
39
+ "typescript": "^6.0.3",
40
+ "vite": "^8.0.16"
41
+ }
42
+ }