@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,195 @@
|
|
|
1
|
+
import YAML from "yaml";
|
|
2
|
+
//#region src/compiler/split.ts
|
|
3
|
+
const HORIZONTAL = /^---\s*$/;
|
|
4
|
+
const VERTICAL = /^----+\s*$/;
|
|
5
|
+
const FRONTMATTER_LINE = /^\s*[A-Za-z_][\w.-]*\s*:(\s|$)/;
|
|
6
|
+
const NOTES = /<Notes(?:\s[^>]*)?>([\s\S]*?)<\/Notes>/g;
|
|
7
|
+
/** True when every non-empty line of `lines` could be a YAML mapping line or a continuation. */
|
|
8
|
+
function looksLikeYaml(lines) {
|
|
9
|
+
let any = false;
|
|
10
|
+
for (const line of lines) {
|
|
11
|
+
if (!line.trim()) continue;
|
|
12
|
+
if (/^\s+/.test(line) || /^\s*-\s/.test(line) || /^\s*#/.test(line)) continue;
|
|
13
|
+
if (!FRONTMATTER_LINE.test(line)) return false;
|
|
14
|
+
any = true;
|
|
15
|
+
}
|
|
16
|
+
return any;
|
|
17
|
+
}
|
|
18
|
+
function parseYaml(text, where) {
|
|
19
|
+
const value = YAML.parse(text);
|
|
20
|
+
if (value === null || value === void 0) return {};
|
|
21
|
+
if (typeof value !== "object" || Array.isArray(value)) throw new Error(`${where}: frontmatter must be a YAML mapping`);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Cut the text at separators, taking fenced frontmatter as it goes. After a separator at line i,
|
|
26
|
+
* if the lines up to the next `---` read as YAML, they are that piece's frontmatter and the closing
|
|
27
|
+
* fence is consumed rather than treated as another separator. The head piece works the same way
|
|
28
|
+
* when the file opens with a fence. Fenced code is never cut.
|
|
29
|
+
*/
|
|
30
|
+
function cut(text) {
|
|
31
|
+
const lines = text.split(/\r?\n/);
|
|
32
|
+
const pieces = [];
|
|
33
|
+
let inFence = false;
|
|
34
|
+
let fence = "";
|
|
35
|
+
/** Try to read a fenced YAML block starting at `from` (the line after an opening fence). */
|
|
36
|
+
const frontmatterAt = (from) => {
|
|
37
|
+
let close = -1;
|
|
38
|
+
for (let k = from; k < lines.length; k++) {
|
|
39
|
+
const l = lines[k];
|
|
40
|
+
if (HORIZONTAL.test(l)) {
|
|
41
|
+
close = k;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
if (VERTICAL.test(l)) return null;
|
|
45
|
+
}
|
|
46
|
+
if (close === -1) return null;
|
|
47
|
+
const block = lines.slice(from, close);
|
|
48
|
+
if (!looksLikeYaml(block)) return null;
|
|
49
|
+
return {
|
|
50
|
+
yaml: block.join("\n"),
|
|
51
|
+
next: close + 1
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
let i = 0;
|
|
55
|
+
let firstContent = 0;
|
|
56
|
+
while (firstContent < lines.length && !lines[firstContent].trim()) firstContent++;
|
|
57
|
+
let head = {
|
|
58
|
+
vertical: false,
|
|
59
|
+
frontmatter: null,
|
|
60
|
+
lines: [],
|
|
61
|
+
line: 1
|
|
62
|
+
};
|
|
63
|
+
if (firstContent < lines.length && HORIZONTAL.test(lines[firstContent])) {
|
|
64
|
+
const fm = frontmatterAt(firstContent + 1);
|
|
65
|
+
if (fm) {
|
|
66
|
+
head = {
|
|
67
|
+
vertical: false,
|
|
68
|
+
frontmatter: fm.yaml,
|
|
69
|
+
lines: [],
|
|
70
|
+
line: fm.next + 1
|
|
71
|
+
};
|
|
72
|
+
i = fm.next;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
pieces.push(head);
|
|
76
|
+
for (; i < lines.length; i++) {
|
|
77
|
+
const line = lines[i];
|
|
78
|
+
const fenceMatch = /^\s*(`{3,}|~{3,})/.exec(line);
|
|
79
|
+
if (fenceMatch) {
|
|
80
|
+
const mark = fenceMatch[1];
|
|
81
|
+
if (!inFence) {
|
|
82
|
+
inFence = true;
|
|
83
|
+
fence = mark;
|
|
84
|
+
} else if (mark[0] === fence[0] && mark.length >= fence.length) inFence = false;
|
|
85
|
+
}
|
|
86
|
+
if (!inFence && (HORIZONTAL.test(line) || VERTICAL.test(line))) {
|
|
87
|
+
const vertical = VERTICAL.test(line);
|
|
88
|
+
const fm = frontmatterAt(i + 1);
|
|
89
|
+
if (fm) {
|
|
90
|
+
pieces.push({
|
|
91
|
+
vertical,
|
|
92
|
+
frontmatter: fm.yaml,
|
|
93
|
+
lines: [],
|
|
94
|
+
line: fm.next + 1
|
|
95
|
+
});
|
|
96
|
+
i = fm.next - 1;
|
|
97
|
+
} else pieces.push({
|
|
98
|
+
vertical,
|
|
99
|
+
frontmatter: null,
|
|
100
|
+
lines: [],
|
|
101
|
+
line: i + 2
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
pieces[pieces.length - 1].lines.push(line);
|
|
106
|
+
}
|
|
107
|
+
return pieces;
|
|
108
|
+
}
|
|
109
|
+
function extractNotes(content) {
|
|
110
|
+
const notes = [];
|
|
111
|
+
return {
|
|
112
|
+
content: content.replace(NOTES, (_m, inner) => {
|
|
113
|
+
notes.push(inner.trim());
|
|
114
|
+
return "";
|
|
115
|
+
}),
|
|
116
|
+
notes: notes.length ? notes.join("\n\n") : null
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Split a deck file into slides. Includes (`src:`) are expanded in place; a piece that is only
|
|
121
|
+
* frontmatter with a `src` contributes the included file's slides, with the included file's own
|
|
122
|
+
* headmatter and the piece's remaining frontmatter merged into the first of them.
|
|
123
|
+
*/
|
|
124
|
+
function splitDeck(text, options) {
|
|
125
|
+
const pieces = cut(text);
|
|
126
|
+
const head = pieces[0];
|
|
127
|
+
const headmatter = head.frontmatter ? parseYaml(head.frontmatter, `${options.file}:1`) : {};
|
|
128
|
+
const slides = [];
|
|
129
|
+
let h = -1;
|
|
130
|
+
let v = 0;
|
|
131
|
+
const push = (content, frontmatter, vertical, source) => {
|
|
132
|
+
if (vertical && h >= 0) v++;
|
|
133
|
+
else {
|
|
134
|
+
h++;
|
|
135
|
+
v = 0;
|
|
136
|
+
}
|
|
137
|
+
const { content: body, notes } = extractNotes(content);
|
|
138
|
+
slides.push({
|
|
139
|
+
index: slides.length,
|
|
140
|
+
h,
|
|
141
|
+
v,
|
|
142
|
+
frontmatter,
|
|
143
|
+
content: body.trim(),
|
|
144
|
+
notes,
|
|
145
|
+
source
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
pieces.forEach((piece, i) => {
|
|
149
|
+
const where = `${options.file}:${piece.line}`;
|
|
150
|
+
const frontmatter = i === 0 ? { ...headmatter } : !piece.frontmatter ? {} : parseYaml(piece.frontmatter, where);
|
|
151
|
+
const content = piece.lines.join("\n");
|
|
152
|
+
const blank = !content.trim();
|
|
153
|
+
if (typeof frontmatter.src === "string") {
|
|
154
|
+
const { src, ...merge } = frontmatter;
|
|
155
|
+
const included = options.resolveInclude?.(src, options.file);
|
|
156
|
+
if (!included) throw new Error(`${where}: cannot include "${src}"`);
|
|
157
|
+
const inner = splitDeck(included.text, {
|
|
158
|
+
...options,
|
|
159
|
+
file: included.file
|
|
160
|
+
});
|
|
161
|
+
inner.slides.forEach((s, j) => {
|
|
162
|
+
push(s.content + (s.notes ? `\n\n<Notes>${s.notes}</Notes>` : ""), j === 0 ? {
|
|
163
|
+
...inner.headmatter,
|
|
164
|
+
...s.frontmatter,
|
|
165
|
+
...merge
|
|
166
|
+
} : s.frontmatter, j === 0 ? piece.vertical : s.v > 0, s.source);
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (blank && (i === 0 || !Object.keys(frontmatter).length)) return;
|
|
171
|
+
push(content, frontmatter, piece.vertical, {
|
|
172
|
+
file: options.file,
|
|
173
|
+
line: piece.line
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
headmatter,
|
|
178
|
+
slides
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** The title a slide reports to the table of contents: frontmatter `title`, else its first heading. */
|
|
182
|
+
function titleOf(slide) {
|
|
183
|
+
if (typeof slide.frontmatter.title === "string") return slide.frontmatter.title;
|
|
184
|
+
const m = /^\s*#{1,6}\s+(.+?)\s*#*\s*$/m.exec(slide.content);
|
|
185
|
+
if (!m) return null;
|
|
186
|
+
return m[1].replace(/[*_`~]/g, "").replace(/<[^>]+>/g, "").trim() || null;
|
|
187
|
+
}
|
|
188
|
+
/** The level of the title, for the table of contents: frontmatter `level`, else the heading depth. */
|
|
189
|
+
function levelOf(slide) {
|
|
190
|
+
if (typeof slide.frontmatter.level === "number") return slide.frontmatter.level;
|
|
191
|
+
const m = /^\s*(#{1,6})\s+/m.exec(slide.content);
|
|
192
|
+
return m ? m[1].length : 1;
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
export { levelOf, splitDeck, titleOf };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/components/Background.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* A component background inside a slide — the RLA deck's pattern of an absolutely positioned
|
|
5
|
+
* animated layer under the content. `<Background name="aurora" />` picks a registered one;
|
|
6
|
+
* children make a custom one.
|
|
7
|
+
*/
|
|
8
|
+
export declare function Background({ name, children, opacity, className, ...props }: {
|
|
9
|
+
name?: string;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
opacity?: number;
|
|
12
|
+
className?: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
//#endregion
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useSlide } from "../react/context.js";
|
|
2
|
+
import { backgroundComponent } from "../react/Backgrounds.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/Background.tsx
|
|
5
|
+
/**
|
|
6
|
+
* A component background inside a slide — the RLA deck's pattern of an absolutely positioned
|
|
7
|
+
* animated layer under the content. `<Background name="aurora" />` picks a registered one;
|
|
8
|
+
* children make a custom one.
|
|
9
|
+
*/
|
|
10
|
+
function Background({ name, children, opacity = 1, className, ...props }) {
|
|
11
|
+
const slide = useSlide();
|
|
12
|
+
const Comp = name ? backgroundComponent(name) : null;
|
|
13
|
+
return /* @__PURE__ */ jsx("div", {
|
|
14
|
+
className: ["deck-slide-background", className].filter(Boolean).join(" "),
|
|
15
|
+
style: { opacity },
|
|
16
|
+
"aria-hidden": true,
|
|
17
|
+
children: Comp ? /* @__PURE__ */ jsx(Comp, {
|
|
18
|
+
...props,
|
|
19
|
+
active: slide.active
|
|
20
|
+
}) : children
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { Background };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ClickAt } from "../core/clicks.js";
|
|
2
|
+
import { ElementType, ReactNode } from "react";
|
|
3
|
+
//#region src/components/Click.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Click-driven reveals. `<Click>` is Slidev's v-click and reveal.js's fragment in one: it registers
|
|
6
|
+
* a step and shows its children from that step on (or hides them, with `hide`). `effect` names the
|
|
7
|
+
* reveal.js fragment styles — fade-in (default), fade-up/down/left/right, fade-out, grow, shrink,
|
|
8
|
+
* strike, highlight-red/green/blue, highlight-current-*, semi-fade-out, current-visible,
|
|
9
|
+
* fade-in-then-out, fade-in-then-semi-out — and any custom class the theme defines.
|
|
10
|
+
*/
|
|
11
|
+
export type ClickEffect = "fade-in" | "fade-out" | "fade-up" | "fade-down" | "fade-left" | "fade-right" | "fade-in-then-out" | "fade-in-then-semi-out" | "current-visible" | "grow" | "shrink" | "strike" | "semi-fade-out" | "highlight-red" | "highlight-green" | "highlight-blue" | "highlight-current-red" | "highlight-current-green" | "highlight-current-blue" | (string & {});
|
|
12
|
+
export interface ClickProps {
|
|
13
|
+
at?: ClickAt;
|
|
14
|
+
hide?: boolean;
|
|
15
|
+
effect?: ClickEffect;
|
|
16
|
+
/** The element to render; a `span` for inline text, `div` by default. */
|
|
17
|
+
as?: ElementType;
|
|
18
|
+
className?: string;
|
|
19
|
+
style?: React.CSSProperties;
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
export declare function Click({ at, hide, effect, as, className, style, children, ...rest }: ClickProps): import("react").JSX.Element;
|
|
24
|
+
/** reveal.js spelling. */
|
|
25
|
+
export declare const Fragment: typeof Click;
|
|
26
|
+
/** Shows with the previous click rather than one after it. */
|
|
27
|
+
export declare function After(props: Omit<ClickProps, "at">): import("react").JSX.Element;
|
|
28
|
+
export interface ClicksProps {
|
|
29
|
+
/** Reveal this many items per click. */
|
|
30
|
+
every?: number;
|
|
31
|
+
/** How deep nested lists take part; 1 means only the outer list. */
|
|
32
|
+
depth?: number;
|
|
33
|
+
effect?: ClickEffect;
|
|
34
|
+
children?: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
export declare function Clicks({ every, depth, effect, children }: ClicksProps): import("react").JSX.Element;
|
|
37
|
+
/** The `li` mapping: a plain list item, or a click step when inside `<Clicks>`. */
|
|
38
|
+
export declare function Li({ children, ...rest }: {
|
|
39
|
+
children?: ReactNode;
|
|
40
|
+
[key: string]: unknown;
|
|
41
|
+
}): import("react").JSX.Element;
|
|
42
|
+
/** Lists keep their markup. `<Clicks>` recognises these two by identity, so it leaves them to
|
|
43
|
+
* step their own items; the class is for the stylesheet. */
|
|
44
|
+
export declare function Ul(props: {
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
className?: string;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}): import("react").JSX.Element;
|
|
49
|
+
export declare function Ol(props: {
|
|
50
|
+
children?: ReactNode;
|
|
51
|
+
className?: string;
|
|
52
|
+
[key: string]: unknown;
|
|
53
|
+
}): import("react").JSX.Element;
|
|
54
|
+
//#endregion
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { useClick } from "../react/hooks.js";
|
|
2
|
+
import { Children, createContext, isValidElement, useContext } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/Click.tsx
|
|
5
|
+
function Click({ at, hide, effect = "fade-in", as, className, style, children, ...rest }) {
|
|
6
|
+
const { shown, current, range, click } = useClick({
|
|
7
|
+
at,
|
|
8
|
+
hide
|
|
9
|
+
});
|
|
10
|
+
const Tag = as ?? "div";
|
|
11
|
+
const past = click > range.start;
|
|
12
|
+
return /* @__PURE__ */ jsx(Tag, {
|
|
13
|
+
...rest,
|
|
14
|
+
className: [
|
|
15
|
+
"deck-click",
|
|
16
|
+
`effect-${effect}`,
|
|
17
|
+
shown ? "is-shown" : "is-hidden",
|
|
18
|
+
current ? "is-current" : "",
|
|
19
|
+
past ? "is-past" : "",
|
|
20
|
+
className
|
|
21
|
+
].filter(Boolean).join(" "),
|
|
22
|
+
style,
|
|
23
|
+
"data-click-at": range.start,
|
|
24
|
+
"aria-hidden": shown ? void 0 : true,
|
|
25
|
+
children
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** reveal.js spelling. */
|
|
29
|
+
const Fragment$1 = Click;
|
|
30
|
+
/** Shows with the previous click rather than one after it. */
|
|
31
|
+
function After(props) {
|
|
32
|
+
return /* @__PURE__ */ jsx(Click, {
|
|
33
|
+
...props,
|
|
34
|
+
at: "+0"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const ClicksContext = createContext(null);
|
|
38
|
+
/**
|
|
39
|
+
* Reveal each child in turn. Wrap a Markdown list to step through its items (the `li` mapping
|
|
40
|
+
* registers itself when inside a `<Clicks>`), or any elements to step through them.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Does this child step its own items, so `<Clicks>` must leave it alone?
|
|
44
|
+
*
|
|
45
|
+
* A Markdown list does: the `li` mapping below turns each item into a click while it is inside a
|
|
46
|
+
* `<Clicks>`. Wrapping the list as well spends a click on the list as a whole and lands every item
|
|
47
|
+
* one step late — which puts the notes' `[click]` markers, the presenter's step count and any
|
|
48
|
+
* `<Click at={n}>` later on the slide out of step with what the audience sees.
|
|
49
|
+
*
|
|
50
|
+
* MDX maps `ul` to `Ul` (`export { Ul as ul }` in components/index.ts), so what arrives here has
|
|
51
|
+
* that component as its type and no className of its own — the class exists only once `Ul` has
|
|
52
|
+
* rendered, which is why the className test alone could never see it.
|
|
53
|
+
*/
|
|
54
|
+
function stepsItsOwnItems(child) {
|
|
55
|
+
if (!isValidElement(child)) return false;
|
|
56
|
+
const type = child.type;
|
|
57
|
+
if (type === Ul || type === Ol) return true;
|
|
58
|
+
if (type === "ul" || type === "ol") return true;
|
|
59
|
+
return !!child.props?.className?.includes?.("deck-list");
|
|
60
|
+
}
|
|
61
|
+
function Clicks({ every = 1, depth = 1, effect = "fade-in", children }) {
|
|
62
|
+
const items = Children.toArray(children);
|
|
63
|
+
const listLike = items.some(stepsItsOwnItems);
|
|
64
|
+
return /* @__PURE__ */ jsx(ClicksContext.Provider, {
|
|
65
|
+
value: {
|
|
66
|
+
every,
|
|
67
|
+
depth,
|
|
68
|
+
level: 0,
|
|
69
|
+
effect
|
|
70
|
+
},
|
|
71
|
+
children: listLike ? children : items.map((child, i) => isValidElement(child) ? /* @__PURE__ */ jsx(Click, {
|
|
72
|
+
at: i % every === 0 ? void 0 : "+0",
|
|
73
|
+
effect,
|
|
74
|
+
children: child
|
|
75
|
+
}, i) : child)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** The `li` mapping: a plain list item, or a click step when inside `<Clicks>`. */
|
|
79
|
+
function Li({ children, ...rest }) {
|
|
80
|
+
const scope = useContext(ClicksContext);
|
|
81
|
+
if (!scope || scope.level >= scope.depth) return /* @__PURE__ */ jsx("li", {
|
|
82
|
+
...rest,
|
|
83
|
+
children
|
|
84
|
+
});
|
|
85
|
+
return /* @__PURE__ */ jsx(ClicksContext.Provider, {
|
|
86
|
+
value: {
|
|
87
|
+
...scope,
|
|
88
|
+
level: scope.level + 1
|
|
89
|
+
},
|
|
90
|
+
children: /* @__PURE__ */ jsx(ClickLi, {
|
|
91
|
+
scope,
|
|
92
|
+
...rest,
|
|
93
|
+
children
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
let liCounter = 0;
|
|
98
|
+
function ClickLi({ scope, children, ...rest }) {
|
|
99
|
+
const at = liCounter++ % scope.every === 0 ? void 0 : "+0";
|
|
100
|
+
const { shown, current, range, click } = useClick({ at });
|
|
101
|
+
return /* @__PURE__ */ jsx("li", {
|
|
102
|
+
...rest,
|
|
103
|
+
className: [
|
|
104
|
+
"deck-click",
|
|
105
|
+
`effect-${scope.effect}`,
|
|
106
|
+
shown ? "is-shown" : "is-hidden",
|
|
107
|
+
current ? "is-current" : "",
|
|
108
|
+
click > range.start ? "is-past" : ""
|
|
109
|
+
].filter(Boolean).join(" "),
|
|
110
|
+
"data-click-at": range.start,
|
|
111
|
+
children
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Lists keep their markup. `<Clicks>` recognises these two by identity, so it leaves them to
|
|
115
|
+
* step their own items; the class is for the stylesheet. */
|
|
116
|
+
function Ul(props) {
|
|
117
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
118
|
+
...props,
|
|
119
|
+
className: ["deck-list", props.className].filter(Boolean).join(" ")
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function Ol(props) {
|
|
123
|
+
return /* @__PURE__ */ jsx("ol", {
|
|
124
|
+
...props,
|
|
125
|
+
className: ["deck-list", props.className].filter(Boolean).join(" ")
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
export { After, Click, Clicks, Fragment$1 as Fragment, Li, Ol, Ul, stepsItsOwnItems };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/components/Code.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The `pre` mapping: a code block highlighted at build time, with the fence meta carried in data
|
|
5
|
+
* attributes. `{2-3|5|all}` reserves one click per step after the first and lights the lines of
|
|
6
|
+
* the current step; `{lines:true}` numbers lines; `[title]` adds a title bar; `{maxHeight}` scrolls;
|
|
7
|
+
* `{hide|…}` keeps the block hidden until its first click.
|
|
8
|
+
*/
|
|
9
|
+
export interface CodeProps {
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
"data-steps"?: string;
|
|
13
|
+
"data-lines"?: string;
|
|
14
|
+
"data-start"?: string;
|
|
15
|
+
"data-title"?: string;
|
|
16
|
+
"data-max-height"?: string;
|
|
17
|
+
"data-hide-until-step"?: string;
|
|
18
|
+
"data-lang"?: string;
|
|
19
|
+
"data-total"?: string;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
export declare function Code(props: CodeProps): import("react").JSX.Element;
|
|
23
|
+
/** Shiki Magic Move: the precomputed steps from the plugin, morphing on each click. */
|
|
24
|
+
export declare function MagicMove({ block }: {
|
|
25
|
+
block: number;
|
|
26
|
+
}): import("react").JSX.Element;
|
|
27
|
+
//#endregion
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { useSlide } from "../react/context.js";
|
|
2
|
+
import { useClick, useUi } from "../react/hooks.js";
|
|
3
|
+
import { deserialiseSteps } from "../compiler/code-meta.js";
|
|
4
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { ShikiMagicMovePrecompiled } from "shiki-magic-move/react";
|
|
7
|
+
//#region src/components/Code.tsx
|
|
8
|
+
function Code(props) {
|
|
9
|
+
const { children, className, "data-steps": stepsAttr, "data-lines": linesAttr, "data-start": startAttr, "data-title": title, "data-max-height": maxHeight, "data-hide-until-step": hideUntil, "data-lang": lang, "data-total": total, ...rest } = props;
|
|
10
|
+
const steps = useMemo(() => stepsAttr ? deserialiseSteps(stepsAttr) : null, [stepsAttr]);
|
|
11
|
+
const clickCount = (steps ? steps.length - 1 : 0) + (hideUntil ? 1 : 0);
|
|
12
|
+
const { range, click } = useClick(clickCount > 0 ? { span: clickCount } : { at: 0 });
|
|
13
|
+
const pre = useRef(null);
|
|
14
|
+
const [copied, setCopied] = useState(false);
|
|
15
|
+
const rel = clickCount > 0 ? Math.min(clickCount, Math.max(0, click - range.start + 1)) : 0;
|
|
16
|
+
const hidden = !!hideUntil && rel < 1;
|
|
17
|
+
const stepIndex = steps ? Math.min(steps.length - 1, Math.max(0, hideUntil ? rel - 1 : rel)) : -1;
|
|
18
|
+
const active = steps && stepIndex >= 0 ? steps[stepIndex] : null;
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const el = pre.current;
|
|
21
|
+
if (!el) return;
|
|
22
|
+
for (const line of el.querySelectorAll(".line")) {
|
|
23
|
+
const n = Number(line.dataset.line);
|
|
24
|
+
const on = active === null ? true : active.has(n);
|
|
25
|
+
line.classList.toggle("is-active", !!steps && on && active !== null);
|
|
26
|
+
line.classList.toggle("is-dim", !!steps && active !== null && !on);
|
|
27
|
+
}
|
|
28
|
+
}, [
|
|
29
|
+
active,
|
|
30
|
+
steps,
|
|
31
|
+
children
|
|
32
|
+
]);
|
|
33
|
+
const copy = async () => {
|
|
34
|
+
try {
|
|
35
|
+
await navigator.clipboard.writeText(pre.current?.innerText ?? "");
|
|
36
|
+
setCopied(true);
|
|
37
|
+
setTimeout(() => setCopied(false), 1200);
|
|
38
|
+
} catch {}
|
|
39
|
+
};
|
|
40
|
+
const start = Number(startAttr ?? 1);
|
|
41
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
42
|
+
className: [
|
|
43
|
+
"deck-code-block",
|
|
44
|
+
hidden ? "is-hidden" : "",
|
|
45
|
+
title ? "has-title" : ""
|
|
46
|
+
].filter(Boolean).join(" "),
|
|
47
|
+
"data-lang": lang ?? "text",
|
|
48
|
+
children: [
|
|
49
|
+
title ? /* @__PURE__ */ jsxs("div", {
|
|
50
|
+
className: "deck-code-title",
|
|
51
|
+
children: [/* @__PURE__ */ jsx("span", { className: "deck-code-title-dot" }), /* @__PURE__ */ jsx("span", { children: title })]
|
|
52
|
+
}) : null,
|
|
53
|
+
/* @__PURE__ */ jsx("pre", {
|
|
54
|
+
ref: pre,
|
|
55
|
+
...rest,
|
|
56
|
+
className: [
|
|
57
|
+
"deck-code",
|
|
58
|
+
"shiki",
|
|
59
|
+
linesAttr === "true" ? "has-lines" : "",
|
|
60
|
+
className
|
|
61
|
+
].filter(Boolean).join(" "),
|
|
62
|
+
style: {
|
|
63
|
+
...maxHeight ? {
|
|
64
|
+
maxHeight,
|
|
65
|
+
overflow: "auto"
|
|
66
|
+
} : null,
|
|
67
|
+
counterReset: `line ${start - 1}`
|
|
68
|
+
},
|
|
69
|
+
"data-total": total || void 0,
|
|
70
|
+
children
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ jsx("button", {
|
|
73
|
+
type: "button",
|
|
74
|
+
className: "deck-code-copy",
|
|
75
|
+
onClick: copy,
|
|
76
|
+
"aria-label": "Copy code",
|
|
77
|
+
children: copied ? "Copied" : "Copy"
|
|
78
|
+
})
|
|
79
|
+
]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Shiki Magic Move: the precomputed steps from the plugin, morphing on each click. */
|
|
83
|
+
function MagicMove({ block }) {
|
|
84
|
+
const slide = useSlide();
|
|
85
|
+
const scheme = useUi((s) => s.colorScheme);
|
|
86
|
+
const data = slide.magic?.[block] ?? null;
|
|
87
|
+
const count = data ? Math.max(0, data.steps.length - 1) : 0;
|
|
88
|
+
const atOption = data?.options.at;
|
|
89
|
+
const { range, click } = useClick(count > 0 ? {
|
|
90
|
+
span: count,
|
|
91
|
+
at: atOption !== void 0 ? Number(atOption) : void 0
|
|
92
|
+
} : { at: 0 });
|
|
93
|
+
if (!data) return /* @__PURE__ */ jsx("pre", {
|
|
94
|
+
className: "deck-code",
|
|
95
|
+
children: "Magic move block missing"
|
|
96
|
+
});
|
|
97
|
+
const step = count > 0 ? Math.min(count, Math.max(0, click - range.start + 1)) : 0;
|
|
98
|
+
const steps = data.steps.map((s) => scheme === "dark" ? s.tokens.dark : s.tokens.light);
|
|
99
|
+
const duration = data.options.duration ? Number(data.options.duration) : void 0;
|
|
100
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
101
|
+
className: [
|
|
102
|
+
"deck-code-block",
|
|
103
|
+
"is-magic",
|
|
104
|
+
data.title ? "has-title" : ""
|
|
105
|
+
].filter(Boolean).join(" "),
|
|
106
|
+
children: [data.title ? /* @__PURE__ */ jsxs("div", {
|
|
107
|
+
className: "deck-code-title",
|
|
108
|
+
children: [/* @__PURE__ */ jsx("span", { className: "deck-code-title-dot" }), /* @__PURE__ */ jsx("span", { children: data.title })]
|
|
109
|
+
}) : null, /* @__PURE__ */ jsx("div", {
|
|
110
|
+
className: "deck-code shiki-magic-move-wrap",
|
|
111
|
+
children: /* @__PURE__ */ jsx(ShikiMagicMovePrecompiled, {
|
|
112
|
+
steps,
|
|
113
|
+
step,
|
|
114
|
+
animate: slide.active,
|
|
115
|
+
options: {
|
|
116
|
+
duration,
|
|
117
|
+
lineNumbers: data.steps[step]?.lines
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
})]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { Code, MagicMove };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { TargetAndTransition } from "motion/react";
|
|
3
|
+
//#region src/components/Motion.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Slidev's `v-motion`, in React: `initial` before the slide (or click) arrives, `enter` when it
|
|
6
|
+
* does, `leave` when it goes, plus `click-N` variants that apply from click N on. Backed by the
|
|
7
|
+
* motion library, so any animatable property works.
|
|
8
|
+
*/
|
|
9
|
+
export interface MotionProps {
|
|
10
|
+
initial?: TargetAndTransition;
|
|
11
|
+
enter?: TargetAndTransition;
|
|
12
|
+
leave?: TargetAndTransition;
|
|
13
|
+
/** Apply from a click on: `{ "click-1": { x: 40 }, "click-2": { y: 60 } }`. */
|
|
14
|
+
clicks?: Record<string, TargetAndTransition>;
|
|
15
|
+
/** Tie the whole thing to a click step (like `v-click` + `v-motion`). */
|
|
16
|
+
at?: number | string;
|
|
17
|
+
transition?: TargetAndTransition["transition"];
|
|
18
|
+
className?: string;
|
|
19
|
+
style?: React.CSSProperties;
|
|
20
|
+
children?: ReactNode;
|
|
21
|
+
as?: "div" | "span" | "p" | "li" | "h1" | "h2" | "h3" | "img";
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
export declare function Motion({ initial, enter, leave, clicks, at, transition, className, style, children, as, ...rest }: MotionProps): import("react").JSX.Element;
|
|
25
|
+
//#endregion
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useSlide } from "../react/context.js";
|
|
2
|
+
import { useClick } from "../react/hooks.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { motion } from "motion/react";
|
|
5
|
+
//#region src/components/Motion.tsx
|
|
6
|
+
function Motion({ initial = {
|
|
7
|
+
opacity: 0,
|
|
8
|
+
y: 24
|
|
9
|
+
}, enter = {
|
|
10
|
+
opacity: 1,
|
|
11
|
+
y: 0
|
|
12
|
+
}, leave, clicks = {}, at, transition, className, style, children, as = "div", ...rest }) {
|
|
13
|
+
const slide = useSlide();
|
|
14
|
+
const step = useClick(at !== void 0 ? { at } : { at: "+0" });
|
|
15
|
+
const gated = at !== void 0;
|
|
16
|
+
const on = slide.active && (!gated || step.shown);
|
|
17
|
+
let target = on ? { ...enter } : { ...initial };
|
|
18
|
+
if (on) for (const [key, value] of Object.entries(clicks)) {
|
|
19
|
+
const m = /^click-(\d+)(?:-(\d+))?$/.exec(key);
|
|
20
|
+
if (!m) continue;
|
|
21
|
+
const from = Number(m[1]);
|
|
22
|
+
const to = m[2] ? Number(m[2]) : Number.POSITIVE_INFINITY;
|
|
23
|
+
if (slide.click >= from && slide.click < to) target = {
|
|
24
|
+
...target,
|
|
25
|
+
...value
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const Tag = motion[as] ?? motion.div;
|
|
29
|
+
return /* @__PURE__ */ jsx(Tag, {
|
|
30
|
+
className: ["deck-motion", className].filter(Boolean).join(" "),
|
|
31
|
+
style,
|
|
32
|
+
initial,
|
|
33
|
+
animate: target,
|
|
34
|
+
exit: leave,
|
|
35
|
+
transition: transition ?? {
|
|
36
|
+
type: "spring",
|
|
37
|
+
stiffness: 260,
|
|
38
|
+
damping: 26
|
|
39
|
+
},
|
|
40
|
+
...rest,
|
|
41
|
+
children
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { Motion };
|