vite-plugin-react-deck 1.1.4 → 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,105 +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="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css">
225
- <link rel="icon" type="image/x-icon" href="favicon.ico" />
226
- </head>
227
- <body>
228
- <div id="root"></div>
229
- <script type="module" src="${entrypoint}"></script>
230
- </body>
231
- </html>
232
- `;
233
- }
234
- function createAppDeckFile({
235
- slidePath,
236
- theme,
237
- config
238
- }) {
239
- if (!themes_exports[theme]) {
240
- throw new Error(`Theme ${theme} not found`);
241
- }
242
- const themeObject = themes_exports[theme];
243
- const layoutImport = config.layoutsFile ? `import layouts from "${config.layoutsFile}";` : "import { layouts } from '@gpichot/spectacle-deck';";
244
- return `import React, { StrictMode } from "react";
245
- import * as ReactDOM from "react-dom/client";
246
- import { Deck } from '@gpichot/spectacle-deck';
247
- ${layoutImport};
248
-
249
- import deck from "${slidePath}";
250
-
251
- const root = ReactDOM.createRoot(
252
- document.getElementById("root") as HTMLElement
253
- );
254
- root.render(
255
- <StrictMode>
256
- <Deck deck={deck} theme={${JSON.stringify(themeObject)}} layouts={layouts} />
257
- </StrictMode>
258
- )`;
259
- }
260
-
261
- // src/index.ts
262
- import * as glob from "glob";
263
- import path from "path";
264
-
265
574
  // src/config.ts
266
575
  import _ from "lodash";
267
576
 
268
577
  // src/types.ts
269
578
  import { z } from "zod";
270
579
  var PestacleConfigSchema = z.object({
271
- theme: z.enum(["green", "purple"]).default("green")
580
+ theme: z.enum(["green", "purple", "solarized-light"]).default("green"),
581
+ startupPage: z.boolean().optional()
272
582
  });
273
583
 
274
584
  // src/config.ts
@@ -277,9 +587,9 @@ function defineConfig(config) {
277
587
  }
278
588
 
279
589
  // src/index.ts
280
- async function checkIfDirectoryExists(path2) {
590
+ async function checkIfDirectoryExists(path) {
281
591
  try {
282
- await fs2.access(path2);
592
+ await fs2.access(path);
283
593
  return true;
284
594
  } catch {
285
595
  return false;
@@ -292,15 +602,45 @@ async function findDecks({ port }) {
292
602
  deckUrl: `http://localhost:${port}/${deck.replace("src/", "").replace("deck.mdx", "")}`
293
603
  }));
294
604
  }
295
- 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) {
296
637
  const candidateExts = [".ts", ".tsx", ".js", ".jsx"];
297
638
  for await (const ext of candidateExts) {
298
- const fullPath = `${path2}${ext}`;
639
+ const fullPath = `${path}${ext}`;
299
640
  try {
300
641
  await fs2.access(fullPath);
301
642
  return fullPath.replace(/^\./, "");
302
643
  } catch {
303
- continue;
304
644
  }
305
645
  }
306
646
  }
@@ -310,32 +650,39 @@ async function loadCustomConfig() {
310
650
  layoutsFile
311
651
  };
312
652
  }
