@kadal/deck 0.0.0-stage → 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 (157) hide show
  1. package/LICENSE +661 -0
  2. package/LICENSING.md +40 -0
  3. package/README.md +170 -2
  4. package/dist/_virtual/_rolldown/runtime.js +13 -0
  5. package/dist/audience/index.d.ts +14 -0
  6. package/dist/audience/index.js +680 -0
  7. package/dist/backgrounds/index.d.ts +65 -0
  8. package/dist/backgrounds/index.js +604 -0
  9. package/dist/backgrounds/shared.d.ts +7 -0
  10. package/dist/backgrounds/shared.js +89 -0
  11. package/dist/cli/bin.d.ts +1 -0
  12. package/dist/cli/bin.js +8 -0
  13. package/dist/cli/host.js +111 -0
  14. package/dist/cli/index.d.ts +4 -0
  15. package/dist/cli/index.js +140 -0
  16. package/dist/cli/login.js +56 -0
  17. package/dist/cli/publish.js +159 -0
  18. package/dist/cli/vite.js +39 -0
  19. package/dist/compiler/code-meta.d.ts +33 -0
  20. package/dist/compiler/code-meta.js +81 -0
  21. package/dist/compiler/index.d.ts +8 -0
  22. package/dist/compiler/index.js +8 -0
  23. package/dist/compiler/magic-move.d.ts +26 -0
  24. package/dist/compiler/magic-move.js +33 -0
  25. package/dist/compiler/plain-text.js +7 -0
  26. package/dist/compiler/rehype-code.d.ts +18 -0
  27. package/dist/compiler/rehype-code.js +66 -0
  28. package/dist/compiler/rehype-glossary.d.ts +35 -0
  29. package/dist/compiler/rehype-glossary.js +211 -0
  30. package/dist/compiler/slots.d.ts +3 -0
  31. package/dist/compiler/slots.js +36 -0
  32. package/dist/compiler/split.d.ts +63 -0
  33. package/dist/compiler/split.js +195 -0
  34. package/dist/components/Background.d.ts +15 -0
  35. package/dist/components/Background.js +24 -0
  36. package/dist/components/Click.d.ts +54 -0
  37. package/dist/components/Click.js +129 -0
  38. package/dist/components/Code.d.ts +27 -0
  39. package/dist/components/Code.js +124 -0
  40. package/dist/components/Motion.d.ts +25 -0
  41. package/dist/components/Motion.js +45 -0
  42. package/dist/components/Sketch.d.ts +44 -0
  43. package/dist/components/Sketch.js +141 -0
  44. package/dist/components/Term.d.ts +63 -0
  45. package/dist/components/Term.js +492 -0
  46. package/dist/components/bling.d.ts +75 -0
  47. package/dist/components/bling.js +146 -0
  48. package/dist/components/content.d.ts +232 -0
  49. package/dist/components/content.js +637 -0
  50. package/dist/components/index.d.ts +10 -0
  51. package/dist/components/index.js +88 -0
  52. package/dist/core/clicks.d.ts +45 -0
  53. package/dist/core/clicks.js +75 -0
  54. package/dist/core/hash.d.ts +33 -0
  55. package/dist/core/hash.js +49 -0
  56. package/dist/core/keyboard.d.ts +17 -0
  57. package/dist/core/keyboard.js +198 -0
  58. package/dist/core/layout.d.ts +16 -0
  59. package/dist/core/layout.js +25 -0
  60. package/dist/core/model.d.ts +259 -0
  61. package/dist/core/model.js +377 -0
  62. package/dist/core/navigation.d.ts +74 -0
  63. package/dist/core/navigation.js +244 -0
  64. package/dist/core/poster.js +30 -0
  65. package/dist/core/store.d.ts +20 -0
  66. package/dist/core/store.js +64 -0
  67. package/dist/core/touch.js +27 -0
  68. package/dist/draw/DrawingLayer.js +315 -0
  69. package/dist/draw/strokes.js +188 -0
  70. package/dist/export/index.d.ts +68 -0
  71. package/dist/export/index.js +251 -0
  72. package/dist/export/run.js +81 -0
  73. package/dist/glossary/model.d.ts +81 -0
  74. package/dist/glossary/model.js +156 -0
  75. package/dist/glossary/parse.d.ts +27 -0
  76. package/dist/glossary/parse.js +90 -0
  77. package/dist/index.d.ts +31 -0
  78. package/dist/index.js +30 -0
  79. package/dist/live/LiveProvider.d.ts +20 -0
  80. package/dist/live/LiveProvider.js +450 -0
  81. package/dist/live/analytics.d.ts +15 -0
  82. package/dist/live/analytics.js +103 -0
  83. package/dist/live/client.d.ts +51 -0
  84. package/dist/live/client.js +193 -0
  85. package/dist/live/components.d.ts +64 -0
  86. package/dist/live/components.js +471 -0
  87. package/dist/live/context.d.ts +15 -0
  88. package/dist/live/context.js +28 -0
  89. package/dist/live/index.d.ts +7 -0
  90. package/dist/live/index.js +7 -0
  91. package/dist/live/protocol.d.ts +284 -0
  92. package/dist/live/protocol.js +42 -0
  93. package/dist/live/turnstile.js +49 -0
  94. package/dist/presenter/index.d.ts +20 -0
  95. package/dist/presenter/index.js +1211 -0
  96. package/dist/react/AutoAnimate.js +92 -0
  97. package/dist/react/Backgrounds.d.ts +11 -0
  98. package/dist/react/Backgrounds.js +154 -0
  99. package/dist/react/ConfettiLayer.js +85 -0
  100. package/dist/react/Deck.d.ts +35 -0
  101. package/dist/react/Deck.js +983 -0
  102. package/dist/react/Frame.d.ts +16 -0
  103. package/dist/react/Frame.js +174 -0
  104. package/dist/react/Hints.d.ts +3 -0
  105. package/dist/react/Hints.js +201 -0
  106. package/dist/react/Lightbox.js +54 -0
  107. package/dist/react/Overview.js +93 -0
  108. package/dist/react/PrintView.js +128 -0
  109. package/dist/react/ScrollView.js +128 -0
  110. package/dist/react/SlideFrame.d.ts +51 -0
  111. package/dist/react/SlideFrame.js +323 -0
  112. package/dist/react/Stage.js +151 -0
  113. package/dist/react/ThemeMenu.d.ts +5 -0
  114. package/dist/react/ThemeMenu.js +124 -0
  115. package/dist/react/Zoom.js +23 -0
  116. package/dist/react/chrome.js +339 -0
  117. package/dist/react/context.d.ts +165 -0
  118. package/dist/react/context.js +19 -0
  119. package/dist/react/hooks.d.ts +33 -0
  120. package/dist/react/hooks.js +70 -0
  121. package/dist/react/layouts.d.ts +21 -0
  122. package/dist/react/layouts.js +104 -0
  123. package/dist/react/mdx.d.ts +4 -0
  124. package/dist/react/mdx.js +33 -0
  125. package/dist/react/terms.d.ts +6 -0
  126. package/dist/react/terms.js +66 -0
  127. package/dist/renderers/Island.d.ts +57 -0
  128. package/dist/renderers/Island.js +223 -0
  129. package/dist/renderers/contract.d.ts +173 -0
  130. package/dist/renderers/contract.js +152 -0
  131. package/dist/renderers/index.d.ts +12 -0
  132. package/dist/renderers/index.js +16 -0
  133. package/dist/renderers/react.d.ts +16 -0
  134. package/dist/renderers/react.js +32 -0
  135. package/dist/renderers/web-components.d.ts +4 -0
  136. package/dist/renderers/web-components.js +103 -0
  137. package/dist/sketch/Canvas.js +49 -0
  138. package/dist/sketch/dsl.d.ts +131 -0
  139. package/dist/sketch/dsl.js +281 -0
  140. package/dist/sketch/index.d.ts +4 -0
  141. package/dist/sketch/index.js +3 -0
  142. package/dist/sketch/palette.d.ts +21 -0
  143. package/dist/sketch/palette.js +66 -0
  144. package/dist/sketch/render.d.ts +43 -0
  145. package/dist/sketch/render.js +198 -0
  146. package/dist/themes/base.css +1704 -0
  147. package/dist/themes/bling.css +592 -0
  148. package/dist/themes/glossary.css +317 -0
  149. package/dist/themes/live.css +1557 -0
  150. package/dist/themes/minimal.css +83 -0
  151. package/dist/themes/rla.css +92 -0
  152. package/dist/themes/sketch.css +121 -0
  153. package/dist/themes/stack.css +62 -0
  154. package/dist/vite/index.d.ts +15 -0
  155. package/dist/vite/index.js +320 -0
  156. package/package.json +159 -4
  157. package/tools/verify-island.mjs +150 -0
