@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/LICENSING.md ADDED
@@ -0,0 +1,40 @@
1
+ # Licensing
2
+
3
+ **Kadal Deck** (`@kadal/deck` and the `@kadal/deck-*` renderers) is dual-licensed.
4
+
5
+ ## 1 · AGPL-3.0-or-later (the default)
6
+
7
+ The engine is free software under the **GNU Affero General Public License, version 3 or later** —
8
+ the full text is in [`LICENSE`](./LICENSE). You may use it, change it, and build on it, for anything,
9
+ including commercially.
10
+
11
+ The one obligation that matters in practice: **if you run a modified version of this engine as a
12
+ network service, the people using that service must be able to get your modified source.** That is
13
+ AGPL §13, and it is the whole reason this licence was chosen rather than MIT. Running an unmodified
14
+ copy, embedding the engine in a site you publish, or presenting with it, triggers nothing.
15
+
16
+ If you are self-hosting your own decks, this licence is all you need and you owe nobody anything.
17
+
18
+ ## 2 · Commercial licence
19
+
20
+ If you want to build a **hosted or proprietary product on top of the engine** without publishing
21
+ your own source, a commercial licence removes the AGPL obligations. That is the arrangement that
22
+ funds the engine's development, in the same way Slides.com funds reveal.js.
23
+
24
+ Ask: **pmithunish@gmail.com**.
25
+
26
+ ## Why this split
27
+
28
+ The engine is the part that should belong to everyone: the compiler, the runtime, the components,
29
+ the themes, the export, the framework renderers. A deck you wrote should keep rendering with no
30
+ account, no network and no company, for as long as a browser exists.
31
+
32
+ The parts that cost money to run — the live room, publishing, accounts, the share window's hosted
33
+ half, analytics — are the product. That boundary is honest about where the expense actually is:
34
+ a Durable Object holding a room for thirty phones is somebody's bill.
35
+
36
+ ## What a contributor is agreeing to
37
+
38
+ Contributions are accepted under the AGPL, and dual licensing means they may also appear in the
39
+ commercial offering. A CLA will be added before the repository opens to outside contributions; until
40
+ then the only contributor is the author.
package/README.md CHANGED
@@ -1,3 +1,171 @@
1
- # Temporary Holding Version
1
+ # @kadal/deck
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Kadal Deck** — presentations written in MDX. Every reveal.js and Slidev feature, a live room so
4
+ the audience can follow, react, ask and vote from their phones, islands in any framework, and
5
+ export to PDF, PNG and PPTX. Docs: **https://deck.kadal.cc/docs/**
6
+
7
+ ## Start a deck
8
+
9
+ ```sh
10
+ npm create @kadal/deck@latest my-talk
11
+ cd my-talk && npm install
12
+ npm run dev # edit deck.mdx; the page reloads as you type
13
+ npx kadal-deck build # dist/, for any static host
14
+ npx kadal-deck login # once per machine
15
+ npx kadal-deck publish # https://deck.kadal.cc/@you/my-talk/ with a live room
16
+ ```
17
+
18
+ Slides split at `---`, vertical slides at `----`. YAML between the dashes is that slide's
19
+ frontmatter; the block at the top is the headmatter (deck settings and the title slide).
20
+ `deck.config.ts` overrides the headmatter; every option is documented in `src/core/model.ts`.
21
+
22
+ ## The CLI
23
+
24
+ | command | what |
25
+ | --- | --- |
26
+ | `kadal-deck dev` | Vite dev server for the deck in this folder |
27
+ | `kadal-deck build` | `dist/` for any static host |
28
+ | `kadal-deck export [--png] [--pptx]` | PDF (and PNG/PPTX) into `export/` — install `playwright` (and `pptxgenjs` for PPTX) |
29
+ | `kadal-deck login` / `logout` / `whoami` | sign this machine in to a Kadal Deck host (device code, opens the browser) |
30
+ | `kadal-deck publish [--slug] [--workspace] [--title]` | build for the host and publish; only changed files are uploaded |
31
+
32
+ `KADAL_DECK_TOKEN` (an API token from the dashboard) and `KADAL_DECK_HOST` override the saved
33
+ login, which is how CI publishes.
34
+
35
+ ## Writing slides
36
+
37
+ Markdown, with these on top:
38
+
39
+ | What | How |
40
+ | --- | --- |
41
+ | Steps (fragments / clicks) | `<Click>…</Click>`, `<After>`, `<Clicks>` around a list; `at={3}`, `at="+2"`, `at={[2,5]}`, `hide`, `effect="fade-up"` |
42
+ | Code that steps | ```` ```ts {1\|3-5\|all} {lines:true} [title.ts] ```` |
43
+ | Magic move | ````` ````md magic-move ````` holding one fence per step |
44
+ | Math | `$inline$`, `$$ display $$`, `<Tex tex="…" />` |
45
+ | Diagrams | `<Mermaid>{`flowchart LR …`}</Mermaid>`; hand-drawn: `<Sketch scene={…} steps={[…]} />`, `<Sketch src="x.excalidraw" />`, `<Sketch>{`flowchart …`}</Sketch>` |
46
+ | Notes | `<Notes>… [click] …</Notes>` — the presenter view splits them at `[click]` |
47
+ | Layouts | `layout: cover \| center \| section \| two-cols \| image-right \| iframe-left \| fact \| quote …`; `::right::` names a slot |
48
+ | Backgrounds | `background: aurora` (also mesh, waves, lines, beams, sparkles, starfield, grid, dots, noise, spotlight), a colour, a gradient, an image, a video, an iframe, or `{ component: starfield, count: 400 }` |
49
+ | Transitions | `transition: slide \| fade \| convex \| concave \| zoom \| none \| view-transition`, `-in`/`-out`, `transitionSpeed`, `backgroundTransition` |
50
+ | Auto-animate | `autoAnimate: true` on consecutive slides; matching `data-id`s move |
51
+ | Motion | `<Motion initial enter clicks={{ "click-1": {…} }}>` |
52
+ | Bits | `Cols/Col`, `Grid`, `Callout`, `Badge`, `Kbd`, `Counter`, `Typewriter`, `Encrypted`, `Mark`, `Confetti`, `Timer`, `Qr`, `Toc`, `Image preview`, `Youtube`, `Tweet`, `Iframe`, `FitText`, `Arrow`, `Transform`, `Absolute`, `FlipBoard` |
53
+ | Live | `<QrJoin />`, `<Presence />`, `<Reactions />`, `<Poll id question options kind="choice\|multi\|rating" />`, `<WordCloud id question />`, `<Quiz id question options correct seconds />`, `<Leaderboard />`, `<Questions />`, `<Pace />` |
54
+ | Per-slide | `class`, `colorScheme: dark`, `transition`, `clicks`, `hide`, `hideInToc`, `timing`, `zoom`, `state` |
55
+
56
+ Cards that share a row share a height: a `<Glass>` or `<Callout>` that is the only thing in its `<Col>` (or in the `<Click>` that reveals it) fills the cell, unless the row says `align="start"`; give your own card component the `deck-card` class to join in. Your own components go in `src/components.tsx` and are available in `deck.mdx` without imports; `layout:name` entries register custom layouts.
57
+
58
+ ## Keys
59
+
60
+ `?` shows the sheet. Vim first: `j`/`k` next and previous step through everything, `h`/`l` previous and next column (steps first, then the column — reveal.js's arrows do the same), `gg`/`G` first and last, `/` search, `:12` jump, `f` **focus** — every link, button, glossary term, image preview and embedded page on the slide gets a home-row label; type it to use it (Vimium's f). Also Space/N, `↓`/`↑` for the vertical stack, `O` overview, `T` theme menu, `⇧F` fullscreen, `B` blackout, `D` dark/light, `C` draw, `X` laser, `S` presenter view, `Z` zoom, `Esc`.
61
+
62
+ ## Themes and looks
63
+
64
+ `T` (or the palette button) opens the theme menu on every view — play, presenter, audience: the themes the deck ships (`config.themes`, whose CSS its main.tsx imports) and light or dark. Each screen keeps its own choice per deck in the browser.
65
+
66
+ Backgrounds (`background: name` in frontmatter, `{ component, ...props }` for props, or `<Background name>` inside a slide): mesh, aurora, waves, lines, beams, sparkles, starfield, grid, dots, noise, spotlight, the quieter set — topo (contours, with index lines), glows, graticule, hex, constellation, vignette, rays, stripes, sweep — and the map room: **globe** (a wire globe off to one side, turning slowly, day and night sides), **lattice** (a grid zooming into one cell three levels deep), **thiessen** (drifting seeds under their Voronoi polygons), **cells** (reports landing on a lattice, rippling, going dark), **compass** (a rose with its degree ring turning), **neatline** (a map sheet's frame with coordinate ticks, north arrow and scale bar), **scan** (a graticule swept by a satellite pass), **track** (a GPS trail with a pulsing head), **raster** (a choropleth of cells classed by a drifting field, with its legend), **plane** (a dotted ground plane in oblique projection — the lattice an exploded figure stands on — with surveyor's control points at every `major`-th point, thinning toward a `horizon` and away from `focus` so words beside the figure sit on clean paper, and one slow glint crossing it; a figure that carries `data-plane="origin"` and `data-plane="unit"` marks has the floor locked onto it, point for point, at any size). All pause off-slide and hold a still frame under reduced motion; colours are CSS strings, so `rgba()` sets their weight. With a component, `color` is the component's stroke and `fill` (or `tint`) washes the layer behind it — a soft diagonal gradient of that one colour, strongest top-left, never a flat block (`background: { component: topo, color: "rgba(5,150,105,0.55)", fill: "rgba(5,150,105,0.12)" }`); `tint:` alone does the same on a slide without a component, where plain `color:` stays flat.
67
+
68
+ Bling components, all reading the theme's tokens: `<GradientText>`, `<Glass tone glow>`, `<Glow>`, `<Chip tone icon>`, `<Icon name>` (any lucide icon), `<Divider label>`, `<Ribbon>`, `<Halo>`, `<Shimmer>`, `<Blob>`, `<Tilt>`. Tones: accent, accent-2, good, warn, bad, info, neutral, purple, pink, amber, teal.
69
+
70
+ ## Terms
71
+
72
+ Key concepts and abbreviations explain themselves. A `glossary.yaml` beside `deck.mdx` holds them; the compiler links the **first mention of each term on every slide** (body text first, a heading only when the body never says it; code, maths, links, controls and self-painting text like `<GradientText>` are left alone), and the runtime shows the definition in a card. Rest the pointer on a dotted word: the cursor fills like a pie and the underline draws itself solid for `delay` ms — sweeping across a slide sets nothing off — then the card opens, anchored to the word, inside the slide's canvas. A click or a tap opens it at once and pins it; `f` labels every term, so the keyboard reaches them; Escape, a press elsewhere or the next step closes it. The card never drops below readable type: on a small window it grows against the canvas's scale.
73
+
74
+ ```yaml
75
+ # glossary.yaml — the key is the card's title and the first string matched
76
+ GNSS: Satellite positioning — GPS, NavIC, Galileo and the rest. # short form
77
+ UTM:
78
+ full: Universal Transverse Mercator # an abbreviation's expansion
79
+ def: Sixty projections that turn latitude and longitude into **metres**. # inline Markdown, $maths$
80
+ more: Chennai sits in zone 44N. # a second, quieter paragraph
81
+ aliases: [UTM zone 44N] # other spellings; plurals are found on their own
82
+ tag: Projection # the category chip
83
+ link: https://epsg.io/32644 # a source (or { href, label })
84
+ see: [WGS84, EPSG] # chips that swap the card to a related term
85
+ auto: false # never linked automatically — a word too common to trust
86
+ ```
87
+
88
+ ```mdx
89
+ <Term>UTM</Term> {/* the entry, by name or alias */}
90
+ <Term id="utm">projected metres</Term> {/* other words, same entry */}
91
+ <Term def="Root-mean-square error." tag="Statistics">RMSE</Term> {/* a one-off, no entry */}
92
+ ```
93
+
94
+ A name with no lowercase letter, or shorter than four characters, matches in its exact case only (`WHO`, never "who"); part of a hyphenated compound is a mention only for such an abbreviation (`GNSS-derived`, not `cell-level`). Definitions — a one-off's too — are rendered at build time, KaTeX included, so the browser loads no Markdown or maths code for them; a YAML mistake or an unknown `<Term>` is a build **warning**, never a failed build. Per slide, `terms: false` links nothing automatically and `terms: { skip: [GIS] }` leaves single terms out (the first slide shares the headmatter, which is why this key is not `glossary`). Deck-wide, in the headmatter: `glossary: terms.yaml` (another path), `glossary: false`, or `glossary: { repeat: all, headings: false | true | fallback, skip: [MyComponent], delay: 700, sync: false }`. Words that reach a component as a prop are out of the compiler's reach; link them where they render with `<Terms>{label}</Terms>`.
95
+
96
+ **Sharing.** A card is the presenter's own aid until the presenter view's **Share terms** is on (off by default, remembered per deck; `glossary: { sync: true }` starts it on). Then every card the presenter opens — hover, click, hint or a see-also swap — opens on the projector window and on every phone, and leaves them when it closes or the deck moves on. What travels through the room is a mention (slide, term id, which occurrence), not words: every screen runs the same build and resolves them itself, so a late joiner gets the open card too. A viewer's own cards stay private. On the audience page the mirror is too small to carry a card, so the definition appears as a readout under the slide (`<TermReadout />`, with the mirror inside `data-term-cards="off"`); full-screen it is a card again. Print and the previews (overview, next slide) render terms as plain text.
97
+
98
+ ## Sketches
99
+
100
+ `<Sketch>` puts an Excalidraw drawing on a slide — the hand-drawn look, in the deck's colours. Three ways in:
101
+
102
+ ```tsx
103
+ // src/sketches.ts — drawn in code with the DSL (plain data; Excalidraw loads only when a sketch renders)
104
+ import { sketch } from "@kadal/deck/sketch";
105
+ const s = sketch({ width: 1280, height: 600 });
106
+ s.region("edge", { x: 440, y: 20, w: 420, h: 560, title: "Cloudflare Workers", tone: "accent" });
107
+ s.node("w", { x: 470, y: 70, w: 360, h: 96, label: "Next.js 16\nSSR + OG image", tone: "accent", region: "edge" });
108
+ s.node("d1", { x: 470, y: 472, w: 160, h: 88, shape: "cylinder", label: "D1", tone: "pink", region: ["edge", "report"] });
109
+ s.arrow("w", "d1", { label: "only what is on the map", fromShift: -0.5 });
110
+ export const architecture = s.build();
111
+ ```
112
+
113
+ ```mdx
114
+ <Sketch scene={architecture} steps={["edge", { focus: "report", caption: "…" }, null]} height="660px" />
115
+ <Sketch src="diagrams/flow.excalidraw" /> {/* saved on excalidraw.com, in public/; frames and groups are regions */}
116
+ <Sketch>{`flowchart LR; a --> b`}</Sketch> {/* Mermaid, converted (flowchart, sequence, class, state, ER) */}
117
+ ```
118
+
119
+ Nodes: `box`, `pill`, `ellipse`, `diamond`, `cylinder`, `note`, `text`; tones as for bling plus `ink` and `muted`; `fill: hachure`, `dashed`, `font: hand | normal | code`. Arrows pick the facing sides (`from`/`to`/`fromShift`/`via` to steer), take a `label`, and belong to the regions of both ends. `steps` makes a tour: the whole drawing first, then one click per entry lights that region and dims the rest, with a caption; the notes' `[click]` markers line up. The corner button (and its F-hint) opens the real Excalidraw canvas in view mode to pan and zoom. Colours are rewritten to theme tokens on export, so a sketch follows the theme menu and the dark scheme; fonts are self-hosted by the Vite plugin under `<base>excalidraw/fonts/`.
120
+
121
+ Embedded pages (`layout: iframe*`, `<Iframe>`) load when their slide shows, unload when it is left, and sit behind a "click to interact" shield so keys keep driving the deck until you mean it; `Done` hands the keyboard back. The slide a press lands on next has its pages loading already, hidden, so arriving there looks instant. `<Iframe scale={0.5}>` renders a page at twice the pane and shrinks it, for a desktop layout in a small card. `posterUrl:` (or `<Iframe posterUrl>`) photographs a different address for the poster — the same page in a state its URL does not carry — and `posterWait:` gives it longer than the default five seconds to settle. Where a page cannot be live — print, the overview, the presenter's next-slide box, the scroll view — and while it loads, a **poster** stands in: `poster: shots/map.jpg` on the slide (or `<Iframe poster>`), else the screenshot `kadal-deck export` captures for that URL at the pane's size into `public/posters/` (`--posters` captures only, `--refresh-posters` recaptures), else a card with the address. A slide whose content overflows is scaled to fit (`autoFit`, on by default).
122
+
123
+ ## Views
124
+
125
+ - `/<slug>/?print-pdf` — every slide as a page; on screen the pages are zoomed to the window, the browser's print dialog gets them at full size.
126
+ - `/<slug>/` — the deck. The stage is authored at 1920 × 1080 and scaled uniformly to every window: edge to edge on a 16:9 screen, centred between bars on any other shape — a phone, a tall window, a projector at 4:3 — never reflowed. `?view=scroll` reads it as a page, `?print-pdf` lays it out for print.
127
+ - `/<slug>/presenter` — current + next, notes with click markers, timer with pacing, tools (draw, laser, spotlight, blackout, share notes, share terms, confetti, camera, record, mirror, link), the room panel (QR, presence, pace, questions, polls; collapses to a rail with badges for hands, open questions and open polls, and the current-slide card grows). Passcode-gated on a host. Drawing: each pen is a colour family and a stroke drifts through it as it travels; strokes are scribbles that linger five seconds and unwind along their own path on every screen — the pin keeps them.
128
+ - `/<slug>/join` — the audience page: the deck at the presenter's slide in a stage-exact mirror (same scale, auto-fit and backgrounds as the projector; embedded pages load live, staggered so a room of phones does not hit a site at once), tap or ⤢ to fill the screen sideways with the reactions floating, plus reactions, ask + upvote, polls, quiz with nickname, pace, raise a hand, bookmarks, the notes when shared.
129
+ **Following.** A viewer who opens a live deck starts in step with the presenter and is shown nothing about it — the deck simply moves. The moment they move themselves, by any means at all (a key, an arrow, a swipe, the overview, search, the progress bar, a link that changes the hash, a second tab of their own browser), they stop following and nothing moves them again: the presenter can step through the whole deck and the viewer stays exactly where they stopped reading. A pill then offers **Follow the presenter**, which jumps them to where the presenter is and puts them back in step.
130
+
131
+ Where a window starts, in order of how much each signal knows: a `?at` link — one the deck itself minted ABOUT a slide (a share link, the phone's "Open full") — lands on that slide, free, and the marker is taken out of the address bar once it has been read; failing that, what this viewer last chose for this deck, for twelve hours, which is what carries a bookmark taken mid-talk, a tab closed at the interval or a sleeping phone back in step; failing that, a plain link follows and a bare hash is a slide someone typed. The hash alone cannot decide it, because the deck writes the hash as the viewer navigates: a bookmark's address is indistinguishable from a link someone chose to send, which is why the deliberate ones say so.
132
+
133
+ The phone page roams the same way — a chevron on each edge of the slide, or a swipe across it, and the same button underneath. What still reaches a viewer who is reading on their own: the presenter's drawings (kept per slide, so they appear on the slide they were drawn on), confetti, a shared term card (it names a slide, so it opens only on that one), blackout. What does not: the laser and the spotlight, which point at a place on the presenter's slide and mean nothing on any other.
134
+
135
+ The one rule underneath: a move with no `sync.source` is a person, here, and ends the follow — see `withTransition` in `react/Deck.tsx`. Everything arriving from the room, from another window, or from the deck's own auto-slide carries a source and leaves the follow alone.
136
+
137
+ - `/<slug>/?projector` — a window that exists to be LOOKED at: a second screen, or the one window to share on a call. It renders the deck exactly as `/<slug>/` does — backgrounds, controls, progress, slide number, the presenter's laser and pen, reactions — and gives up only input: no key, swipe, wheel or click reaches navigation, and the follow can never be broken, because a stray press on a shared window in the middle of a call is not a thing to be recoverable from. It shows no cursor. The presenter view's **Share window** button opens and closes one with a plain `window.open`, so it works in any browser; a desktop shell that wants to give it a frame of its own only has to recognise the URL.
138
+ - `/<slug>/notes` — every note on one page. `/<slug>/stats` — views, dwell per slide, sessions, questions, reactions, exports, share links.
139
+
140
+ ## Export
141
+
142
+ ```sh
143
+ npx kadal-deck export # PDF into export/ (builds + previews the deck itself)
144
+ npx kadal-deck export --png --pptx # also PNGs and a PPTX (one image per slide, notes attached)
145
+ npx kadal-deck export --posters # only capture posters for the embedded pages
146
+ ```
147
+
148
+ Needs `playwright` (and `pptxgenjs` for PPTX) installed in the deck: they are optional peers, so
149
+ a deck that never exports never downloads a browser.
150
+
151
+ ## Publish
152
+
153
+ `npx kadal-deck publish` puts the deck on https://deck.kadal.cc with a live room, analytics and
154
+ share links (free plan included). Any static host also works: `npx kadal-deck build` and upload
155
+ `dist/` — everything except the live room runs without a server.
156
+
157
+ ## Layout of this package
158
+
159
+ - `src/compiler` — splitting, frontmatter, code meta, magic-move, slots, plain text for search, the glossary pass
160
+ - `src/glossary` — the glossary model shared by compiler and browser (matcher, inline Markdown, options) and the YAML parser
161
+ - `src/vite` — the plugin: `virtual:deck` manifest, one module per slide / notes / magic block
162
+ - `src/core` — model, navigation, click registry, hash, keyboard, layout, touch (pure, tested)
163
+ - `src/react` — the runtime: Deck, Stage, SlideFrame, layouts, backgrounds, auto-animate, chrome, overview, scroll, print, lightbox, zoom
164
+ - `src/components` — everything a slide can use
165
+ - `src/sketch` — Excalidraw: the DSL, the renderer (elements → theme-aware SVG), the canvas
166
+ - `src/live` — the room protocol, client, provider, live components, analytics, Turnstile
167
+ - `src/draw` — drawing layer and strokes
168
+ - `src/presenter`, `src/audience` — the pages
169
+ - `src/backgrounds` — the background components
170
+ - `src/themes` — base.css (engine), live.css, minimal / rla / stack
171
+ - `src/export` — Playwright export to PDF, PNG, PPTX
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll };
@@ -0,0 +1,14 @@
1
+ //#region src/audience/index.d.ts
2
+ /**
3
+ * ─────────────────────────────────────────────────────────────────────────────
4
+ * THE AUDIENCE PAGE — /<slug>/join
5
+ * ─────────────────────────────────────────────────────────────────────────────
6
+ *
7
+ * What a phone shows after scanning the code: the slide the presenter is on (the real slide,
8
+ * scaled), the reactions, an ask box with upvotes, whatever poll or quiz is open, pace buttons,
9
+ * a raised hand, the presenter's notes when shared, and bookmarks kept on the phone. Nothing
10
+ * requires an account; a nickname is optional and only used for the quiz leaderboard.
11
+ * ─────────────────────────────────────────────────────────────────────────────
12
+ */
13
+ export declare function AudiencePage(): import("react").JSX.Element;
14
+ //#endregion