vite-plugin-react-deck 1.1.5 → 1.5.1
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/config.d.ts +1 -1
- package/helpers.d.ts +26 -2
- package/index.cjs +569 -147
- package/index.d.ts +2 -2
- package/index.mjs +570 -148
- package/package.json +11 -11
- package/slides.d.ts +1 -1
- package/themes/index.d.ts +1 -0
- package/themes/solarized-light.d.ts +11 -0
- package/types.d.ts +20 -7
package/index.mjs
CHANGED
|
@@ -6,12 +6,411 @@ var __export = (target, all) => {
|
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
8
|
import * as fs2 from "fs/promises";
|
|
9
|
+
import * as glob from "glob";
|
|
10
|
+
import matter2 from "gray-matter";
|
|
11
|
+
|
|
12
|
+
// src/themes/green.ts
|
|
13
|
+
var green_exports = {};
|
|
14
|
+
__export(green_exports, {
|
|
15
|
+
themeTokens: () => themeTokens
|
|
16
|
+
});
|
|
17
|
+
var colors = {
|
|
18
|
+
primary: "#FFFFFF",
|
|
19
|
+
secondary: "#F49676",
|
|
20
|
+
tertiary: "#042F3B"
|
|
21
|
+
};
|
|
22
|
+
var themeTokens = {
|
|
23
|
+
colors
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/themes/purple.ts
|
|
27
|
+
var purple_exports = {};
|
|
28
|
+
__export(purple_exports, {
|
|
29
|
+
themeTokens: () => themeTokens2
|
|
30
|
+
});
|
|
31
|
+
var colors2 = {
|
|
32
|
+
//primary: "#56D4F8",
|
|
33
|
+
primary: "#ffffff",
|
|
34
|
+
secondary: "#F530EC",
|
|
35
|
+
tertiary: "#2B135A"
|
|
36
|
+
};
|
|
37
|
+
var themeTokens2 = {
|
|
38
|
+
colors: colors2
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/themes/solarized-light.ts
|
|
42
|
+
var solarized_light_exports = {};
|
|
43
|
+
__export(solarized_light_exports, {
|
|
44
|
+
themeTokens: () => themeTokens3
|
|
45
|
+
});
|
|
46
|
+
var colors3 = {
|
|
47
|
+
primary: "#073642",
|
|
48
|
+
// base02 - dark text on light background
|
|
49
|
+
secondary: "#268bd2",
|
|
50
|
+
// blue accent
|
|
51
|
+
tertiary: "#fdf6e3"
|
|
52
|
+
// base3 - light background
|
|
53
|
+
};
|
|
54
|
+
var fonts = {
|
|
55
|
+
header: '"Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif',
|
|
56
|
+
text: '"Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif'
|
|
57
|
+
};
|
|
58
|
+
var themeTokens3 = {
|
|
59
|
+
colors: colors3,
|
|
60
|
+
fonts
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/helpers.ts
|
|
64
|
+
var themes = {
|
|
65
|
+
green: green_exports,
|
|
66
|
+
purple: purple_exports,
|
|
67
|
+
"solarized-light": solarized_light_exports
|
|
68
|
+
};
|
|
69
|
+
function createDecksIndexFile() {
|
|
70
|
+
return `<!DOCTYPE html>
|
|
71
|
+
<html lang="en">
|
|
72
|
+
<head>
|
|
73
|
+
<meta charset="utf-8" />
|
|
74
|
+
<title>Pestacle - Decks</title>
|
|
75
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
76
|
+
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
77
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
78
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
79
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
80
|
+
<style>* { margin: 0; padding: 0; box-sizing: border-box; }</style>
|
|
81
|
+
</head>
|
|
82
|
+
<body>
|
|
83
|
+
<div id="root"></div>
|
|
84
|
+
<script type="module" src="__decks.tsx"></script>
|
|
85
|
+
</body>
|
|
86
|
+
</html>
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
function createDecksPageFile({
|
|
90
|
+
decks,
|
|
91
|
+
theme
|
|
92
|
+
}) {
|
|
93
|
+
var _a;
|
|
94
|
+
const themeModule = themes[theme];
|
|
95
|
+
const colors4 = (_a = themeModule == null ? void 0 : themeModule.themeTokens) == null ? void 0 : _a.colors;
|
|
96
|
+
const primary = (colors4 == null ? void 0 : colors4.primary) ?? "#ffffff";
|
|
97
|
+
const secondary = (colors4 == null ? void 0 : colors4.secondary) ?? "#F49676";
|
|
98
|
+
const tertiary = (colors4 == null ? void 0 : colors4.tertiary) ?? "#042F3B";
|
|
99
|
+
return `import React, { StrictMode, useState } from "react";
|
|
100
|
+
import * as ReactDOM from "react-dom/client";
|
|
101
|
+
|
|
102
|
+
const decks = ${JSON.stringify(decks)};
|
|
103
|
+
|
|
104
|
+
function DeckCard({ deck }) {
|
|
105
|
+
const [hovered, setHovered] = useState(false);
|
|
106
|
+
const title = deck.title || deck.name;
|
|
107
|
+
const hasMeta = deck.author || deck.slideCount > 0;
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<a
|
|
111
|
+
href={deck.path}
|
|
112
|
+
onMouseEnter={() => setHovered(true)}
|
|
113
|
+
onMouseLeave={() => setHovered(false)}
|
|
114
|
+
style={{
|
|
115
|
+
display: "flex",
|
|
116
|
+
flexDirection: "column",
|
|
117
|
+
padding: "1.5rem",
|
|
118
|
+
borderRadius: 16,
|
|
119
|
+
backgroundColor: hovered ? "${secondary}18" : "${secondary}0a",
|
|
120
|
+
border: hovered ? "1px solid ${secondary}88" : "1px solid ${secondary}22",
|
|
121
|
+
color: "${primary}",
|
|
122
|
+
textDecoration: "none",
|
|
123
|
+
transition: "all 0.2s ease",
|
|
124
|
+
transform: hovered ? "translateY(-2px)" : "translateY(0)",
|
|
125
|
+
boxShadow: hovered
|
|
126
|
+
? "0 8px 24px rgba(0,0,0,0.2)"
|
|
127
|
+
: "0 2px 8px rgba(0,0,0,0.1)",
|
|
128
|
+
minHeight: 140,
|
|
129
|
+
justifyContent: "space-between",
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
<div>
|
|
133
|
+
<div style={{
|
|
134
|
+
fontSize: "1.25rem",
|
|
135
|
+
fontWeight: 600,
|
|
136
|
+
marginBottom: "0.5rem",
|
|
137
|
+
lineHeight: 1.3,
|
|
138
|
+
color: "${primary}",
|
|
139
|
+
}}>
|
|
140
|
+
{title}
|
|
141
|
+
</div>
|
|
142
|
+
{deck.description && (
|
|
143
|
+
<div style={{
|
|
144
|
+
fontSize: "0.875rem",
|
|
145
|
+
opacity: 0.6,
|
|
146
|
+
lineHeight: 1.5,
|
|
147
|
+
marginBottom: "0.75rem",
|
|
148
|
+
}}>
|
|
149
|
+
{deck.description}
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
<div style={{
|
|
154
|
+
display: "flex",
|
|
155
|
+
alignItems: "center",
|
|
156
|
+
gap: "1rem",
|
|
157
|
+
flexWrap: "wrap",
|
|
158
|
+
marginTop: "auto",
|
|
159
|
+
}}>
|
|
160
|
+
{deck.author && (
|
|
161
|
+
<span style={{
|
|
162
|
+
fontSize: "0.8rem",
|
|
163
|
+
opacity: 0.5,
|
|
164
|
+
display: "flex",
|
|
165
|
+
alignItems: "center",
|
|
166
|
+
gap: "0.35rem",
|
|
167
|
+
}}>
|
|
168
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
169
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
|
170
|
+
<circle cx="12" cy="7" r="4" />
|
|
171
|
+
</svg>
|
|
172
|
+
{deck.author}
|
|
173
|
+
</span>
|
|
174
|
+
)}
|
|
175
|
+
{deck.slideCount > 0 && (
|
|
176
|
+
<span style={{
|
|
177
|
+
fontSize: "0.8rem",
|
|
178
|
+
opacity: 0.5,
|
|
179
|
+
display: "flex",
|
|
180
|
+
alignItems: "center",
|
|
181
|
+
gap: "0.35rem",
|
|
182
|
+
}}>
|
|
183
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
184
|
+
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
|
|
185
|
+
<line x1="8" y1="21" x2="16" y2="21" />
|
|
186
|
+
<line x1="12" y1="17" x2="12" y2="21" />
|
|
187
|
+
</svg>
|
|
188
|
+
{deck.slideCount} slide{deck.slideCount !== 1 ? "s" : ""}
|
|
189
|
+
</span>
|
|
190
|
+
)}
|
|
191
|
+
{deck.date && (
|
|
192
|
+
<span style={{
|
|
193
|
+
fontSize: "0.8rem",
|
|
194
|
+
opacity: 0.5,
|
|
195
|
+
}}>
|
|
196
|
+
{deck.date}
|
|
197
|
+
</span>
|
|
198
|
+
)}
|
|
199
|
+
</div>
|
|
200
|
+
</a>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function DecksPage() {
|
|
205
|
+
const [search, setSearch] = useState("");
|
|
206
|
+
const [searchFocused, setSearchFocused] = useState(false);
|
|
207
|
+
const filtered = decks.filter((d) => {
|
|
208
|
+
const q = search.toLowerCase();
|
|
209
|
+
return (
|
|
210
|
+
d.name.toLowerCase().includes(q) ||
|
|
211
|
+
(d.title || "").toLowerCase().includes(q) ||
|
|
212
|
+
(d.author || "").toLowerCase().includes(q) ||
|
|
213
|
+
(d.description || "").toLowerCase().includes(q)
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<div style={{
|
|
219
|
+
minHeight: "100vh",
|
|
220
|
+
backgroundColor: "${tertiary}",
|
|
221
|
+
color: "${primary}",
|
|
222
|
+
fontFamily: "'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif",
|
|
223
|
+
}}>
|
|
224
|
+
<div style={{
|
|
225
|
+
maxWidth: 960,
|
|
226
|
+
margin: "0 auto",
|
|
227
|
+
padding: "4rem 2rem 3rem",
|
|
228
|
+
}}>
|
|
229
|
+
<div style={{ marginBottom: "3rem" }}>
|
|
230
|
+
<div style={{
|
|
231
|
+
display: "flex",
|
|
232
|
+
alignItems: "center",
|
|
233
|
+
gap: "0.75rem",
|
|
234
|
+
marginBottom: "0.75rem",
|
|
235
|
+
}}>
|
|
236
|
+
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="${secondary}" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
237
|
+
<polygon points="23 7 16 12 23 17 23 7" />
|
|
238
|
+
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
|
|
239
|
+
</svg>
|
|
240
|
+
<h1 style={{
|
|
241
|
+
fontSize: "2.5rem",
|
|
242
|
+
fontWeight: 700,
|
|
243
|
+
margin: 0,
|
|
244
|
+
background: "linear-gradient(135deg, ${secondary}, ${primary})",
|
|
245
|
+
WebkitBackgroundClip: "text",
|
|
246
|
+
WebkitTextFillColor: "transparent",
|
|
247
|
+
backgroundClip: "text",
|
|
248
|
+
}}>Pestacle</h1>
|
|
249
|
+
</div>
|
|
250
|
+
<p style={{
|
|
251
|
+
fontSize: "1rem",
|
|
252
|
+
opacity: 0.5,
|
|
253
|
+
margin: 0,
|
|
254
|
+
}}>{decks.length} presentation{decks.length !== 1 ? "s" : ""} available</p>
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
<div style={{
|
|
258
|
+
position: "relative",
|
|
259
|
+
marginBottom: "2rem",
|
|
260
|
+
}}>
|
|
261
|
+
<svg
|
|
262
|
+
width="18" height="18"
|
|
263
|
+
viewBox="0 0 24 24"
|
|
264
|
+
fill="none"
|
|
265
|
+
stroke="${primary}"
|
|
266
|
+
strokeWidth="2"
|
|
267
|
+
strokeLinecap="round"
|
|
268
|
+
strokeLinejoin="round"
|
|
269
|
+
style={{
|
|
270
|
+
position: "absolute",
|
|
271
|
+
left: 16,
|
|
272
|
+
top: "50%",
|
|
273
|
+
transform: "translateY(-50%)",
|
|
274
|
+
opacity: 0.4,
|
|
275
|
+
}}
|
|
276
|
+
>
|
|
277
|
+
<circle cx="11" cy="11" r="8" />
|
|
278
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
279
|
+
</svg>
|
|
280
|
+
<input
|
|
281
|
+
type="text"
|
|
282
|
+
placeholder="Search presentations..."
|
|
283
|
+
value={search}
|
|
284
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
285
|
+
onFocus={() => setSearchFocused(true)}
|
|
286
|
+
onBlur={() => setSearchFocused(false)}
|
|
287
|
+
autoFocus
|
|
288
|
+
style={{
|
|
289
|
+
width: "100%",
|
|
290
|
+
padding: "0.875rem 1rem 0.875rem 2.75rem",
|
|
291
|
+
fontSize: "1rem",
|
|
292
|
+
borderRadius: 12,
|
|
293
|
+
border: searchFocused ? "2px solid ${secondary}" : "2px solid ${secondary}33",
|
|
294
|
+
backgroundColor: "${secondary}08",
|
|
295
|
+
color: "${primary}",
|
|
296
|
+
outline: "none",
|
|
297
|
+
boxSizing: "border-box",
|
|
298
|
+
transition: "border-color 0.2s ease, background-color 0.2s ease",
|
|
299
|
+
}}
|
|
300
|
+
/>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
<div style={{
|
|
304
|
+
display: "grid",
|
|
305
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
|
|
306
|
+
gap: "1rem",
|
|
307
|
+
}}>
|
|
308
|
+
{filtered.map((deck) => (
|
|
309
|
+
<DeckCard key={deck.path} deck={deck} />
|
|
310
|
+
))}
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
{filtered.length === 0 && (
|
|
314
|
+
<div style={{
|
|
315
|
+
textAlign: "center",
|
|
316
|
+
padding: "4rem 2rem",
|
|
317
|
+
opacity: 0.4,
|
|
318
|
+
}}>
|
|
319
|
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" style={{ marginBottom: "1rem" }}>
|
|
320
|
+
<circle cx="11" cy="11" r="8" />
|
|
321
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
322
|
+
</svg>
|
|
323
|
+
<p style={{ fontSize: "1.1rem" }}>No presentations matching “{search}”</p>
|
|
324
|
+
</div>
|
|
325
|
+
)}
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const root = ReactDOM.createRoot(
|
|
332
|
+
document.getElementById("root") as HTMLElement
|
|
333
|
+
);
|
|
334
|
+
root.render(
|
|
335
|
+
<StrictMode>
|
|
336
|
+
<DecksPage />
|
|
337
|
+
</StrictMode>
|
|
338
|
+
);
|
|
339
|
+
`;
|
|
340
|
+
}
|
|
341
|
+
function createIndexFile({ entrypoint }) {
|
|
342
|
+
return `<!DOCTYPE html>
|
|
343
|
+
<html lang="en">
|
|
344
|
+
<head>
|
|
345
|
+
<meta charset="utf-8" />
|
|
346
|
+
<title>Title</title>
|
|
347
|
+
|
|
348
|
+
<style>
|
|
349
|
+
html {
|
|
350
|
+
--code-preview-background-color: #222;
|
|
351
|
+
}
|
|
352
|
+
</style>
|
|
353
|
+
|
|
354
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
355
|
+
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
356
|
+
</head>
|
|
357
|
+
<body>
|
|
358
|
+
<div id="root"></div>
|
|
359
|
+
<script type="module" src="${entrypoint}"></script>
|
|
360
|
+
</body>
|
|
361
|
+
</html>
|
|
362
|
+
`;
|
|
363
|
+
}
|
|
364
|
+
function createAppDeckFile({
|
|
365
|
+
slidePath,
|
|
366
|
+
theme,
|
|
367
|
+
deckTheme,
|
|
368
|
+
config
|
|
369
|
+
}) {
|
|
370
|
+
const resolvedThemeName = deckTheme ?? theme;
|
|
371
|
+
const isBuiltinTheme = resolvedThemeName in themes;
|
|
372
|
+
const isCustomThemePath = !isBuiltinTheme && (resolvedThemeName.startsWith("./") || resolvedThemeName.startsWith("../") || resolvedThemeName.startsWith("/"));
|
|
373
|
+
if (!isBuiltinTheme && !isCustomThemePath) {
|
|
374
|
+
const available = Object.keys(themes).join(", ");
|
|
375
|
+
throw new Error(
|
|
376
|
+
`Theme "${resolvedThemeName}" not found. Available built-in themes: ${available}. For custom themes, use a relative path (e.g. "./pestacle/themes/my-theme").`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
const layoutImport = config.layoutsFile ? `import layouts from "${config.layoutsFile}";` : "import { layouts } from '@gpichot/spectacle-deck';";
|
|
380
|
+
const themeCode = isBuiltinTheme ? `const theme = ${JSON.stringify(themes[resolvedThemeName])};` : `import { themeTokens as _customThemeTokens } from "${resolvedThemeName}";
|
|
381
|
+
const theme = { themeTokens: _customThemeTokens };`;
|
|
382
|
+
return `import React, { StrictMode } from "react";
|
|
383
|
+
import * as ReactDOM from "react-dom/client";
|
|
384
|
+
import { Deck } from '@gpichot/spectacle-deck';
|
|
385
|
+
${layoutImport};
|
|
386
|
+
|
|
387
|
+
import deck from "${slidePath}";
|
|
388
|
+
${themeCode}
|
|
389
|
+
|
|
390
|
+
const root = ReactDOM.createRoot(
|
|
391
|
+
document.getElementById("root") as HTMLElement
|
|
392
|
+
);
|
|
393
|
+
root.render(
|
|
394
|
+
<StrictMode>
|
|
395
|
+
<Deck deck={deck} theme={theme} layouts={layouts}${config.transition ? ` transition="${config.transition}"` : ""} />
|
|
396
|
+
</StrictMode>
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
let link = document.createElement('link')
|
|
400
|
+
link.rel = 'stylesheet'
|
|
401
|
+
link.type = 'text/css'
|
|
402
|
+
link.href = 'https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css'
|
|
403
|
+
document.head.appendChild(link)
|
|
404
|
+
`;
|
|
405
|
+
}
|
|
9
406
|
|
|
10
407
|
// src/slides.ts
|
|
11
|
-
import matter from "gray-matter";
|
|
12
|
-
import remarkDirective from "remark-directive";
|
|
13
408
|
import fs from "fs";
|
|
14
409
|
import { compile } from "@mdx-js/mdx";
|
|
410
|
+
import matter from "gray-matter";
|
|
411
|
+
import remarkDirective from "remark-directive";
|
|
412
|
+
import remarkGfm from "remark-gfm";
|
|
413
|
+
import { visit } from "unist-util-visit";
|
|
15
414
|
|
|
16
415
|
// src/codegen.ts
|
|
17
416
|
var Patterns = {
|
|
@@ -49,13 +448,14 @@ ${header}
|
|
|
49
448
|
}
|
|
50
449
|
|
|
51
450
|
// src/slides.ts
|
|
52
|
-
import remarkGfm from "remark-gfm";
|
|
53
|
-
import { visit } from "unist-util-visit";
|
|
54
451
|
function myRemarkPlugin() {
|
|
55
452
|
return (tree) => {
|
|
56
453
|
visit(tree, (node) => {
|
|
57
454
|
if (node.type === "containerDirective" || node.type === "leafDirective" || node.type === "textDirective") {
|
|
58
|
-
|
|
455
|
+
if (!node.data) {
|
|
456
|
+
node.data = {};
|
|
457
|
+
}
|
|
458
|
+
const data = node.data;
|
|
59
459
|
data.hName = "directive";
|
|
60
460
|
data.hProperties = node.attributes;
|
|
61
461
|
data.hProperties._name = node.name;
|
|
@@ -74,10 +474,9 @@ async function transformSlidesMdxToReact(sourceContent, {
|
|
|
74
474
|
);
|
|
75
475
|
const slides = finalContent.split("---\n");
|
|
76
476
|
const enrichedSlides = [];
|
|
77
|
-
const LAYOUT_REGEX = /\S*\nlayout: (.*)/g;
|
|
78
477
|
let frontmatterForNextSlide = null;
|
|
79
478
|
for (const slide of slides) {
|
|
80
|
-
if (
|
|
479
|
+
if (isFrontmatterBlock(slide)) {
|
|
81
480
|
frontmatterForNextSlide = matter(`---
|
|
82
481
|
${slide}
|
|
83
482
|
---`).data;
|
|
@@ -90,12 +489,12 @@ ${slide}
|
|
|
90
489
|
frontmatterForNextSlide = null;
|
|
91
490
|
}
|
|
92
491
|
const compiledSlides = await Promise.all(
|
|
93
|
-
enrichedSlides.map(async (slide,
|
|
492
|
+
enrichedSlides.map(async (slide, _index) => {
|
|
94
493
|
const code = addInlineModules(slide.content, inlineModules);
|
|
95
494
|
const normalizedCode = code.replace("process.env", "process\u200B.env");
|
|
96
495
|
const result = await compile(normalizedCode, {
|
|
97
496
|
outputFormat: "program",
|
|
98
|
-
jsx:
|
|
497
|
+
jsx: false,
|
|
99
498
|
providerImportSource: "@mdx-js/react",
|
|
100
499
|
...options,
|
|
101
500
|
remarkPlugins: [
|
|
@@ -106,7 +505,7 @@ ${slide}
|
|
|
106
505
|
]
|
|
107
506
|
});
|
|
108
507
|
const mainCode = extractMainCodeAsChildren(result.value.toString(), {
|
|
109
|
-
isJsx:
|
|
508
|
+
isJsx: false
|
|
110
509
|
});
|
|
111
510
|
return {
|
|
112
511
|
...slide,
|
|
@@ -117,7 +516,8 @@ ${slide}
|
|
|
117
516
|
const output = addInlineModules(
|
|
118
517
|
`
|
|
119
518
|
import React from 'react';
|
|
120
|
-
|
|
519
|
+
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
|
|
520
|
+
import {useMDXComponents as _provideComponents} from "@mdx-js/react";
|
|
121
521
|
|
|
122
522
|
${compiledSlides.map(
|
|
123
523
|
(slide, index) => `
|
|
@@ -165,116 +565,24 @@ ${[...inlineModules.keys()].join("\n")}
|
|
|
165
565
|
${source}
|
|
166
566
|
`;
|
|
167
567
|
}
|
|
568
|
+
function isFrontmatterBlock(text) {
|
|
569
|
+
const lines = text.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
570
|
+
return lines.length > 0 && lines.every((l) => /^[\w][\w-]*\s*:/.test(l));
|
|
571
|
+
}
|
|
168
572
|
var CRLF = "\r\n";
|
|
169
573
|
function normalizeNewline(input) {
|
|
170
574
|
return input.replace(new RegExp(CRLF, "g"), "\n");
|
|
171
575
|
}
|
|
172
576
|
|
|
173
|
-
// src/themes/index.ts
|
|
174
|
-
var themes_exports = {};
|
|
175
|
-
__export(themes_exports, {
|
|
176
|
-
green: () => green_exports,
|
|
177
|
-
purple: () => purple_exports
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// src/themes/green.ts
|
|
181
|
-
var green_exports = {};
|
|
182
|
-
__export(green_exports, {
|
|
183
|
-
themeTokens: () => themeTokens
|
|
184
|
-
});
|
|
185
|
-
var colors = {
|
|
186
|
-
primary: "#FFFFFF",
|
|
187
|
-
secondary: "#F49676",
|
|
188
|
-
tertiary: "#042F3B"
|
|
189
|
-
};
|
|
190
|
-
var themeTokens = {
|
|
191
|
-
colors
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
// src/themes/purple.ts
|
|
195
|
-
var purple_exports = {};
|
|
196
|
-
__export(purple_exports, {
|
|
197
|
-
themeTokens: () => themeTokens2
|
|
198
|
-
});
|
|
199
|
-
var colors2 = {
|
|
200
|
-
//primary: "#56D4F8",
|
|
201
|
-
primary: "#ffffff",
|
|
202
|
-
secondary: "#F530EC",
|
|
203
|
-
tertiary: "#2B135A"
|
|
204
|
-
};
|
|
205
|
-
var themeTokens2 = {
|
|
206
|
-
colors: colors2
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
// src/helpers.ts
|
|
210
|
-
function createIndexFile({ entrypoint }) {
|
|
211
|
-
return `<!DOCTYPE html>
|
|
212
|
-
<html lang="en">
|
|
213
|
-
<head>
|
|
214
|
-
<meta charset="utf-8" />
|
|
215
|
-
<title>Title</title>
|
|
216
|
-
|
|
217
|
-
<style>
|
|
218
|
-
html {
|
|
219
|
-
--code-preview-background-color: #222;
|
|
220
|
-
}
|
|
221
|
-
</style>
|
|
222
|
-
|
|
223
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
224
|
-
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
225
|
-
</head>
|
|
226
|
-
<body>
|
|
227
|
-
<div id="root"></div>
|
|
228
|
-
<script type="module" src="${entrypoint}"></script>
|
|
229
|
-
</body>
|
|
230
|
-
</html>
|
|
231
|
-
`;
|
|
232
|
-
}
|
|
233
|
-
function createAppDeckFile({
|
|
234
|
-
slidePath,
|
|
235
|
-
theme,
|
|
236
|
-
config
|
|
237
|
-
}) {
|
|
238
|
-
if (!themes_exports[theme]) {
|
|
239
|
-
throw new Error(`Theme ${theme} not found`);
|
|
240
|
-
}
|
|
241
|
-
const themeObject = themes_exports[theme];
|
|
242
|
-
const layoutImport = config.layoutsFile ? `import layouts from "${config.layoutsFile}";` : "import { layouts } from '@gpichot/spectacle-deck';";
|
|
243
|
-
return `import React, { StrictMode } from "react";
|
|
244
|
-
import * as ReactDOM from "react-dom/client";
|
|
245
|
-
import { Deck } from '@gpichot/spectacle-deck';
|
|
246
|
-
${layoutImport};
|
|
247
|
-
|
|
248
|
-
import deck from "${slidePath}";
|
|
249
|
-
|
|
250
|
-
const root = ReactDOM.createRoot(
|
|
251
|
-
document.getElementById("root") as HTMLElement
|
|
252
|
-
);
|
|
253
|
-
root.render(
|
|
254
|
-
<StrictMode>
|
|
255
|
-
<Deck deck={deck} theme={${JSON.stringify(themeObject)}} layouts={layouts} />
|
|
256
|
-
</StrictMode>
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
let link = document.createElement('link')
|
|
260
|
-
link.rel = 'stylesheet'
|
|
261
|
-
link.type = 'text/css'
|
|
262
|
-
link.href = 'https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css'
|
|
263
|
-
document.head.appendChild(link)
|
|
264
|
-
`;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// src/index.ts
|
|
268
|
-
import * as glob from "glob";
|
|
269
|
-
import path from "path";
|
|
270
|
-
|
|
271
577
|
// src/config.ts
|
|
272
578
|
import _ from "lodash";
|
|
273
579
|
|
|
274
580
|
// src/types.ts
|
|
275
581
|
import { z } from "zod";
|
|
276
582
|
var PestacleConfigSchema = z.object({
|
|
277
|
-
theme: z.enum(["green", "purple"]).default("green")
|
|
583
|
+
theme: z.enum(["green", "purple", "solarized-light"]).default("green"),
|
|
584
|
+
startupPage: z.boolean().optional(),
|
|
585
|
+
transition: z.enum(["fade", "slide", "drop", "none"]).optional()
|
|
278
586
|
});
|
|
279
587
|
|
|
280
588
|
// src/config.ts
|
|
@@ -283,9 +591,9 @@ function defineConfig(config) {
|
|
|
283
591
|
}
|
|
284
592
|
|
|
285
593
|
// src/index.ts
|
|
286
|
-
async function checkIfDirectoryExists(
|
|
594
|
+
async function checkIfDirectoryExists(path) {
|
|
287
595
|
try {
|
|
288
|
-
await fs2.access(
|
|
596
|
+
await fs2.access(path);
|
|
289
597
|
return true;
|
|
290
598
|
} catch {
|
|
291
599
|
return false;
|
|
@@ -298,15 +606,45 @@ async function findDecks({ port }) {
|
|
|
298
606
|
deckUrl: `http://localhost:${port}/${deck.replace("src/", "").replace("deck.mdx", "")}`
|
|
299
607
|
}));
|
|
300
608
|
}
|
|
301
|
-
async function
|
|
609
|
+
async function extractDeckMeta(filePath) {
|
|
610
|
+
try {
|
|
611
|
+
const raw = await fs2.readFile(filePath, "utf-8");
|
|
612
|
+
const { data, content } = matter2(raw);
|
|
613
|
+
const slides = content.split("---\n");
|
|
614
|
+
const LAYOUT_REGEX = /\S*\nlayout: (.*)/g;
|
|
615
|
+
let slideCount = 0;
|
|
616
|
+
for (const slide of slides) {
|
|
617
|
+
if (!LAYOUT_REGEX.test(slide) && slide.trim().length > 0) {
|
|
618
|
+
slideCount++;
|
|
619
|
+
}
|
|
620
|
+
LAYOUT_REGEX.lastIndex = 0;
|
|
621
|
+
}
|
|
622
|
+
let title = data.title;
|
|
623
|
+
if (!title) {
|
|
624
|
+
const headingMatch = content.match(/^#\s+\*{0,2}(.+?)\*{0,2}\s*$/m);
|
|
625
|
+
if (headingMatch) {
|
|
626
|
+
title = headingMatch[1].replace(/\*+/g, "").trim();
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
title,
|
|
631
|
+
author: data.author,
|
|
632
|
+
date: data.date,
|
|
633
|
+
description: data.description,
|
|
634
|
+
slideCount
|
|
635
|
+
};
|
|
636
|
+
} catch {
|
|
637
|
+
return { slideCount: 0 };
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
async function fileExists(_name, path) {
|
|
302
641
|
const candidateExts = [".ts", ".tsx", ".js", ".jsx"];
|
|
303
642
|
for await (const ext of candidateExts) {
|
|
304
|
-
const fullPath = `${
|
|
643
|
+
const fullPath = `${path}${ext}`;
|
|
305
644
|
try {
|
|
306
645
|
await fs2.access(fullPath);
|
|
307
646
|
return fullPath.replace(/^\./, "");
|
|
308
647
|
} catch {
|
|
309
|
-
continue;
|
|
310
648
|
}
|
|
311
649
|
}
|
|
312
650
|
}
|
|
@@ -316,32 +654,39 @@ async function loadCustomConfig() {
|
|
|
316
654
|
layoutsFile
|
|
317
655
|
};
|
|
318
656
|
}
|
|
319
|
-
var
|
|
657
|
+
var index_default = async (options) => {
|
|
320
658
|
let isProd = false;
|
|
659
|
+
const showStartupPage = options.startupPage;
|
|
321
660
|
const deckConfig = {
|
|
322
661
|
decks: []
|
|
323
662
|
};
|
|
324
663
|
return {
|
|
325
664
|
name: "react-deck",
|
|
326
|
-
async config(config) {
|
|
327
|
-
var _a, _b
|
|
665
|
+
async config(config, env) {
|
|
666
|
+
var _a, _b;
|
|
328
667
|
const decks = await glob.glob("./src/**/deck.mdx");
|
|
329
|
-
const inputs = ((_b = (_a = config.build) == null ? void 0 : _a.
|
|
330
|
-
deckConfig.decks = decks.map((deck) =>
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
668
|
+
const inputs = ((_b = (_a = config.build) == null ? void 0 : _a.rolldownOptions) == null ? void 0 : _b.input) || {};
|
|
669
|
+
deckConfig.decks = decks.map((deck) => {
|
|
670
|
+
const name = deck.replace("./src/", "").replace("/deck.mdx", "").replace(/\//g, " / ");
|
|
671
|
+
return {
|
|
672
|
+
originalFile: deck,
|
|
673
|
+
index: deck.replace("/deck.mdx", "/index.html").replace("src/", ""),
|
|
674
|
+
name
|
|
675
|
+
};
|
|
676
|
+
});
|
|
677
|
+
const isProduction = env.mode === "production";
|
|
678
|
+
const startupPageEnabled = showStartupPage !== void 0 ? showStartupPage : !isProduction;
|
|
679
|
+
const newInputs = decks.map((deck) => {
|
|
335
680
|
const deckPath = deck.replace("/deck.mdx", "");
|
|
336
|
-
return
|
|
337
|
-
}
|
|
681
|
+
return `${deckPath.replace("src/", "")}/index.html`;
|
|
682
|
+
});
|
|
683
|
+
if (startupPageEnabled) {
|
|
684
|
+
newInputs.unshift("index.html");
|
|
685
|
+
}
|
|
338
686
|
const finalInputs = typeof inputs === "string" ? [inputs, ...decks] : Array.isArray(inputs) ? [...inputs, ...decks] : { ...inputs, ...newInputs };
|
|
339
687
|
return {
|
|
340
|
-
...config,
|
|
341
688
|
build: {
|
|
342
|
-
|
|
343
|
-
rollupOptions: {
|
|
344
|
-
...(_c = config.build) == null ? void 0 : _c.rollupOptions,
|
|
689
|
+
rolldownOptions: {
|
|
345
690
|
input: finalInputs
|
|
346
691
|
}
|
|
347
692
|
}
|
|
@@ -351,6 +696,9 @@ var src_default = async (options) => {
|
|
|
351
696
|
isProd = config.isProduction;
|
|
352
697
|
},
|
|
353
698
|
resolveId(id) {
|
|
699
|
+
if (id === "index.html" || id === "__decks.tsx" || id === "/__decks.tsx") {
|
|
700
|
+
return id.replace(/^\//, "");
|
|
701
|
+
}
|
|
354
702
|
if (deckConfig.decks.some((deck) => deck.index === id)) {
|
|
355
703
|
return id;
|
|
356
704
|
}
|
|
@@ -362,6 +710,27 @@ var src_default = async (options) => {
|
|
|
362
710
|
}
|
|
363
711
|
},
|
|
364
712
|
async load(id) {
|
|
713
|
+
const shouldShowStartupPage = showStartupPage !== void 0 ? showStartupPage : !isProd;
|
|
714
|
+
if (id === "index.html" && shouldShowStartupPage) {
|
|
715
|
+
return createDecksIndexFile();
|
|
716
|
+
}
|
|
717
|
+
if (id === "__decks.tsx") {
|
|
718
|
+
const decks = await Promise.all(
|
|
719
|
+
deckConfig.decks.map(async (d) => {
|
|
720
|
+
const meta = await extractDeckMeta(d.originalFile);
|
|
721
|
+
return {
|
|
722
|
+
name: d.name,
|
|
723
|
+
path: `/${d.index.replace("/index.html", "/")}`,
|
|
724
|
+
title: meta.title,
|
|
725
|
+
author: meta.author,
|
|
726
|
+
date: meta.date,
|
|
727
|
+
description: meta.description,
|
|
728
|
+
slideCount: meta.slideCount
|
|
729
|
+
};
|
|
730
|
+
})
|
|
731
|
+
);
|
|
732
|
+
return createDecksPageFile({ decks, theme: options.theme });
|
|
733
|
+
}
|
|
365
734
|
const config = await loadCustomConfig();
|
|
366
735
|
const deck = deckConfig.decks.find((deck2) => deck2.index === id);
|
|
367
736
|
if (deck) {
|
|
@@ -371,18 +740,27 @@ var src_default = async (options) => {
|
|
|
371
740
|
}
|
|
372
741
|
if (id.endsWith("__deck.tsx")) {
|
|
373
742
|
const directory = id.replace("/__deck.tsx", "");
|
|
374
|
-
const
|
|
375
|
-
const
|
|
376
|
-
if (!await checkIfDirectoryExists(
|
|
377
|
-
|
|
743
|
+
const dir = directory.startsWith(".") ? directory : `./${directory}`;
|
|
744
|
+
const deckMdxPath = `${dir}/deck.mdx`;
|
|
745
|
+
if (!await checkIfDirectoryExists(deckMdxPath)) {
|
|
746
|
+
this.warn(`No deck.mdx file found in ${deckMdxPath}`);
|
|
378
747
|
return;
|
|
379
748
|
}
|
|
749
|
+
let deckTheme;
|
|
750
|
+
try {
|
|
751
|
+
const raw = await fs2.readFile(deckMdxPath, "utf-8");
|
|
752
|
+
const { data: data2 } = matter2(raw);
|
|
753
|
+
if (typeof data2.theme === "string") {
|
|
754
|
+
deckTheme = data2.theme;
|
|
755
|
+
}
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
380
758
|
const contentIndex = createAppDeckFile({
|
|
381
759
|
slidePath: `${directory}/deck.mdx`,
|
|
382
760
|
theme: options.theme,
|
|
383
|
-
|
|
761
|
+
deckTheme,
|
|
762
|
+
config: { ...config, transition: options.transition }
|
|
384
763
|
});
|
|
385
|
-
console.log({ contentIndex });
|
|
386
764
|
return contentIndex;
|
|
387
765
|
}
|
|
388
766
|
if (!id.endsWith("deck.mdx")) {
|
|
@@ -393,38 +771,82 @@ var src_default = async (options) => {
|
|
|
393
771
|
production: isProd,
|
|
394
772
|
...options
|
|
395
773
|
});
|
|
396
|
-
const dir = path.relative(process.cwd(), id);
|
|
397
774
|
return data;
|
|
398
775
|
},
|
|
399
776
|
transformIndexHtml: {
|
|
400
777
|
order: "pre",
|
|
401
|
-
|
|
402
|
-
transform: async (html, ctx) => {
|
|
778
|
+
handler: async (html, ctx) => {
|
|
403
779
|
var _a;
|
|
404
780
|
const originalUrl = ((_a = ctx.originalUrl) == null ? void 0 : _a.split("?")[0]) || "";
|
|
781
|
+
if (originalUrl === "/" || originalUrl === "") {
|
|
782
|
+
const shouldShow = showStartupPage !== void 0 ? showStartupPage : !isProd;
|
|
783
|
+
if (shouldShow) {
|
|
784
|
+
return html.replace("__SCRIPT__", `__decks.tsx`);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
405
787
|
const deckDir = ctx.path.replace("/index.html", "");
|
|
406
788
|
const dir = originalUrl ? `./src${originalUrl}` : `.${deckDir}`;
|
|
407
|
-
const
|
|
408
|
-
if (
|
|
409
|
-
|
|
789
|
+
const deckPath = `${dir}/deck.mdx`;
|
|
790
|
+
if (await checkIfDirectoryExists(deckPath)) {
|
|
791
|
+
const resolvedDeckPath = dir.startsWith(".") ? dir : `.${dir}`;
|
|
792
|
+
return html.replace("__SCRIPT__", `${resolvedDeckPath}/__deck.tsx`);
|
|
410
793
|
}
|
|
411
|
-
const
|
|
412
|
-
|
|
794
|
+
const decks = await glob.glob("./src/**/deck.mdx");
|
|
795
|
+
const deckLinks = decks.map((d) => {
|
|
796
|
+
const url = `/${d.replace("src/", "").replace("/deck.mdx", "")}/`;
|
|
797
|
+
const name = d.replace("src/", "").replace("/deck.mdx", "");
|
|
798
|
+
return `<li><a href="${url}">${name}</a></li>`;
|
|
799
|
+
}).join("\n");
|
|
800
|
+
return html.replace('<script type="module" src="__SCRIPT__"></script>', "").replace(
|
|
801
|
+
'<div id="root"></div>',
|
|
802
|
+
`<div id="root">
|
|
803
|
+
<h1>Available Decks</h1>
|
|
804
|
+
<ul>${deckLinks}</ul>
|
|
805
|
+
</div>`
|
|
806
|
+
);
|
|
413
807
|
}
|
|
414
808
|
},
|
|
415
809
|
configureServer(server) {
|
|
416
810
|
var _a;
|
|
811
|
+
const shouldShow = showStartupPage !== void 0 ? showStartupPage : true;
|
|
812
|
+
if (shouldShow) {
|
|
813
|
+
server.middlewares.use(async (req, res, next) => {
|
|
814
|
+
var _a2;
|
|
815
|
+
const url = ((_a2 = req.url) == null ? void 0 : _a2.split("?")[0]) || "";
|
|
816
|
+
if (url === "/" || url === "/index.html") {
|
|
817
|
+
const html = createDecksIndexFile();
|
|
818
|
+
const transformed = await server.transformIndexHtml(
|
|
819
|
+
url,
|
|
820
|
+
html,
|
|
821
|
+
req.originalUrl
|
|
822
|
+
);
|
|
823
|
+
res.setHeader("Content-Type", "text/html");
|
|
824
|
+
res.statusCode = 200;
|
|
825
|
+
res.end(transformed);
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
next();
|
|
829
|
+
});
|
|
830
|
+
}
|
|
417
831
|
(_a = server.httpServer) == null ? void 0 : _a.once("listening", async () => {
|
|
418
832
|
const port = server.config.server.port || 5173;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
833
|
+
if (shouldShow) {
|
|
834
|
+
server.config.logger.info(
|
|
835
|
+
`
|
|
836
|
+
Decks available at http://localhost:${port}/
|
|
837
|
+
`
|
|
838
|
+
);
|
|
839
|
+
} else {
|
|
840
|
+
const decks = await findDecks({ port });
|
|
841
|
+
for (const deck of decks) {
|
|
842
|
+
server.config.logger.info(`Deck available at ${deck.deckUrl}`);
|
|
843
|
+
}
|
|
422
844
|
}
|
|
423
845
|
});
|
|
424
846
|
}
|
|
425
847
|
};
|
|
426
848
|
};
|
|
427
849
|
export {
|
|
428
|
-
|
|
850
|
+
index_default as default,
|
|
429
851
|
defineConfig
|
|
430
852
|
};
|