package/package.json CHANGED
@@ -1,6 +1,161 @@
1
1
  {
2
2
  "name": "@kadal/deck",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Kadal Deck: presentations written in MDX, with reveal.js and Slidev features, a live room for the audience, islands in any framework, and export to PDF, PNG and PPTX.",
5
+ "keywords": [
6
+ "presentation",
7
+ "slides",
8
+ "mdx",
9
+ "deck",
10
+ "reveal.js",
11
+ "slidev",
12
+ "vite",
13
+ "react",
14
+ "live",
15
+ "audience"
16
+ ],
17
+ "homepage": "https://deck.kadal.cc",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/kadalcc/deck.git",
21
+ "directory": "packages/deck"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/kadalcc/deck/issues"
25
+ },
26
+ "license": "AGPL-3.0-or-later",
27
+ "author": "Mithunish Prabhakaran <pmithunish@gmail.com>",
28
+ "type": "module",
29
+ "sideEffects": [
30
+ "**/*.css",
31
+ "./dist/backgrounds/*.js",
32
+ "./dist/renderers/index.js"
33
+ ],
34
+ "files": [
35
+ "dist",
36
+ "LICENSE",
37
+ "LICENSING.md",
38
+ "README.md",
39
+ "tools/verify-island.mjs"
40
+ ],
41
+ "bin": {
42
+ "kadal-deck": "dist/cli/bin.js"
43
+ },
44
+ "main": "./dist/index.js",
45
+ "types": "./dist/index.d.ts",
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/index.d.ts",
49
+ "import": "./dist/index.js"
50
+ },
51
+ "./vite": {
52
+ "types": "./dist/vite/index.d.ts",
53
+ "import": "./dist/vite/index.js"
54
+ },
55
+ "./compiler": {
56
+ "types": "./dist/compiler/index.d.ts",
57
+ "import": "./dist/compiler/index.js"
58
+ },
59
+ "./live": {
60
+ "types": "./dist/live/index.d.ts",
61
+ "import": "./dist/live/index.js"
62
+ },
63
+ "./protocol": {
64
+ "types": "./dist/live/protocol.d.ts",
65
+ "import": "./dist/live/protocol.js"
66
+ },
67
+ "./presenter": {
68
+ "types": "./dist/presenter/index.d.ts",
69
+ "import": "./dist/presenter/index.js"
70
+ },
71
+ "./audience": {
72
+ "types": "./dist/audience/index.d.ts",
73
+ "import": "./dist/audience/index.js"
74
+ },
75
+ "./backgrounds": {
76
+ "types": "./dist/backgrounds/index.d.ts",
77
+ "import": "./dist/backgrounds/index.js"
78
+ },
79
+ "./renderers": {
80
+ "types": "./dist/renderers/index.d.ts",
81
+ "import": "./dist/renderers/index.js"
82
+ },
83
+ "./renderers/contract": {
84
+ "types": "./dist/renderers/contract.d.ts",
85
+ "import": "./dist/renderers/contract.js"
86
+ },
87
+ "./sketch": {
88
+ "types": "./dist/sketch/index.d.ts",
89
+ "import": "./dist/sketch/index.js"
90
+ },
91
+ "./export": {
92
+ "types": "./dist/export/index.d.ts",
93
+ "import": "./dist/export/index.js"
94
+ },
95
+ "./styles.css": "./dist/themes/base.css",
96
+ "./themes/*": "./dist/themes/*",
97
+ "./package.json": "./package.json"
98
+ },
99
+ "publishConfig": {
100
+ "access": "public",
101
+ "provenance": true
102
+ },
103
+ "engines": {
104
+ "node": ">=20.19"
105
+ },
106
+ "scripts": {
107
+ "build": "tsdown",
108
+ "typecheck": "tsc --noEmit",
109
+ "lint": "oxlint src",
110
+ "format": "oxfmt src",
111
+ "test": "bun test",
112
+ "verify:island": "node tools/verify-island.mjs",
113
+ "prepack": "tsdown"
114
+ },
115
+ "dependencies": {
116
+ "@excalidraw/excalidraw": "0.18.1",
117
+ "@excalidraw/mermaid-to-excalidraw": "2.2.2",
118
+ "@mdx-js/mdx": "^3.1.1",
119
+ "katex": "^0.16.22",
120
+ "lucide-react": "^1.18.0",
121
+ "mermaid": "^11.4.1",
122
+ "motion": "^12.23.24",
123
+ "qrcode": "^1.5.4",
124
+ "rehype-katex": "^7.0.1",
125
+ "remark-frontmatter": "^5.0.0",
126
+ "remark-gfm": "^4.0.1",
127
+ "remark-math": "^6.0.0",
128
+ "shiki": "^3.13.0",
129
+ "shiki-magic-move": "^1.1.0",
130
+ "unist-util-visit": "^5.0.0",
131
+ "yaml": "^2.8.0"
132
+ },
133
+ "devDependencies": {
134
+ "@types/hast": "^3.0.5",
135
+ "@types/qrcode": "^1.5.5",
136
+ "@types/react": "^19.2.0",
137
+ "@types/react-dom": "^19.2.0",
138
+ "typescript": "^6.0.3",
139
+ "vite": "^8.0.16",
140
+ "@types/node": "^25.9.3",
141
+ "playwright": "1.61.1",
142
+ "pptxgenjs": "^3.12.0",
143
+ "react": "^19.2.7",
144
+ "react-dom": "^19.2.7"
145
+ },
146
+ "peerDependencies": {
147
+ "react": "^19.2.0",
148
+ "react-dom": "^19.2.0",
149
+ "vite": "^7.0.0 || ^8.0.0",
150
+ "playwright": "^1.50.0",
151
+ "pptxgenjs": "^3.12.0"
152
+ },
153
+ "peerDependenciesMeta": {
154
+ "playwright": {
155
+ "optional": true
156
+ },
157
+ "pptxgenjs": {
158
+ "optional": true
159
+ }
160
+ }
161
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Does a framework renderer actually work on a slide?
3
+ *
4
+ * Every renderer in the engine's framework list lands with a showcase slide, and this is what turns
5
+ * that slide into a test. It runs against a BUILT deck, not the dev server, because the two things
6
+ * most worth knowing are about bundling and teardown and neither is honest in dev:
7
+ *
8
+ * 1. the framework's runtime is NOT in the first chunk — zero requests for it on slide one
9
+ * 2. it is fetched on arrival at the slide that needs it
10
+ * 3. the island mounts and is genuinely running, not just rendered once
11
+ * 4. stepping a click changes the island's props WITHOUT restarting it — the one measurement that
12
+ * distinguishes a renderer's `update` from a remount, and so whether a chart on a slide keeps
13
+ * its animation
14
+ * 5. leaving the slide tears it down, and coming back mounts it again
15
+ *
16
+ * The slide's component has to cooperate on two counts: a monotonically increasing attribute that
17
+ * only a live loop can advance, and a `{ mounted, destroyed }` tally on `window`. See
18
+ * `examples/showcase/src/vue-ticker.ts` for the shape to copy.
19
+ *
20
+ * bun packages/deck/tools/verify-island.mjs \
21
+ * --base http://localhost:4178/showcase --slide 3 --framework vue \
22
+ * --tally __vueIsland --ticks data-vue-ticks --step data-vue-step --chunks 'runtime-core|vue-ticker'
23
+ *
24
+ * `--settle <ms>` waits longer before looking, for a framework that has to download an interpreter.
25
+ * `--eager` is for an island that belongs in the first chunk — the framework-less baseline — and
26
+ * inverts checks 1 and 2 instead of skipping them.
27
+ */
28
+
29
+ import { chromium } from "playwright";
30
+
31
+ const argv = process.argv.slice(2);
32
+ const arg = (name, fallback) => {
33
+ const at = argv.indexOf(`--${name}`);
34
+ return at >= 0 && argv[at + 1] ? argv[at + 1] : fallback;
35
+ };
36
+
37
+ const BASE = arg("base", "http://localhost:4178/showcase").replace(/\/$/, "");
38
+ const SLIDE = Number(arg("slide", "3"));
39
+ const AWAY = Number(arg("away", String(SLIDE + 4)));
40
+ const FRAMEWORK = arg("framework", "vue");
41
+ const TALLY = arg("tally", "__vueIsland");
42
+ const TICKS = arg("ticks", "data-vue-ticks");
43
+ const STEP = arg("step", "data-vue-step");
44
+ const CHUNKS = new RegExp(arg("chunks", FRAMEWORK));
45
+ // Pyodide is six megabytes and a second or two of startup; everything else is here in a frame.
46
+ const SETTLE = Number(arg("settle", "1500"));
47
+ // An island that is deliberately in the first chunk — the custom-element baseline is statically
48
+ // imported, having no framework to keep out of it. The bundle assertions invert rather than vanish.
49
+ const EAGER = argv.includes("--eager");
50
+
51
+ const results = [];
52
+ const say = (ok, line) => {
53
+ results.push(`${ok ? "PASS" : "FAIL"} ${line}`);
54
+ if (!ok) process.exitCode = 1;
55
+ };
56
+
57
+ const browser = await chromium.launch();
58
+ const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
59
+
60
+ // The live room has no worker behind a static preview; its retries are not what is under test.
61
+ // The live room has no worker behind a static preview: its socket retries and the 5xx from
62
+ // the dev proxy are the environment, not the island.
63
+ const expected = /WebSocket|\/api\/rooms\/|\/beacon|Failed to load resource.*status of 5\d\d/;
64
+ const errors = [];
65
+ page.on("console", (m) => m.type() === "error" && !expected.test(m.text()) && errors.push(m.text()));
66
+ page.on("pageerror", (e) => errors.push(String(e)));
67
+
68
+ const fetched = [];
69
+ page.on("request", (r) => fetched.push(r.url()));
70
+ const frameworkChunks = () => fetched.filter((u) => CHUNKS.test(u));
71
+
72
+ // Slide one: nothing about this framework should be on the wire for a deck that has not reached it.
73
+ await page.goto(`${BASE}/#/0`, { waitUntil: "networkidle" });
74
+ await page.waitForTimeout(1200);
75
+ say(
76
+ EAGER ? frameworkChunks().length === 0 : frameworkChunks().length === 0,
77
+ `${FRAMEWORK} is not in the first load: ${frameworkChunks().length} requests on slide one`,
78
+ );
79
+
80
+ await page.goto(`${BASE}/#/${SLIDE}`, { waitUntil: "networkidle" });
81
+ await page.waitForTimeout(SETTLE);
82
+ const arrived = frameworkChunks().map((u) => u.split("/").pop());
83
+ say(
84
+ EAGER ? arrived.length === 0 : arrived.length > 0,
85
+ EAGER
86
+ ? `in the first chunk by design, nothing fetched on arrival (${arrived.length} requests)`
87
+ : `fetched on arrival: ${arrived.join(", ") || "nothing"}`,
88
+ );
89
+
90
+ // Scoped by the ticks attribute, not just the framework name: one renderer can serve several
91
+ // islands. `wasm` draws both the hand-written module and the Leptos one, which is the ABI working
92
+ // as intended and makes the framework name alone ambiguous.
93
+ const island = page.locator(
94
+ `.deck-slide.is-present .deck-island[data-framework="${FRAMEWORK}"]:has([${TICKS}])`,
95
+ );
96
+ const count = await island.count();
97
+ say(count === 1, `exactly one ${FRAMEWORK}/${TICKS} island on slide ${SLIDE} (found ${count})`);
98
+ if (count !== 1) {
99
+ const seen = await page
100
+ .locator(".deck-slide.is-present .deck-island")
101
+ .evaluateAll((els) => els.map((e) => `${e.dataset.framework} · ${e.className}`));
102
+ results.push(` islands present: ${JSON.stringify(seen)}`);
103
+ console.log(results.join("\n"));
104
+ await browser.close();
105
+ process.exit(1);
106
+ }
107
+
108
+ const body = island.locator(`[${TICKS}]`);
109
+ say((await body.count()) === 1, `the component rendered into the island host`);
110
+
111
+ // It is running, not merely rendered: only a live loop advances this.
112
+ const first = Number(await body.getAttribute(TICKS));
113
+ await page.waitForTimeout(600);
114
+ const second = Number(await body.getAttribute(TICKS));
115
+ say(second > first, `the island is alive: ${TICKS} ${first} → ${second}`);
116
+
117
+ // THE POINT. Step a click; the step must change while the count keeps climbing.
118
+ say((await body.getAttribute(STEP)) === "0", "starts at deck click 0");
119
+ await page.keyboard.press("ArrowRight");
120
+ await page.waitForTimeout(500);
121
+ const stepped = await body.getAttribute(STEP);
122
+ const third = Number(await body.getAttribute(TICKS));
123
+ say(stepped === "1", `new props arrived: ${STEP} ${stepped}`);
124
+ say(third > second, `UPDATED, NOT REMOUNTED: ${TICKS} kept climbing ${second} → ${third}`);
125
+
126
+ const afterUpdate = await page.evaluate((key) => window[key], TALLY);
127
+ say(
128
+ afterUpdate?.mounted === 1 && afterUpdate?.destroyed === 0,
129
+ `one mount, no teardown across the prop change: ${JSON.stringify(afterUpdate)}`,
130
+ );
131
+
132
+ // Leaving must stop the loop, or nineteen slides later a phone is still counting.
133
+ await page.goto(`${BASE}/#/${AWAY}`, { waitUntil: "networkidle" });
134
+ await page.waitForTimeout(Math.max(900, SETTLE / 2));
135
+ const afterLeave = await page.evaluate((key) => window[key], TALLY);
136
+ say(afterLeave?.destroyed === 1, `torn down on leaving: ${JSON.stringify(afterLeave)}`);
137
+ say((await page.locator(`[${TICKS}]`).count()) === 0, "no DOM left behind");
138
+
139
+ await page.goto(`${BASE}/#/${SLIDE}`, { waitUntil: "networkidle" });
140
+ await page.waitForTimeout(SETTLE);
141
+ const back = await page.evaluate((key) => window[key], TALLY);
142
+ say(back?.mounted === 2, `remounts on return: ${JSON.stringify(back)}`);
143
+
144
+ say(
145
+ errors.length === 0,
146
+ `console clean (${errors.length} errors)${errors.length ? `: ${errors.slice(0, 3).join(" | ")}` : ""}`,
147
+ );
148
+
149
+ await browser.close();
150
+ console.log(results.join("\n"));