vite-plugin-react-deck 1.1.5 → 1.4.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/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 &ldquo;{search}&rdquo;</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} />
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
- const data = node.data || (node.data = {});
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;
@@ -90,12 +490,12 @@ ${slide}
90
490
  frontmatterForNextSlide = null;
91
491
  }
92
492
  const compiledSlides = await Promise.all(
93
- enrichedSlides.map(async (slide, index) => {
493
+ enrichedSlides.map(async (slide, _index) => {
94
494
  const code = addInlineModules(slide.content, inlineModules);
95
495
  const normalizedCode = code.replace("process.env", "process\u200B.env");
96
496
  const result = await compile(normalizedCode, {
97
497
  outputFormat: "program",
98
- jsx: !isProd,
498
+ jsx: false,
99
499
  providerImportSource: "@mdx-js/react",
100
500
  ...options,
101
501
  remarkPlugins: [
@@ -106,7 +506,7 @@ ${slide}
106
506
  ]
107
507
  });
108
508
  const mainCode = extractMainCodeAsChildren(result.value.toString(), {
109
- isJsx: !isProd
509
+ isJsx: false
110
510
  });
111
511
  return {
112
512
  ...slide,
@@ -117,7 +517,8 @@ ${slide}
117
517
  const output = addInlineModules(
118
518
  `
119
519
  import React from 'react';
120
- ${isProd ? 'import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";import {useMDXComponents as _provideComponents} from "@mdx-js/react" ' : "import {useMDXComponents as _provideComponents} from '@mdx-js/react';"}
520
+ import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from "react/jsx-runtime";
521
+ import {useMDXComponents as _provideComponents} from "@mdx-js/react";
121
522
 
122
523
  ${compiledSlides.map(
123
524
  (slide, index) => `
@@ -170,111 +571,14 @@ function normalizeNewline(input) {
170
571
  return input.replace(new RegExp(CRLF, "g"), "\n");
171
572
  }
172
573
 
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
574
  // src/config.ts
272
575
  import _ from "lodash";
273
576
 
274
577
  // src/types.ts
275
578
  import { z } from "zod";
276
579
  var PestacleConfigSchema = z.object({
277
- theme: z.enum(["green", "purple"]).default("green")
580
+ theme: z.enum(["green", "purple", "solarized-light"]).default("green"),
581
+ startupPage: z.boolean().optional()
278
582
  });
279
583
 
280
584
  // src/config.ts
@@ -283,9 +587,9 @@ function defineConfig(config) {
283
587
  }
284
588
 
285
589
  // src/index.ts
286
- async function checkIfDirectoryExists(path2) {
590
+ async function checkIfDirectoryExists(path) {
287
591
  try {
288
- await fs2.access(path2);
592
+ await fs2.access(path);
289
593
  return true;
290
594
  } catch {
291
595
  return false;
@@ -298,15 +602,45 @@ async function findDecks({ port }) {
298
602
  deckUrl: `http://localhost:${port}/${deck.replace("src/", "").replace("deck.mdx", "")}`
299
603
  }));
300
604
  }
301
- async function fileExists(name, path2) {
605
+ async function extractDeckMeta(filePath) {
606
+ try {
607
+ const raw = await fs2.readFile(filePath, "utf-8");
608
+ const { data, content } = matter2(raw);
609
+ const slides = content.split("---\n");
610
+ const LAYOUT_REGEX = /\S*\nlayout: (.*)/g;
611
+ let slideCount = 0;
612
+ for (const slide of slides) {
613
+ if (!LAYOUT_REGEX.test(slide) && slide.trim().length > 0) {
614
+ slideCount++;
615
+ }
616
+ LAYOUT_REGEX.lastIndex = 0;
617
+ }
618
+ let title = data.title;
619
+ if (!title) {
620
+ const headingMatch = content.match(/^#\s+\*{0,2}(.+?)\*{0,2}\s*$/m);
621
+ if (headingMatch) {
622
+ title = headingMatch[1].replace(/\*+/g, "").trim();
623
+ }
624
+ }
625
+ return {
626
+ title,
627
+ author: data.author,
628
+ date: data.date,
629
+ description: data.description,
630
+ slideCount
631
+ };
632
+ } catch {
633
+ return { slideCount: 0 };
634
+ }
635
+ }
636
+ async function fileExists(_name, path) {
302
637
  const candidateExts = [".ts", ".tsx", ".js", ".jsx"];
303
638
  for await (const ext of candidateExts) {
304
- const fullPath = `${path2}${ext}`;
639
+ const fullPath = `${path}${ext}`;
305
640
  try {
306
641
  await fs2.access(fullPath);
307
642
  return fullPath.replace(/^\./, "");
308
643
  } catch {
309
- continue;
310
644
  }
311
645
  }
312
646
  }
@@ -316,32 +650,39 @@ async function loadCustomConfig() {
316
650
  layoutsFile
317
651
  };
318
652
  }
319
- var src_default = async (options) => {
653
+ var index_default = async (options) => {
320
654
  let isProd = false;
655
+ const showStartupPage = options.startupPage;
321
656
  const deckConfig = {
322
657
  decks: []
323
658
  };
324
659
  return {
325
660
  name: "react-deck",
326
- async config(config) {
327
- var _a, _b, _c;
661
+ async config(config, env) {
662
+ var _a, _b;
328
663
  const decks = await glob.glob("./src/**/deck.mdx");
329
- const inputs = ((_b = (_a = config.build) == null ? void 0 : _a.rollupOptions) == null ? void 0 : _b.input) || {};
330
- deckConfig.decks = decks.map((deck) => ({
331
- originalFile: deck,
332
- index: deck.replace("/deck.mdx", "/index.html").replace("src/", "")
333
- }));
334
- const newInputs = decks.reduce((acc, deck) => {
664
+ const inputs = ((_b = (_a = config.build) == null ? void 0 : _a.rolldownOptions) == null ? void 0 : _b.input) || {};
665
+ deckConfig.decks = decks.map((deck) => {
666
+ const name = deck.replace("./src/", "").replace("/deck.mdx", "").replace(/\//g, " / ");
667
+ return {
668
+ originalFile: deck,
669
+ index: deck.replace("/deck.mdx", "/index.html").replace("src/", ""),
670
+ name
671
+ };
672
+ });
673
+ const isProduction = env.mode === "production";
674
+ const startupPageEnabled = showStartupPage !== void 0 ? showStartupPage : !isProduction;
675
+ const newInputs = decks.map((deck) => {
335
676
  const deckPath = deck.replace("/deck.mdx", "");
336
- return [...acc, `${deckPath.replace("src/", "")}/index.html`];
337
- }, []);
677
+ return `${deckPath.replace("src/", "")}/index.html`;
678
+ });
679
+ if (startupPageEnabled) {
680
+ newInputs.unshift("index.html");
681
+ }
338
682
  const finalInputs = typeof inputs === "string" ? [inputs, ...decks] : Array.isArray(inputs) ? [...inputs, ...decks] : { ...inputs, ...newInputs };
339
683
  return {
340
- ...config,
341
684
  build: {
342
- ...config.build,
343
- rollupOptions: {
344
- ...(_c = config.build) == null ? void 0 : _c.rollupOptions,
685
+ rolldownOptions: {
345
686
  input: finalInputs
346
687
  }
347
688
  }
@@ -351,6 +692,9 @@ var src_default = async (options) => {
351
692
  isProd = config.isProduction;
352
693
  },
353
694
  resolveId(id) {
695
+ if (id === "index.html" || id === "__decks.tsx" || id === "/__decks.tsx") {
696
+ return id.replace(/^\//, "");
697
+ }
354
698
  if (deckConfig.decks.some((deck) => deck.index === id)) {
355
699
  return id;
356
700
  }
@@ -362,6 +706,27 @@ var src_default = async (options) => {
362
706
  }
363
707
  },
364
708
  async load(id) {
709
+ const shouldShowStartupPage = showStartupPage !== void 0 ? showStartupPage : !isProd;
710
+ if (id === "index.html" && shouldShowStartupPage) {
711
+ return createDecksIndexFile();
712
+ }
713
+ if (id === "__decks.tsx") {
714
+ const decks = await Promise.all(
715
+ deckConfig.decks.map(async (d) => {
716
+ const meta = await extractDeckMeta(d.originalFile);
717
+ return {
718
+ name: d.name,
719
+ path: `/${d.index.replace("/index.html", "/")}`,
720
+ title: meta.title,
721
+ author: meta.author,
722
+ date: meta.date,
723
+ description: meta.description,
724
+ slideCount: meta.slideCount
725
+ };
726
+ })
727
+ );
728
+ return createDecksPageFile({ decks, theme: options.theme });
729
+ }
365
730
  const config = await loadCustomConfig();
366
731
  const deck = deckConfig.decks.find((deck2) => deck2.index === id);
367
732
  if (deck) {
@@ -371,18 +736,27 @@ var src_default = async (options) => {
371
736
  }
372
737
  if (id.endsWith("__deck.tsx")) {
373
738
  const directory = id.replace("/__deck.tsx", "");
374
- const dir2 = directory.startsWith(".") ? directory : `./${directory}`;
375
- const path2 = `${dir2}/deck.mdx`;
376
- if (!await checkIfDirectoryExists(path2)) {
377
- console.warn(`No deck.mdx file found in ${path2}`);
739
+ const dir = directory.startsWith(".") ? directory : `./${directory}`;
740
+ const deckMdxPath = `${dir}/deck.mdx`;
741
+ if (!await checkIfDirectoryExists(deckMdxPath)) {
742
+ this.warn(`No deck.mdx file found in ${deckMdxPath}`);
378
743
  return;
379
744
  }
745
+ let deckTheme;
746
+ try {
747
+ const raw = await fs2.readFile(deckMdxPath, "utf-8");
748
+ const { data: data2 } = matter2(raw);
749
+ if (typeof data2.theme === "string") {
750
+ deckTheme = data2.theme;
751
+ }
752
+ } catch {
753
+ }
380
754
  const contentIndex = createAppDeckFile({
381
755
  slidePath: `${directory}/deck.mdx`,
382
756
  theme: options.theme,
757
+ deckTheme,
383
758
  config
384
759
  });
385
- console.log({ contentIndex });
386
760
  return contentIndex;
387
761
  }
388
762
  if (!id.endsWith("deck.mdx")) {
@@ -393,38 +767,82 @@ var src_default = async (options) => {
393
767
  production: isProd,
394
768
  ...options
395
769
  });
396
- const dir = path.relative(process.cwd(), id);
397
770
  return data;
398
771
  },
399
772
  transformIndexHtml: {
400
773
  order: "pre",
401
- enforce: "pre",
402
- transform: async (html, ctx) => {
774
+ handler: async (html, ctx) => {
403
775
  var _a;
404
776
  const originalUrl = ((_a = ctx.originalUrl) == null ? void 0 : _a.split("?")[0]) || "";
777
+ if (originalUrl === "/" || originalUrl === "") {
778
+ const shouldShow = showStartupPage !== void 0 ? showStartupPage : !isProd;
779
+ if (shouldShow) {
780
+ return html.replace("__SCRIPT__", `__decks.tsx`);
781
+ }
782
+ }
405
783
  const deckDir = ctx.path.replace("/index.html", "");
406
784
  const dir = originalUrl ? `./src${originalUrl}` : `.${deckDir}`;
407
- const path2 = `${dir}/deck.mdx`;
408
- if (!await checkIfDirectoryExists(path2)) {
409
- return html.replace("__SCRIPT__", `./src/main.tsx`);
785
+ const deckPath = `${dir}/deck.mdx`;
786
+ if (await checkIfDirectoryExists(deckPath)) {
787
+ const resolvedDeckPath = dir.startsWith(".") ? dir : `.${dir}`;
788
+ return html.replace("__SCRIPT__", `${resolvedDeckPath}/__deck.tsx`);
410
789
  }
411
- const deckPath = dir.startsWith(".") ? dir : `.${dir}`;
412
- return html.replace("__SCRIPT__", `${deckPath}/__deck.tsx`);
790
+ const decks = await glob.glob("./src/**/deck.mdx");
791
+ const deckLinks = decks.map((d) => {
792
+ const url = `/${d.replace("src/", "").replace("/deck.mdx", "")}/`;
793
+ const name = d.replace("src/", "").replace("/deck.mdx", "");
794
+ return `<li><a href="${url}">${name}</a></li>`;
795
+ }).join("\n");
796
+ return html.replace('<script type="module" src="__SCRIPT__"></script>', "").replace(
797
+ '<div id="root"></div>',
798
+ `<div id="root">
799
+ <h1>Available Decks</h1>
800
+ <ul>${deckLinks}</ul>
801
+ </div>`
802
+ );
413
803
  }
414
804
  },
415
805
  configureServer(server) {
416
806
  var _a;
807
+ const shouldShow = showStartupPage !== void 0 ? showStartupPage : true;
808
+ if (shouldShow) {
809
+ server.middlewares.use(async (req, res, next) => {
810
+ var _a2;
811
+ const url = ((_a2 = req.url) == null ? void 0 : _a2.split("?")[0]) || "";
812
+ if (url === "/" || url === "/index.html") {
813
+ const html = createDecksIndexFile();
814
+ const transformed = await server.transformIndexHtml(
815
+ url,
816
+ html,
817
+ req.originalUrl
818
+ );
819
+ res.setHeader("Content-Type", "text/html");
820
+ res.statusCode = 200;
821
+ res.end(transformed);
822
+ return;
823
+ }
824
+ next();
825
+ });
826
+ }
417
827
  (_a = server.httpServer) == null ? void 0 : _a.once("listening", async () => {
418
828
  const port = server.config.server.port || 5173;
419
- const decks = await findDecks({ port });
420
- for (const deck of decks) {
421
- console.log(`Deck available at ${deck.deckUrl}`);
829
+ if (shouldShow) {
830
+ server.config.logger.info(
831
+ `
832
+ Decks available at http://localhost:${port}/
833
+ `
834
+ );
835
+ } else {
836
+ const decks = await findDecks({ port });
837
+ for (const deck of decks) {
838
+ server.config.logger.info(`Deck available at ${deck.deckUrl}`);
839
+ }
422
840
  }
423
841
  });
424
842
  }
425
843
  };
426
844
  };
427
845
  export {
428
- src_default as default,
846
+ index_default as default,
429
847
  defineConfig
430
848
  };