@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.
- package/LICENSE +661 -0
- package/LICENSING.md +40 -0
- package/README.md +170 -2
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/audience/index.d.ts +14 -0
- package/dist/audience/index.js +680 -0
- package/dist/backgrounds/index.d.ts +65 -0
- package/dist/backgrounds/index.js +604 -0
- package/dist/backgrounds/shared.d.ts +7 -0
- package/dist/backgrounds/shared.js +89 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +8 -0
- package/dist/cli/host.js +111 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.js +140 -0
- package/dist/cli/login.js +56 -0
- package/dist/cli/publish.js +159 -0
- package/dist/cli/vite.js +39 -0
- package/dist/compiler/code-meta.d.ts +33 -0
- package/dist/compiler/code-meta.js +81 -0
- package/dist/compiler/index.d.ts +8 -0
- package/dist/compiler/index.js +8 -0
- package/dist/compiler/magic-move.d.ts +26 -0
- package/dist/compiler/magic-move.js +33 -0
- package/dist/compiler/plain-text.js +7 -0
- package/dist/compiler/rehype-code.d.ts +18 -0
- package/dist/compiler/rehype-code.js +66 -0
- package/dist/compiler/rehype-glossary.d.ts +35 -0
- package/dist/compiler/rehype-glossary.js +211 -0
- package/dist/compiler/slots.d.ts +3 -0
- package/dist/compiler/slots.js +36 -0
- package/dist/compiler/split.d.ts +63 -0
- package/dist/compiler/split.js +195 -0
- package/dist/components/Background.d.ts +15 -0
- package/dist/components/Background.js +24 -0
- package/dist/components/Click.d.ts +54 -0
- package/dist/components/Click.js +129 -0
- package/dist/components/Code.d.ts +27 -0
- package/dist/components/Code.js +124 -0
- package/dist/components/Motion.d.ts +25 -0
- package/dist/components/Motion.js +45 -0
- package/dist/components/Sketch.d.ts +44 -0
- package/dist/components/Sketch.js +141 -0
- package/dist/components/Term.d.ts +63 -0
- package/dist/components/Term.js +492 -0
- package/dist/components/bling.d.ts +75 -0
- package/dist/components/bling.js +146 -0
- package/dist/components/content.d.ts +232 -0
- package/dist/components/content.js +637 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.js +88 -0
- package/dist/core/clicks.d.ts +45 -0
- package/dist/core/clicks.js +75 -0
- package/dist/core/hash.d.ts +33 -0
- package/dist/core/hash.js +49 -0
- package/dist/core/keyboard.d.ts +17 -0
- package/dist/core/keyboard.js +198 -0
- package/dist/core/layout.d.ts +16 -0
- package/dist/core/layout.js +25 -0
- package/dist/core/model.d.ts +259 -0
- package/dist/core/model.js +377 -0
- package/dist/core/navigation.d.ts +74 -0
- package/dist/core/navigation.js +244 -0
- package/dist/core/poster.js +30 -0
- package/dist/core/store.d.ts +20 -0
- package/dist/core/store.js +64 -0
- package/dist/core/touch.js +27 -0
- package/dist/draw/DrawingLayer.js +315 -0
- package/dist/draw/strokes.js +188 -0
- package/dist/export/index.d.ts +68 -0
- package/dist/export/index.js +251 -0
- package/dist/export/run.js +81 -0
- package/dist/glossary/model.d.ts +81 -0
- package/dist/glossary/model.js +156 -0
- package/dist/glossary/parse.d.ts +27 -0
- package/dist/glossary/parse.js +90 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +30 -0
- package/dist/live/LiveProvider.d.ts +20 -0
- package/dist/live/LiveProvider.js +450 -0
- package/dist/live/analytics.d.ts +15 -0
- package/dist/live/analytics.js +103 -0
- package/dist/live/client.d.ts +51 -0
- package/dist/live/client.js +193 -0
- package/dist/live/components.d.ts +64 -0
- package/dist/live/components.js +471 -0
- package/dist/live/context.d.ts +15 -0
- package/dist/live/context.js +28 -0
- package/dist/live/index.d.ts +7 -0
- package/dist/live/index.js +7 -0
- package/dist/live/protocol.d.ts +284 -0
- package/dist/live/protocol.js +42 -0
- package/dist/live/turnstile.js +49 -0
- package/dist/presenter/index.d.ts +20 -0
- package/dist/presenter/index.js +1211 -0
- package/dist/react/AutoAnimate.js +92 -0
- package/dist/react/Backgrounds.d.ts +11 -0
- package/dist/react/Backgrounds.js +154 -0
- package/dist/react/ConfettiLayer.js +85 -0
- package/dist/react/Deck.d.ts +35 -0
- package/dist/react/Deck.js +983 -0
- package/dist/react/Frame.d.ts +16 -0
- package/dist/react/Frame.js +174 -0
- package/dist/react/Hints.d.ts +3 -0
- package/dist/react/Hints.js +201 -0
- package/dist/react/Lightbox.js +54 -0
- package/dist/react/Overview.js +93 -0
- package/dist/react/PrintView.js +128 -0
- package/dist/react/ScrollView.js +128 -0
- package/dist/react/SlideFrame.d.ts +51 -0
- package/dist/react/SlideFrame.js +323 -0
- package/dist/react/Stage.js +151 -0
- package/dist/react/ThemeMenu.d.ts +5 -0
- package/dist/react/ThemeMenu.js +124 -0
- package/dist/react/Zoom.js +23 -0
- package/dist/react/chrome.js +339 -0
- package/dist/react/context.d.ts +165 -0
- package/dist/react/context.js +19 -0
- package/dist/react/hooks.d.ts +33 -0
- package/dist/react/hooks.js +70 -0
- package/dist/react/layouts.d.ts +21 -0
- package/dist/react/layouts.js +104 -0
- package/dist/react/mdx.d.ts +4 -0
- package/dist/react/mdx.js +33 -0
- package/dist/react/terms.d.ts +6 -0
- package/dist/react/terms.js +66 -0
- package/dist/renderers/Island.d.ts +57 -0
- package/dist/renderers/Island.js +223 -0
- package/dist/renderers/contract.d.ts +173 -0
- package/dist/renderers/contract.js +152 -0
- package/dist/renderers/index.d.ts +12 -0
- package/dist/renderers/index.js +16 -0
- package/dist/renderers/react.d.ts +16 -0
- package/dist/renderers/react.js +32 -0
- package/dist/renderers/web-components.d.ts +4 -0
- package/dist/renderers/web-components.js +103 -0
- package/dist/sketch/Canvas.js +49 -0
- package/dist/sketch/dsl.d.ts +131 -0
- package/dist/sketch/dsl.js +281 -0
- package/dist/sketch/index.d.ts +4 -0
- package/dist/sketch/index.js +3 -0
- package/dist/sketch/palette.d.ts +21 -0
- package/dist/sketch/palette.js +66 -0
- package/dist/sketch/render.d.ts +43 -0
- package/dist/sketch/render.js +198 -0
- package/dist/themes/base.css +1704 -0
- package/dist/themes/bling.css +592 -0
- package/dist/themes/glossary.css +317 -0
- package/dist/themes/live.css +1557 -0
- package/dist/themes/minimal.css +83 -0
- package/dist/themes/rla.css +92 -0
- package/dist/themes/sketch.css +121 -0
- package/dist/themes/stack.css +62 -0
- package/dist/vite/index.d.ts +15 -0
- package/dist/vite/index.js +320 -0
- package/package.json +159 -4
- package/tools/verify-island.mjs +150 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//#region src/react/AutoAnimate.ts
|
|
2
|
+
const CANDIDATES = "h1, h2, h3, h4, h5, h6, p, li, img, video, iframe, pre, code, blockquote, div, span, svg, table, button, figure, [data-id]";
|
|
3
|
+
function keyOf(el, counts) {
|
|
4
|
+
const explicit = el.getAttribute("data-id");
|
|
5
|
+
if (explicit) return `id:${explicit}`;
|
|
6
|
+
const base = `${el.tagName.toLowerCase()}:${el.getAttribute("src") ?? (el.textContent ?? "").trim().slice(0, 120)}`;
|
|
7
|
+
const n = counts.get(base) ?? 0;
|
|
8
|
+
counts.set(base, n + 1);
|
|
9
|
+
return `${base}#${n}`;
|
|
10
|
+
}
|
|
11
|
+
function candidates(root) {
|
|
12
|
+
return [...root.querySelectorAll(CANDIDATES)].filter((el) => {
|
|
13
|
+
if (el.hasAttribute("data-id")) return true;
|
|
14
|
+
const tag = el.tagName.toLowerCase();
|
|
15
|
+
if ([
|
|
16
|
+
"img",
|
|
17
|
+
"video",
|
|
18
|
+
"iframe",
|
|
19
|
+
"svg"
|
|
20
|
+
].includes(tag)) return true;
|
|
21
|
+
if (tag === "div" || tag === "span") return false;
|
|
22
|
+
return (el.textContent ?? "").trim().length > 0;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function snapshot(root) {
|
|
26
|
+
const out = /* @__PURE__ */ new Map();
|
|
27
|
+
const counts = /* @__PURE__ */ new Map();
|
|
28
|
+
for (const el of candidates(root)) {
|
|
29
|
+
const cs = getComputedStyle(el);
|
|
30
|
+
const key = keyOf(el, counts);
|
|
31
|
+
out.set(key, {
|
|
32
|
+
key,
|
|
33
|
+
rect: el.getBoundingClientRect(),
|
|
34
|
+
opacity: cs.opacity,
|
|
35
|
+
color: cs.color,
|
|
36
|
+
background: cs.backgroundColor,
|
|
37
|
+
fontSize: cs.fontSize,
|
|
38
|
+
borderRadius: cs.borderRadius
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
function animateFrom(prev, root, opts) {
|
|
44
|
+
const animations = [];
|
|
45
|
+
const counts = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const el of candidates(root)) {
|
|
47
|
+
const key = keyOf(el, counts);
|
|
48
|
+
const from = prev.get(key);
|
|
49
|
+
const html = el;
|
|
50
|
+
const delay = Number(html.dataset.autoAnimateDelay ?? 0) * 1e3;
|
|
51
|
+
const duration = Number(html.dataset.autoAnimateDuration ?? opts.duration) * 1e3;
|
|
52
|
+
const easing = html.dataset.autoAnimateEasing ?? opts.easing;
|
|
53
|
+
if (!from) {
|
|
54
|
+
if (opts.unmatched) animations.push(el.animate([{ opacity: 0 }, { opacity: 1 }], {
|
|
55
|
+
duration,
|
|
56
|
+
delay,
|
|
57
|
+
easing,
|
|
58
|
+
fill: "backwards"
|
|
59
|
+
}));
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const to = el.getBoundingClientRect();
|
|
63
|
+
const cs = getComputedStyle(el);
|
|
64
|
+
const fromFrame = {
|
|
65
|
+
transform: `translate(${(from.rect.left - to.left) / opts.scale}px, ${(from.rect.top - to.top) / opts.scale}px) scale(${to.width ? from.rect.width / to.width : 1}, ${to.height ? from.rect.height / to.height : 1})`,
|
|
66
|
+
transformOrigin: "top left",
|
|
67
|
+
opacity: from.opacity,
|
|
68
|
+
color: from.color,
|
|
69
|
+
backgroundColor: from.background,
|
|
70
|
+
fontSize: from.fontSize,
|
|
71
|
+
borderRadius: from.borderRadius
|
|
72
|
+
};
|
|
73
|
+
const toFrame = {
|
|
74
|
+
transform: "none",
|
|
75
|
+
transformOrigin: "top left",
|
|
76
|
+
opacity: cs.opacity,
|
|
77
|
+
color: cs.color,
|
|
78
|
+
backgroundColor: cs.backgroundColor,
|
|
79
|
+
fontSize: cs.fontSize,
|
|
80
|
+
borderRadius: cs.borderRadius
|
|
81
|
+
};
|
|
82
|
+
animations.push(el.animate([fromFrame, toFrame], {
|
|
83
|
+
duration,
|
|
84
|
+
delay,
|
|
85
|
+
easing,
|
|
86
|
+
fill: "backwards"
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
return animations;
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { animateFrom, snapshot };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SlideBackground } from "../core/model.js";
|
|
2
|
+
import { ComponentType } from "react";
|
|
3
|
+
//#region src/react/Backgrounds.d.ts
|
|
4
|
+
export declare function registerBackground(name: string, component: ComponentType<Record<string, unknown>>): void;
|
|
5
|
+
export declare function backgroundComponent(name: string): ComponentType<Record<string, unknown>> | null;
|
|
6
|
+
export declare function BackgroundLayer({ bg, active, className }: {
|
|
7
|
+
bg: SlideBackground | null;
|
|
8
|
+
active: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
//#endregion
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { useDeck } from "./context.js";
|
|
2
|
+
import { useLayout, useNav } from "./hooks.js";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/react/Backgrounds.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Backgrounds sit behind the stage and cover exactly its box — a colour, a gradient, an image, a
|
|
8
|
+
* looping video, an iframe, or a registered component (the `backgrounds` module ships aurora,
|
|
9
|
+
* gradient mesh, waves and the rest). On a 16:9 window that is the whole viewport; on any other
|
|
10
|
+
* shape the bars around the stage stay bars, so a slide looks the same on every screen. The
|
|
11
|
+
* current and the leaving background are both rendered during a change so they can cross-fade.
|
|
12
|
+
*/
|
|
13
|
+
const registry = /* @__PURE__ */ new Map();
|
|
14
|
+
function registerBackground(name, component) {
|
|
15
|
+
registry.set(name, component);
|
|
16
|
+
}
|
|
17
|
+
function backgroundComponent(name) {
|
|
18
|
+
return registry.get(name) ?? null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One colour as a soft wash: strongest at the top-left, fading diagonally to a pale tint, with
|
|
22
|
+
* a little extra light in that corner — the same palette, never flat. `color-mix` with
|
|
23
|
+
* transparent scales the colour's own alpha, so any CSS colour works, rgba included.
|
|
24
|
+
*/
|
|
25
|
+
function tintWash(color) {
|
|
26
|
+
const at = (pct) => `color-mix(in oklab, ${color} ${pct}%, transparent)`;
|
|
27
|
+
return [`radial-gradient(110% 85% at 10% 0%, ${at(80)} 0%, ${at(0)} 62%)`, `linear-gradient(158deg, ${at(100)} 0%, ${at(64)} 48%, ${at(36)} 100%)`].join(", ");
|
|
28
|
+
}
|
|
29
|
+
function BackgroundLayer({ bg, active, className }) {
|
|
30
|
+
if (!bg) return /* @__PURE__ */ jsx("div", {
|
|
31
|
+
className: ["deck-background", className].filter(Boolean).join(" "),
|
|
32
|
+
"data-active": active || void 0
|
|
33
|
+
});
|
|
34
|
+
const style = {};
|
|
35
|
+
if (bg.color) style.backgroundColor = bg.color;
|
|
36
|
+
if (bg.tint) style.backgroundImage = tintWash(bg.tint);
|
|
37
|
+
if (bg.gradient) style.backgroundImage = bg.gradient;
|
|
38
|
+
if (bg.image) {
|
|
39
|
+
style.backgroundImage = `url("${bg.image}")`;
|
|
40
|
+
style.backgroundSize = bg.size;
|
|
41
|
+
style.backgroundPosition = bg.position;
|
|
42
|
+
style.backgroundRepeat = bg.repeat;
|
|
43
|
+
}
|
|
44
|
+
const Comp = bg.component ? registry.get(bg.component) : null;
|
|
45
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
46
|
+
className: ["deck-background", className].filter(Boolean).join(" "),
|
|
47
|
+
"data-active": active || void 0,
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ jsx("div", {
|
|
50
|
+
className: "deck-background-fill",
|
|
51
|
+
style: {
|
|
52
|
+
...style,
|
|
53
|
+
opacity: bg.opacity
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
bg.video ? /* @__PURE__ */ jsx("video", {
|
|
57
|
+
className: "deck-background-video",
|
|
58
|
+
src: bg.video,
|
|
59
|
+
autoPlay: active,
|
|
60
|
+
loop: bg.videoLoop,
|
|
61
|
+
muted: bg.videoMuted,
|
|
62
|
+
playsInline: true,
|
|
63
|
+
style: {
|
|
64
|
+
objectFit: bg.size === "contain" ? "contain" : "cover",
|
|
65
|
+
opacity: bg.opacity
|
|
66
|
+
}
|
|
67
|
+
}) : null,
|
|
68
|
+
bg.iframe ? /* @__PURE__ */ jsx("iframe", {
|
|
69
|
+
className: "deck-background-iframe",
|
|
70
|
+
src: active ? bg.iframe : void 0,
|
|
71
|
+
title: "background",
|
|
72
|
+
style: {
|
|
73
|
+
pointerEvents: bg.iframeInteractive ? "auto" : "none",
|
|
74
|
+
opacity: bg.opacity
|
|
75
|
+
}
|
|
76
|
+
}) : null,
|
|
77
|
+
Comp ? /* @__PURE__ */ jsx("div", {
|
|
78
|
+
className: "deck-background-component",
|
|
79
|
+
style: { opacity: bg.opacity },
|
|
80
|
+
children: /* @__PURE__ */ jsx(Comp, {
|
|
81
|
+
...bg.props,
|
|
82
|
+
active
|
|
83
|
+
})
|
|
84
|
+
}) : bg.component ? /* @__PURE__ */ jsxs("div", {
|
|
85
|
+
className: "deck-background-missing",
|
|
86
|
+
children: [
|
|
87
|
+
"Unknown background “",
|
|
88
|
+
bg.component,
|
|
89
|
+
"”"
|
|
90
|
+
]
|
|
91
|
+
}) : null
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/** The two-layer background stack for the play view, clipped to the stage's box. */
|
|
96
|
+
function Backgrounds() {
|
|
97
|
+
const deck = useDeck();
|
|
98
|
+
const layout = useLayout();
|
|
99
|
+
const { h, v, direction } = useNav((s) => ({
|
|
100
|
+
h: s.h,
|
|
101
|
+
v: s.v,
|
|
102
|
+
direction: s.direction
|
|
103
|
+
}));
|
|
104
|
+
const current = deck.options.get(`${h},${v}`)?.background ?? null;
|
|
105
|
+
const [layers, setLayers] = useState([{
|
|
106
|
+
key: `${h},${v}`,
|
|
107
|
+
bg: current,
|
|
108
|
+
leaving: false
|
|
109
|
+
}]);
|
|
110
|
+
const transition = deck.options.get(`${h},${v}`)?.backgroundTransition ?? deck.config.backgroundTransition;
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
const key = `${h},${v}`;
|
|
113
|
+
setLayers((prev) => {
|
|
114
|
+
const top = prev.find((l) => !l.leaving);
|
|
115
|
+
if (top?.key === key) return prev;
|
|
116
|
+
return [...top ? [{
|
|
117
|
+
...top,
|
|
118
|
+
leaving: true
|
|
119
|
+
}] : [], {
|
|
120
|
+
key,
|
|
121
|
+
bg: current,
|
|
122
|
+
leaving: false
|
|
123
|
+
}];
|
|
124
|
+
});
|
|
125
|
+
const t = setTimeout(() => setLayers((prev) => prev.filter((l) => !l.leaving)), transitionMs(deck.config.transitionSpeed));
|
|
126
|
+
return () => clearTimeout(t);
|
|
127
|
+
}, [
|
|
128
|
+
h,
|
|
129
|
+
v,
|
|
130
|
+
current,
|
|
131
|
+
deck.config.transitionSpeed
|
|
132
|
+
]);
|
|
133
|
+
return /* @__PURE__ */ jsx("div", {
|
|
134
|
+
className: "deck-backgrounds",
|
|
135
|
+
style: {
|
|
136
|
+
left: layout.left,
|
|
137
|
+
top: layout.top,
|
|
138
|
+
width: layout.width,
|
|
139
|
+
height: layout.height
|
|
140
|
+
},
|
|
141
|
+
"data-transition": transition,
|
|
142
|
+
"data-direction": direction,
|
|
143
|
+
children: layers.map((l) => /* @__PURE__ */ jsx(BackgroundLayer, {
|
|
144
|
+
bg: l.bg,
|
|
145
|
+
active: !l.leaving,
|
|
146
|
+
className: l.leaving ? "is-leaving" : "is-entering"
|
|
147
|
+
}, l.key))
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function transitionMs(speed) {
|
|
151
|
+
return speed === "fast" ? 300 : speed === "slow" ? 1e3 : 600;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
export { BackgroundLayer, Backgrounds, backgroundComponent, registerBackground, tintWash, transitionMs };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/react/ConfettiLayer.tsx
|
|
4
|
+
/**
|
|
5
|
+
* A burst of confetti on a canvas, shared by the `<Confetti>` slide component and the layers
|
|
6
|
+
* that answer the presenter's Confetti button (the play stage, the presenter's current-slide
|
|
7
|
+
* card, the audience mirror). Returns a stop function.
|
|
8
|
+
*/
|
|
9
|
+
function burstConfetti(c, { count = 140, duration = 2200 } = {}) {
|
|
10
|
+
const ctx = c.getContext("2d");
|
|
11
|
+
if (!ctx) return () => {};
|
|
12
|
+
const W = c.width = c.offsetWidth || c.clientWidth || 1;
|
|
13
|
+
const H = c.height = c.offsetHeight || c.clientHeight || 1;
|
|
14
|
+
const colors = [
|
|
15
|
+
"#f43f5e",
|
|
16
|
+
"#f59e0b",
|
|
17
|
+
"#10b981",
|
|
18
|
+
"#3b82f6",
|
|
19
|
+
"#8b5cf6",
|
|
20
|
+
"#ec4899"
|
|
21
|
+
];
|
|
22
|
+
const parts = Array.from({ length: count }, () => ({
|
|
23
|
+
x: W / 2 + (Math.random() - .5) * W * .3,
|
|
24
|
+
y: H * .4,
|
|
25
|
+
vx: (Math.random() - .5) * 18,
|
|
26
|
+
vy: -Math.random() * 18 - 6,
|
|
27
|
+
r: Math.random() * 6 + 3,
|
|
28
|
+
c: colors[Math.floor(Math.random() * colors.length)],
|
|
29
|
+
a: Math.random() * Math.PI,
|
|
30
|
+
s: (Math.random() - .5) * .3
|
|
31
|
+
}));
|
|
32
|
+
const start = performance.now();
|
|
33
|
+
let raf = 0;
|
|
34
|
+
const tick = (t) => {
|
|
35
|
+
const p = (t - start) / duration;
|
|
36
|
+
ctx.clearRect(0, 0, W, H);
|
|
37
|
+
for (const q of parts) {
|
|
38
|
+
q.x += q.vx;
|
|
39
|
+
q.y += q.vy;
|
|
40
|
+
q.vy += .5;
|
|
41
|
+
q.vx *= .99;
|
|
42
|
+
q.a += q.s;
|
|
43
|
+
ctx.save();
|
|
44
|
+
ctx.globalAlpha = Math.max(0, 1 - p);
|
|
45
|
+
ctx.translate(q.x, q.y);
|
|
46
|
+
ctx.rotate(q.a);
|
|
47
|
+
ctx.fillStyle = q.c;
|
|
48
|
+
ctx.fillRect(-q.r / 2, -q.r, q.r, q.r * 2);
|
|
49
|
+
ctx.restore();
|
|
50
|
+
}
|
|
51
|
+
if (p < 1) raf = requestAnimationFrame(tick);
|
|
52
|
+
else ctx.clearRect(0, 0, W, H);
|
|
53
|
+
};
|
|
54
|
+
raf = requestAnimationFrame(tick);
|
|
55
|
+
return () => {
|
|
56
|
+
cancelAnimationFrame(raf);
|
|
57
|
+
ctx.clearRect(0, 0, W, H);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Listens for `deck:confetti` (the presenter's button, relayed by the room) and bursts over
|
|
62
|
+
* whatever it is mounted in: the viewport (play view) or a scaled stage canvas (`inCanvas`).
|
|
63
|
+
*/
|
|
64
|
+
function ConfettiLayer({ inCanvas = false } = {}) {
|
|
65
|
+
const canvas = useRef(null);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
let stop = null;
|
|
68
|
+
const fire = () => {
|
|
69
|
+
stop?.();
|
|
70
|
+
if (canvas.current) stop = burstConfetti(canvas.current, { count: 180 });
|
|
71
|
+
};
|
|
72
|
+
window.addEventListener("deck:confetti", fire);
|
|
73
|
+
return () => {
|
|
74
|
+
window.removeEventListener("deck:confetti", fire);
|
|
75
|
+
stop?.();
|
|
76
|
+
};
|
|
77
|
+
}, []);
|
|
78
|
+
return /* @__PURE__ */ jsx("canvas", {
|
|
79
|
+
ref: canvas,
|
|
80
|
+
className: ["deck-confetti-layer", inCanvas ? "in-canvas" : ""].filter(Boolean).join(" "),
|
|
81
|
+
"aria-hidden": true
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { ConfettiLayer, burstConfetti };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Frontmatter } from "../compiler/split.js";
|
|
2
|
+
import { DeckConfigInput, DeckManifest } from "../core/model.js";
|
|
3
|
+
import { ViewMode } from "./context.js";
|
|
4
|
+
import { ComponentType, ReactNode } from "react";
|
|
5
|
+
//#region src/react/Deck.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
8
|
+
* <Deck>
|
|
9
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
*
|
|
11
|
+
* The whole runtime, from the manifest the Vite plugin emits. It owns the navigation store and
|
|
12
|
+
* every side effect the browser needs — the hash, the keyboard, touch, resizing, the wake lock,
|
|
13
|
+
* the idle cursor, auto-slide, fullscreen — and decides which page to show from the URL:
|
|
14
|
+
*
|
|
15
|
+
* /<slug>/ the deck (?view=scroll, ?print-pdf, #/h/v/click)
|
|
16
|
+
* /<slug>/presenter the presenter view (notes, timer, next slide, drawing, the room)
|
|
17
|
+
* /<slug>/join the audience page (follow along, react, ask, vote)
|
|
18
|
+
* /<slug>/notes every note on one page
|
|
19
|
+
* /<slug>/stats the deck's numbers
|
|
20
|
+
*
|
|
21
|
+
* Extra pages plug in through `pages` so the presenter, audience and stats modules stay optional
|
|
22
|
+
* imports; a deck that never goes live ships without them.
|
|
23
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
*/
|
|
25
|
+
export interface DeckProps {
|
|
26
|
+
manifest: DeckManifest;
|
|
27
|
+
config?: DeckConfigInput | Frontmatter;
|
|
28
|
+
components?: Record<string, ComponentType<Record<string, unknown>>>;
|
|
29
|
+
/** Route → page component, for the presenter/audience/notes/stats pages. */
|
|
30
|
+
pages?: Partial<Record<Exclude<ViewMode, "play" | "print">, ComponentType>>;
|
|
31
|
+
/** Wraps the runtime (the live provider, a theme provider). */
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
}
|
|
34
|
+
export declare function Deck({ manifest, config: configIn, components, pages, children }: DeckProps): import("react").JSX.Element;
|
|
35
|
+
//#endregion
|