supaslidev 0.4.1 → 0.4.2

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/dist/cli/index.js CHANGED
@@ -206,11 +206,13 @@ function regeneratePresentationsJson(presentationsDir, presentationsJsonPath, op
206
206
  return isDir && hasSlides;
207
207
  }).map((name) => {
208
208
  const frontmatter = parseFrontmatter(readFileSync(join(presentationsDir, name, "slides.md"), "utf-8"));
209
+ const colorSchema = frontmatter.colorSchema;
209
210
  const presentation = {
210
211
  id: name,
211
212
  title: frontmatter.title || name,
212
213
  description: extractDescription(frontmatter.info) || "",
213
214
  theme: frontmatter.theme || "default",
215
+ colorSchema: colorSchema || "",
214
216
  background: frontmatter.background || "",
215
217
  duration: frontmatter.duration || ""
216
218
  };
@@ -186676,7 +186678,7 @@ var require_ts_morph = /* @__PURE__ */ __commonJSMin(((exports) => {
186676
186678
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
186677
186679
  require_dist$2();
186678
186680
  require_ts_morph();
186679
- const CLI_VERSION = "0.4.1";
186681
+ const CLI_VERSION = "0.4.2";
186680
186682
  const PACKAGE_NAME = "@supaslidev/cli";
186681
186683
  const CACHE_DIR = join(tmpdir(), "supaslidev-cli");
186682
186684
  const CACHE_FILE = join(CACHE_DIR, "version-cache.json");
@@ -188029,11 +188031,11 @@ function findNuxtBin(projectRoot, supaslidevRoot) {
188029
188031
  }
188030
188032
  function createVercelConfig(basePath, presentations) {
188031
188033
  const rewrites = presentations.map((id) => ({
188032
- source: `${basePath}presentations/${id}/(.*)`,
188034
+ source: `${basePath}presentations/${id}/:path((?!.*\\.).*)`,
188033
188035
  destination: `${basePath}presentations/${id}/index.html`
188034
188036
  }));
188035
188037
  rewrites.push({
188036
- source: `${basePath}(.*)`,
188038
+ source: `${basePath}:path((?!.*\\.).*)`,
188037
188039
  destination: `${basePath}index.html`
188038
188040
  });
188039
188041
  return JSON.stringify({ rewrites }, null, 2);
@@ -188067,7 +188069,7 @@ async function deploy(options = {}) {
188067
188069
  const presentationsDir = join(projectRoot, "presentations");
188068
188070
  const presentations = getPresentations$1(presentationsDir);
188069
188071
  if (presentations.length === 0) throw new Error("No presentations found in the presentations directory.");
188070
- const outputDir = resolve(options.output ?? join(projectRoot, "deploy"));
188072
+ const outputDir = resolve(projectRoot, options.output ?? "deploy");
188071
188073
  const resolvedProjectRoot = resolve(projectRoot);
188072
188074
  if (!outputDir.startsWith(resolvedProjectRoot)) throw new Error(`Output directory "${outputDir}" is outside the project root "${resolvedProjectRoot}". Use a path within your project.`);
188073
188075
  const basePath = (options.base ?? "/").replace(/\/*$/, "/");
@@ -188103,17 +188105,21 @@ async function deploy(options = {}) {
188103
188105
  const slidevBin = join(presentationDir, "node_modules", ".bin", "slidev");
188104
188106
  const outputBase = join(thumbnailsDir, id);
188105
188107
  const targetFile = join(thumbnailsDir, `${id}.png`);
188106
- console.log(` Thumbnail: ${id}`);
188108
+ const slidesPath = join(presentationDir, "slides.md");
188109
+ const useDark = (existsSync(slidesPath) ? parseFrontmatter(readFileSync(slidesPath, "utf-8")) : {}).colorSchema === "dark";
188110
+ console.log(` Thumbnail: ${id}${useDark ? " (dark mode)" : ""}`);
188111
+ const exportArgs = [
188112
+ "export",
188113
+ "--format",
188114
+ "png",
188115
+ "--range",
188116
+ "1",
188117
+ "--output",
188118
+ outputBase
188119
+ ];
188120
+ if (useDark) exportArgs.push("--dark");
188107
188121
  try {
188108
- await runCommand(slidevBin, [
188109
- "export",
188110
- "--format",
188111
- "png",
188112
- "--range",
188113
- "1",
188114
- "--output",
188115
- outputBase
188116
- ], { cwd: presentationDir });
188122
+ await runCommand(slidevBin, exportArgs, { cwd: presentationDir });
188117
188123
  if (!existsSync(targetFile) && existsSync(outputBase)) {
188118
188124
  const pngs = readdirSync(outputBase).filter((f) => f.endsWith(".png")).sort();
188119
188125
  if (pngs.length > 0) renameSync(join(outputBase, pngs[0]), targetFile);
@@ -188131,6 +188137,7 @@ async function deploy(options = {}) {
188131
188137
  const nuxtEnv = {
188132
188138
  ...process.env,
188133
188139
  NODE_ENV: "production",
188140
+ NITRO_PRESET: process.env.NITRO_PRESET ?? "static",
188134
188141
  SUPASLIDEV_PROJECT_ROOT: projectRoot,
188135
188142
  SUPASLIDEV_PRESENTATIONS_DIR: presentationsDir,
188136
188143
  NUXT_PUBLIC_DEPLOY_MODE: "true"
@@ -188173,6 +188180,17 @@ async function deploy(options = {}) {
188173
188180
  writeFileSync(join(outputDir, "vercel.json"), createVercelConfig(basePath, presentations));
188174
188181
  writeFileSync(join(outputDir, "netlify.toml"), createNetlifyConfig(basePath, presentations));
188175
188182
  writeFileSync(join(outputDir, "package.json"), createDeployPackageJson());
188183
+ const rootVercelJson = join(projectRoot, "vercel.json");
188184
+ if (!existsSync(rootVercelJson)) {
188185
+ const vercelConfig = {
188186
+ framework: null,
188187
+ installCommand: `${existsSync(join(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : existsSync(join(projectRoot, "yarn.lock")) ? "yarn" : "npm"} install`,
188188
+ buildCommand: "npx supaslidev deploy --output dist",
188189
+ outputDirectory: "dist"
188190
+ };
188191
+ writeFileSync(rootVercelJson, JSON.stringify(vercelConfig, null, 2) + "\n");
188192
+ console.log(" Generated vercel.json in project root for Vercel deployment.\n");
188193
+ }
188176
188194
  console.log("=".repeat(50));
188177
188195
  console.log(" Deploy package ready!");
188178
188196
  console.log("=".repeat(50));
@@ -188210,16 +188228,23 @@ async function thumbnail(name, options = {}) {
188210
188228
  console.log(` Generating thumbnail: ${name}`);
188211
188229
  console.log("=".repeat(50) + "\n");
188212
188230
  const slidevBin = join(presentationDir, "node_modules", ".bin", "slidev");
188231
+ let useDark = options.dark ?? false;
188232
+ if (options.dark === void 0) {
188233
+ const slidesPath = join(presentationDir, "slides.md");
188234
+ if (existsSync(slidesPath)) useDark = parseFrontmatter(readFileSync(slidesPath, "utf-8")).colorSchema === "dark";
188235
+ }
188236
+ const exportArgs = [
188237
+ "export",
188238
+ "--format",
188239
+ "png",
188240
+ "--range",
188241
+ "1",
188242
+ "--output",
188243
+ outputPath
188244
+ ];
188245
+ if (useDark) exportArgs.push("--dark");
188213
188246
  await new Promise((resolve) => {
188214
- const slidev = spawn(slidevBin, [
188215
- "export",
188216
- "--format",
188217
- "png",
188218
- "--range",
188219
- "1",
188220
- "--output",
188221
- outputPath
188222
- ], {
188247
+ const slidev = spawn(slidevBin, exportArgs, {
188223
188248
  cwd: presentationDir,
188224
188249
  stdio: "inherit"
188225
188250
  });
@@ -188271,7 +188296,7 @@ program.command("import").description("Import existing Sli.dev presentation(s)")
188271
188296
  install: options.install ?? true
188272
188297
  });
188273
188298
  });
188274
- program.command("thumbnail").description("Generate a PNG thumbnail of the first slide").argument("<name>", "Name of the presentation").option("-o, --output <path>", "Output path for the thumbnail (without extension)").action(async (name, options) => {
188299
+ program.command("thumbnail").description("Generate a PNG thumbnail of the first slide").argument("<name>", "Name of the presentation").option("-o, --output <path>", "Output path for the thumbnail (without extension)").option("--dark", "Export as dark theme (auto-detected from colorSchema if not set)").action(async (name, options) => {
188275
188300
  await thumbnail(name, options);
188276
188301
  });
188277
188302
  program.command("deploy").description("Build all presentations into a static deployable site").option("-o, --output <dir>", "Output directory for the deploy package").option("--base <path>", "Base path for the deployed site (default: /)").action(async (options) => {
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ interface Presentation {
6
6
  title: string;
7
7
  description: string;
8
8
  theme: string;
9
+ colorSchema: 'dark' | 'light' | 'both' | '';
9
10
  background: string;
10
11
  duration: string;
11
12
  thumbnail?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supaslidev",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "CLI toolkit for managing Supaslidev presentations",
5
5
  "keywords": [
6
6
  "slidev",
@@ -65,7 +65,7 @@
65
65
  "typescript": "^6.0.3",
66
66
  "vitest": "^4.1.5",
67
67
  "vue-tsc": "^3.2.7",
68
- "create-supaslidev": "^0.4.1"
68
+ "create-supaslidev": "^0.4.2"
69
69
  },
70
70
  "scripts": {
71
71
  "dev": "nuxt dev",
@@ -12,7 +12,7 @@ import {
12
12
  } from 'node:fs';
13
13
  import { fileURLToPath } from 'node:url';
14
14
  import { findProjectRoot, getPresentations } from '../utils.js';
15
- import { regeneratePresentationsJson } from '../../shared/presentations.js';
15
+ import { parseFrontmatter, regeneratePresentationsJson } from '../../shared/presentations.js';
16
16
  import { optimizeThumbnail } from '../../shared/optimize-thumbnail.js';
17
17
 
18
18
  export interface DeployOptions {
@@ -84,13 +84,15 @@ function findNuxtBin(
84
84
  }
85
85
 
86
86
  export function createVercelConfig(basePath: string, presentations: string[]): string {
87
+ // Use negative lookahead to exclude paths with a dot (file extensions like .js, .css, .png)
88
+ // so that asset requests are served as static files instead of being rewritten to index.html
87
89
  const rewrites = presentations.map((id) => ({
88
- source: `${basePath}presentations/${id}/(.*)`,
90
+ source: `${basePath}presentations/${id}/:path((?!.*\\.).*)`,
89
91
  destination: `${basePath}presentations/${id}/index.html`,
90
92
  }));
91
93
 
92
94
  rewrites.push({
93
- source: `${basePath}(.*)`,
95
+ source: `${basePath}:path((?!.*\\.).*)`,
94
96
  destination: `${basePath}index.html`,
95
97
  });
96
98
 
@@ -147,7 +149,7 @@ export async function deploy(options: DeployOptions = {}): Promise<void> {
147
149
  throw new Error('No presentations found in the presentations directory.');
148
150
  }
149
151
 
150
- const outputDir = resolve(options.output ?? join(projectRoot, 'deploy'));
152
+ const outputDir = resolve(projectRoot, options.output ?? 'deploy');
151
153
  const resolvedProjectRoot = resolve(projectRoot);
152
154
 
153
155
  // Safety check: prevent deleting directories outside the project root
@@ -202,16 +204,24 @@ export async function deploy(options: DeployOptions = {}): Promise<void> {
202
204
  const outputBase = join(thumbnailsDir, id);
203
205
  const targetFile = join(thumbnailsDir, `${id}.png`);
204
206
 
205
- console.log(` Thumbnail: ${id}`);
207
+ // Detect dark mode from frontmatter
208
+ const slidesPath = join(presentationDir, 'slides.md');
209
+ const frontmatter = existsSync(slidesPath)
210
+ ? parseFrontmatter(readFileSync(slidesPath, 'utf-8'))
211
+ : {};
212
+ const useDark = frontmatter.colorSchema === 'dark';
213
+
214
+ console.log(` Thumbnail: ${id}${useDark ? ' (dark mode)' : ''}`);
215
+
216
+ const exportArgs = ['export', '--format', 'png', '--range', '1', '--output', outputBase];
217
+ if (useDark) {
218
+ exportArgs.push('--dark');
219
+ }
206
220
 
207
221
  try {
208
- await runCommand(
209
- slidevBin,
210
- ['export', '--format', 'png', '--range', '1', '--output', outputBase],
211
- {
212
- cwd: presentationDir,
213
- },
214
- );
222
+ await runCommand(slidevBin, exportArgs, {
223
+ cwd: presentationDir,
224
+ });
215
225
 
216
226
  // Slidev exports into a directory <output>/<n>.png — move to <output>.png
217
227
  if (!existsSync(targetFile) && existsSync(outputBase)) {
@@ -244,6 +254,7 @@ export async function deploy(options: DeployOptions = {}): Promise<void> {
244
254
  const nuxtEnv: Record<string, string | undefined> = {
245
255
  ...process.env,
246
256
  NODE_ENV: 'production',
257
+ NITRO_PRESET: process.env.NITRO_PRESET ?? 'static',
247
258
  SUPASLIDEV_PROJECT_ROOT: projectRoot,
248
259
  SUPASLIDEV_PRESENTATIONS_DIR: presentationsDir,
249
260
  NUXT_PUBLIC_DEPLOY_MODE: 'true',
@@ -327,6 +338,24 @@ export async function deploy(options: DeployOptions = {}): Promise<void> {
327
338
  writeFileSync(join(outputDir, 'netlify.toml'), createNetlifyConfig(basePath, presentations));
328
339
  writeFileSync(join(outputDir, 'package.json'), createDeployPackageJson());
329
340
 
341
+ // Generate root vercel.json for build-from-repo workflow if it doesn't exist
342
+ const rootVercelJson = join(projectRoot, 'vercel.json');
343
+ if (!existsSync(rootVercelJson)) {
344
+ const pm = existsSync(join(projectRoot, 'pnpm-lock.yaml'))
345
+ ? 'pnpm'
346
+ : existsSync(join(projectRoot, 'yarn.lock'))
347
+ ? 'yarn'
348
+ : 'npm';
349
+ const vercelConfig = {
350
+ framework: null,
351
+ installCommand: `${pm} install`,
352
+ buildCommand: 'npx supaslidev deploy --output dist',
353
+ outputDirectory: 'dist',
354
+ };
355
+ writeFileSync(rootVercelJson, JSON.stringify(vercelConfig, null, 2) + '\n');
356
+ console.log(' Generated vercel.json in project root for Vercel deployment.\n');
357
+ }
358
+
330
359
  console.log('='.repeat(50));
331
360
  console.log(' Deploy package ready!');
332
361
  console.log('='.repeat(50));
@@ -1,11 +1,13 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { dirname, join } from 'node:path';
3
- import { existsSync, mkdirSync, readdirSync, renameSync } from 'node:fs';
3
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync } from 'node:fs';
4
4
  import { findProjectRoot, getPresentations, printAvailablePresentations } from '../utils.js';
5
5
  import { optimizeThumbnail } from '../../shared/optimize-thumbnail.js';
6
+ import { parseFrontmatter } from '../../shared/presentations.js';
6
7
 
7
8
  export interface ThumbnailOptions {
8
9
  output?: string;
10
+ dark?: boolean;
9
11
  }
10
12
 
11
13
  export async function thumbnail(name: string, options: ThumbnailOptions = {}): Promise<void> {
@@ -40,15 +42,26 @@ export async function thumbnail(name: string, options: ThumbnailOptions = {}): P
40
42
 
41
43
  const slidevBin = join(presentationDir, 'node_modules', '.bin', 'slidev');
42
44
 
45
+ // Auto-detect dark mode from frontmatter unless explicitly set
46
+ let useDark = options.dark ?? false;
47
+ if (options.dark === undefined) {
48
+ const slidesPath = join(presentationDir, 'slides.md');
49
+ if (existsSync(slidesPath)) {
50
+ const frontmatter = parseFrontmatter(readFileSync(slidesPath, 'utf-8'));
51
+ useDark = frontmatter.colorSchema === 'dark';
52
+ }
53
+ }
54
+
55
+ const exportArgs = ['export', '--format', 'png', '--range', '1', '--output', outputPath];
56
+ if (useDark) {
57
+ exportArgs.push('--dark');
58
+ }
59
+
43
60
  await new Promise<void>((resolve) => {
44
- const slidev = spawn(
45
- slidevBin,
46
- ['export', '--format', 'png', '--range', '1', '--output', outputPath],
47
- {
48
- cwd: presentationDir,
49
- stdio: 'inherit',
50
- },
51
- );
61
+ const slidev = spawn(slidevBin, exportArgs, {
62
+ cwd: presentationDir,
63
+ stdio: 'inherit',
64
+ });
52
65
 
53
66
  slidev.on('error', (err) => {
54
67
  console.error(`Failed to generate thumbnail: ${err.message}`);
package/src/cli/index.ts CHANGED
@@ -63,7 +63,8 @@ program
63
63
  .description('Generate a PNG thumbnail of the first slide')
64
64
  .argument('<name>', 'Name of the presentation')
65
65
  .option('-o, --output <path>', 'Output path for the thumbnail (without extension)')
66
- .action(async (name: string, options: { output?: string }) => {
66
+ .option('--dark', 'Export as dark theme (auto-detected from colorSchema if not set)')
67
+ .action(async (name: string, options: { output?: string; dark?: boolean }) => {
67
68
  await thumbnail(name, options);
68
69
  });
69
70
 
@@ -93,11 +93,14 @@ export function regeneratePresentationsJson(
93
93
  const content = readFileSync(slidesPath, 'utf-8');
94
94
  const frontmatter = parseFrontmatter(content);
95
95
 
96
+ const colorSchema = frontmatter.colorSchema as Presentation['colorSchema'];
97
+
96
98
  const presentation: Presentation = {
97
99
  id: name,
98
100
  title: frontmatter.title || name,
99
101
  description: extractDescription(frontmatter.info) || '',
100
102
  theme: frontmatter.theme || 'default',
103
+ colorSchema: colorSchema || '',
101
104
  background: frontmatter.background || '',
102
105
  duration: frontmatter.duration || '',
103
106
  };
@@ -3,6 +3,7 @@ export interface Presentation {
3
3
  title: string;
4
4
  description: string;
5
5
  theme: string;
6
+ colorSchema: 'dark' | 'light' | 'both' | '';
6
7
  background: string;
7
8
  duration: string;
8
9
  thumbnail?: string;