313
- var src_default = async (options) => {
653
+ var index_default = async (options) => {
314
654
  let isProd = false;
655
+ const showStartupPage = options.startupPage;
315
656
  const deckConfig = {
316
657
  decks: []
317
658
  };
318
659
  return {
319
660
  name: "react-deck",
320
- async config(config) {
321
- var _a, _b, _c;
661
+ async config(config, env) {
662
+ var _a, _b;
322
663
  const decks = await glob.glob("./src/**/deck.mdx");
323
- const inputs = ((_b = (_a = config.build) == null ? void 0 : _a.rollupOptions) == null ? void 0 : _b.input) || {};
324
- deckConfig.decks = decks.map((deck) => ({
325
- originalFile: deck,
326
- index: deck.replace("/deck.mdx", "/index.html").replace("src/", "")
327
- }));
328
- 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) => {
329
676
  const deckPath = deck.replace("/deck.mdx", "");
330
- return [...acc, `${deckPath.replace("src/", "")}/index.html`];
331
- }, []);
677
+ return `${deckPath.replace("src/", "")}/index.html`;
678
+ });
679
+ if (startupPageEnabled) {
680
+ newInputs.unshift("index.html");
681
+ }
332
682
  const finalInputs = typeof inputs === "string" ? [inputs, ...decks] : Array.isArray(inputs) ? [...inputs, ...decks] : { ...inputs, ...newInputs };
333
683
  return {
334
- ...config,
335
684
  build: {
336
- ...config.build,
337
- rollupOptions: {
338
- ...(_c = config.build) == null ? void 0 : _c.rollupOptions,
685
+ rolldownOptions: {
339
686
  input: finalInputs
340
687
  }
341
688
  }
@@ -345,6 +692,9 @@ var src_default = async (options) => {
345
692
  isProd = config.isProduction;
346
693
  },
347
694
  resolveId(id) {
695
+ if (id === "index.html" || id === "__decks.tsx" || id === "/__decks.tsx") {
696
+ return id.replace(/^\//, "");
697
+ }
348
698
  if (deckConfig.decks.some((deck) => deck.index === id)) {
349
699
  return id;
350
700
  }
@@ -356,6 +706,27 @@ var src_default = async (options) => {
356
706
  }
357
707
  },
358
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
+ }
359
730
  const config = await loadCustomConfig();
360
731
  const deck = deckConfig.decks.find((deck2) => deck2.index === id);
361
732
  if (deck) {
@@ -365,18 +736,27 @@ var src_default = async (options) => {
365
736
  }
366
737
  if (id.endsWith("__deck.tsx")) {
367
738
  const directory = id.replace("/__deck.tsx", "");
368
- const dir2 = directory.startsWith(".") ? directory : `./${directory}`;
369
- const path2 = `${dir2}/deck.mdx`;
370
- if (!await checkIfDirectoryExists(path2)) {
371
- 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}`);
372
743
  return;
373
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
+ }
374
754
  const contentIndex = createAppDeckFile({
375
755
  slidePath: `${directory}/deck.mdx`,
376
756
  theme: options.theme,
757
+ deckTheme,
377
758
  config
378
759
  });
379
- console.log({ contentIndex });
380
760
  return contentIndex;
381
761
  }
382
762
  if (!id.endsWith("deck.mdx")) {
@@ -387,38 +767,82 @@ var src_default = async (options) => {
387
767
  production: isProd,
388
768
  ...options
389
769
  });
390
- const dir = path.relative(process.cwd(), id);
391
770
  return data;
392
771
  },
393
772
  transformIndexHtml: {
394
773
  order: "pre",
395
- enforce: "pre",
396
- transform: async (html, ctx) => {
774
+ handler: async (html, ctx) => {
397
775
  var _a;
398
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
+ }
399
783
  const deckDir = ctx.path.replace("/index.html", "");
400
784
  const dir = originalUrl ? `./src${originalUrl}` : `.${deckDir}`;
401
- const path2 = `${dir}/deck.mdx`;
402
- if (!await checkIfDirectoryExists(path2)) {
403
- 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`);
404
789
  }
405
- const deckPath = dir.startsWith(".") ? dir : `.${dir}`;
406
- 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
+ );
407
803
  }
408
804
  },
409
805
  configureServer(server) {
410
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
+ }
411
827
  (_a = server.httpServer) == null ? void 0 : _a.once("listening", async () => {
412
828
  const port = server.config.server.port || 5173;
413
- const decks = await findDecks({ port });
414
- for (const deck of decks) {
415
- 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
+ }
416
840
  }
417
841
  });
418
842
  }
419
843
  };
420
844
  };
421
845
  export {
422
- src_default as default,
846
+ index_default as default,
423
847
  defineConfig
424
848
  };