create-deckio 1.0.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.
Files changed (2) hide show
  1. package/index.mjs +477 -0
  2. package/package.json +28 -0
package/index.mjs ADDED
@@ -0,0 +1,477 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-deck-project — scaffold a new presentation project.
4
+ *
5
+ * Usage:
6
+ * npm create @deckio/deck-project my-talk
7
+ * npx @deckio/create-deck-project my-talk
8
+ */
9
+ import { mkdirSync, writeFileSync } from 'fs'
10
+ import { join, resolve } from 'path'
11
+ import { execSync } from 'child_process'
12
+ import { createInterface } from 'readline'
13
+
14
+ // ── Helpers ──
15
+
16
+ function ask(rl, question, fallback) {
17
+ return new Promise((res) => {
18
+ rl.question(`${question} ${fallback ? `(${fallback}) ` : ''}`, (answer) => {
19
+ res(answer.trim() || fallback || '')
20
+ })
21
+ })
22
+ }
23
+
24
+ function slugify(text) {
25
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
26
+ }
27
+
28
+ function write(dir, relPath, content) {
29
+ const full = join(dir, relPath)
30
+ mkdirSync(join(full, '..'), { recursive: true })
31
+ writeFileSync(full, content)
32
+ }
33
+
34
+ // ── Templates ──
35
+
36
+ function packageJson(name) {
37
+ return JSON.stringify({
38
+ name: `deck-project-${name}`,
39
+ version: '0.1.0',
40
+ private: true,
41
+ type: 'module',
42
+ scripts: {
43
+ dev: 'vite',
44
+ build: 'vite build',
45
+ preview: 'vite preview',
46
+ },
47
+ dependencies: {
48
+ '@deckio/deck-engine': '^1.7.5',
49
+ react: '^19.1.0',
50
+ 'react-dom': '^19.1.0',
51
+ },
52
+ devDependencies: {
53
+ '@vitejs/plugin-react': '^4.4.1',
54
+ vite: '^6.3.5',
55
+ },
56
+ }, null, 2) + '\n'
57
+ }
58
+
59
+ const VITE_CONFIG = `\
60
+ import { defineConfig } from 'vite'
61
+ import react from '@vitejs/plugin-react'
62
+ import { deckPlugin } from '@deckio/deck-engine/vite'
63
+
64
+ export default defineConfig({
65
+ plugins: [
66
+ react({
67
+ include: [/\\.[jt]sx?$/, /node_modules\\/@deckio\\/deck-engine\\/.+\\.jsx$/],
68
+ }),
69
+ deckPlugin(),
70
+ ],
71
+ })
72
+ `
73
+
74
+ const INDEX_HTML = `\
75
+ <!doctype html>
76
+ <html lang="en">
77
+ <head>
78
+ <meta charset="UTF-8" />
79
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
80
+ <title>Deck</title>
81
+ </head>
82
+ <body>
83
+ <div id="root"></div>
84
+ <script type="module" src="/src/main.jsx"></script>
85
+ </body>
86
+ </html>
87
+ `
88
+
89
+ const MAIN_JSX = `\
90
+ import { StrictMode } from 'react'
91
+ import { createRoot } from 'react-dom/client'
92
+ import '@deckio/deck-engine/styles/global.css'
93
+ import App from './App.jsx'
94
+
95
+ createRoot(document.getElementById('root')).render(
96
+ <StrictMode>
97
+ <App />
98
+ </StrictMode>,
99
+ )
100
+ `
101
+
102
+ const APP_JSX = `\
103
+ import { useEffect } from 'react'
104
+ import { Navigation, SlideProvider } from '@deckio/deck-engine'
105
+ import project from '../deck.config.js'
106
+
107
+ export default function App() {
108
+ const { accent, id, slides, subtitle, title } = project
109
+
110
+ useEffect(() => {
111
+ document.documentElement.style.setProperty('--accent', accent)
112
+ document.title = title
113
+ }, [accent, title])
114
+
115
+ return (
116
+ <SlideProvider totalSlides={slides.length} project={id} slides={slides}>
117
+ <Navigation />
118
+ <div className="deck" data-project-id={id}>
119
+ {slides.map((SlideComponent, index) => (
120
+ <SlideComponent key={\`\${id}-slide-\${index}\`} index={index} project={project} />
121
+ ))}
122
+ </div>
123
+ </SlideProvider>
124
+ )
125
+ }
126
+ `
127
+
128
+ function deckConfig(slug, title, subtitle, icon, accent) {
129
+ const esc = (s) => s.replace(/'/g, "\\'")
130
+ return `\
131
+ import CoverSlide from './src/slides/CoverSlide.jsx'
132
+ import { GenericThankYouSlide as ThankYouSlide } from '@deckio/deck-engine'
133
+
134
+ export default {
135
+ id: '${esc(slug)}',
136
+ title: '${esc(title)}',
137
+ subtitle: '${esc(subtitle)}',
138
+ description: '${esc(subtitle)}',
139
+ icon: '${esc(icon)}',
140
+ accent: '${esc(accent)}',
141
+ order: 1,
142
+ slides: [
143
+ CoverSlide,
144
+ ThankYouSlide,
145
+ ],
146
+ }
147
+ `
148
+ }
149
+
150
+ function coverSlideJsx(title, subtitle, slug) {
151
+ const highlight = title.split(' ').pop()
152
+ const before = title.split(' ').slice(0, -1).join(' ')
153
+ return `\
154
+ import { BottomBar, Slide } from '@deckio/deck-engine'
155
+ import styles from './CoverSlide.module.css'
156
+
157
+ export default function CoverSlide() {
158
+ return (
159
+ <Slide index={0} className={styles.cover}>
160
+ <div className="accent-bar" />
161
+ <div className={\`orb \${styles.orb1}\`} />
162
+ <div className={\`orb \${styles.orb2}\`} />
163
+ <div className={\`orb \${styles.orb3}\`} />
164
+
165
+ <div className="content-frame content-gutter">
166
+ <div className={styles.content}>
167
+ <p className={styles.eyebrow}>${slug}</p>
168
+ <h1>
169
+ ${before ? `${before} ` : ''}<span className={styles.highlight}>${highlight}</span>
170
+ </h1>
171
+ <p className={styles.subtitle}>
172
+ ${subtitle}
173
+ </p>
174
+
175
+ <div className={styles.meta}>
176
+ <div className={styles.metaItem}>
177
+ <span className={styles.metaLabel}>Project</span>
178
+ <span className={styles.metaValue}>${title}</span>
179
+ </div>
180
+ <div className={styles.metaDivider} />
181
+ <div className={styles.metaItem}>
182
+ <span className={styles.metaLabel}>Date</span>
183
+ <span className={styles.metaValue}>${new Date().getFullYear()}</span>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ </div>
188
+
189
+ <BottomBar text="${slug}" />
190
+ </Slide>
191
+ )
192
+ }
193
+ `
194
+ }
195
+
196
+ const COVER_SLIDE_CSS = `\
197
+ .cover {
198
+ background: var(--bg-deep);
199
+ flex-direction: column;
200
+ justify-content: center;
201
+ padding: 0 0 44px 0;
202
+ }
203
+
204
+ /* ── Orbs ── */
205
+ .orb1 {
206
+ width: 520px; height: 520px;
207
+ top: -120px; right: -80px;
208
+ background: radial-gradient(circle at 40% 40%, var(--accent), rgba(63,185,80,0.3) 50%, transparent 70%);
209
+ }
210
+ .orb2 {
211
+ width: 380px; height: 380px;
212
+ bottom: -80px; right: 140px;
213
+ background: radial-gradient(circle at 50% 50%, var(--purple-deep), rgba(110,64,201,0.3) 60%, transparent 75%);
214
+ }
215
+ .orb3 {
216
+ width: 260px; height: 260px;
217
+ top: 60px; right: 280px;
218
+ background: radial-gradient(circle at 50% 50%, var(--cyan), rgba(86,212,221,0.15) 60%, transparent 75%);
219
+ }
220
+
221
+ /* ── Content ── */
222
+ .content {
223
+ position: relative;
224
+ z-index: 10;
225
+ max-width: 700px;
226
+ }
227
+
228
+ .eyebrow {
229
+ font-size: 13px;
230
+ font-weight: 700;
231
+ text-transform: uppercase;
232
+ letter-spacing: 2.5px;
233
+ color: var(--accent);
234
+ margin-bottom: 16px;
235
+ }
236
+
237
+ .content h1 {
238
+ font-size: clamp(48px, 5.5vw, 80px);
239
+ font-weight: 900;
240
+ line-height: 1.05;
241
+ letter-spacing: -2.5px;
242
+ margin-bottom: 24px;
243
+ }
244
+
245
+ .highlight {
246
+ background: linear-gradient(135deg, var(--accent), var(--cyan));
247
+ -webkit-background-clip: text;
248
+ -webkit-text-fill-color: transparent;
249
+ background-clip: text;
250
+ }
251
+
252
+ .subtitle {
253
+ font-size: clamp(16px, 1.8vw, 20px);
254
+ font-weight: 300;
255
+ color: var(--text-muted);
256
+ line-height: 1.65;
257
+ margin-bottom: 48px;
258
+ max-width: 580px;
259
+ }
260
+
261
+ /* ── Meta strip ── */
262
+ .meta {
263
+ display: inline-flex;
264
+ align-items: center;
265
+ gap: 24px;
266
+ padding: 14px 28px;
267
+ background: rgba(22, 27, 34, 0.8);
268
+ border: 1px solid var(--border);
269
+ border-radius: 10px;
270
+ backdrop-filter: blur(8px);
271
+ }
272
+
273
+ .metaItem {
274
+ display: flex;
275
+ flex-direction: column;
276
+ gap: 2px;
277
+ }
278
+
279
+ .metaLabel {
280
+ font-size: 11px;
281
+ font-weight: 600;
282
+ text-transform: uppercase;
283
+ letter-spacing: 1.2px;
284
+ color: var(--text-muted);
285
+ }
286
+
287
+ .metaValue {
288
+ font-size: 14px;
289
+ font-weight: 600;
290
+ color: var(--text);
291
+ }
292
+
293
+ .metaDivider {
294
+ width: 1px;
295
+ height: 32px;
296
+ background: var(--border);
297
+ }
298
+ `
299
+
300
+ function agentsMd(title) {
301
+ return `\
302
+ # Deck Project
303
+
304
+ This is a presentation deck built with \`@deckio/deck-engine\`.
305
+
306
+ ## Purpose
307
+
308
+ Create and maintain slide-based presentations. Each project is a self-contained deck with its own theme, data, and slides.
309
+
310
+ ## What to do
311
+
312
+ - Create, edit, and delete slides in \`src/slides/\`
313
+ - Manage project data in \`src/data/\`
314
+ - Register and reorder slides in \`deck.config.js\`
315
+
316
+ ## What NOT to do
317
+
318
+ - Do not modify \`App.jsx\`, \`main.jsx\`, \`vite.config.js\`, \`package.json\`, or \`index.html\` — these are scaffolding files driven by the engine
319
+ - Do not modify anything in \`node_modules/\` or the engine itself
320
+ - Do not add dependencies without being asked
321
+
322
+ ## Stack
323
+
324
+ - React 19, Vite, CSS Modules
325
+ - \`@deckio/deck-engine\` provides: \`Slide\`, \`BottomBar\`, \`Navigation\`, \`SlideProvider\`, \`useSlides\`, \`GenericThankYouSlide\`
326
+ - See \`.github/instructions/\` for detailed conventions on slide JSX, CSS modules, and deck.config.js
327
+ - See \`.github/skills/\` for step-by-step workflows (e.g., adding a slide)
328
+ `
329
+ }
330
+
331
+ const README = (title, subtitle, slug) => `\
332
+ # ${title}
333
+
334
+ ${subtitle}
335
+
336
+ ## Getting Started
337
+
338
+ \`\`\`bash
339
+ npm install
340
+ npm run dev
341
+ \`\`\`
342
+
343
+ Open [http://localhost:5173](http://localhost:5173) to view the presentation.
344
+
345
+ ## Adding Slides
346
+
347
+ 1. Create \`src/slides/MySlide.jsx\` and \`src/slides/MySlide.module.css\`
348
+ 2. Register it in \`deck.config.js\`:
349
+
350
+ \`\`\`js
351
+ import MySlide from './src/slides/MySlide.jsx'
352
+ // add to slides array
353
+ \`\`\`
354
+
355
+ ### Slide skeleton
356
+
357
+ \`\`\`jsx
358
+ import { BottomBar, Slide } from '@deckio/deck-engine'
359
+ import styles from './MySlide.module.css'
360
+
361
+ export default function MySlide({ index, project }) {
362
+ return (
363
+ <Slide index={index} className={styles.mySlide}>
364
+ <div className="accent-bar" />
365
+ <div className={\\\`orb \\\${styles.orb1}\\\`} />
366
+ <div className={\\\`orb \\\${styles.orb2}\\\`} />
367
+
368
+ <div className={\\\`\\\${styles.body} content-frame content-gutter\\\`}>
369
+ <h2>My Slide</h2>
370
+ </div>
371
+
372
+ <BottomBar text="${slug}" />
373
+ </Slide>
374
+ )
375
+ }
376
+ \`\`\`
377
+
378
+ ## Built with
379
+
380
+ - [deck-engine](https://www.npmjs.com/package/@deckio/deck-engine) — presentation framework
381
+ - React 19 + Vite
382
+ `
383
+
384
+ // ── Main ──
385
+
386
+ async function main() {
387
+ const arg = process.argv[2]
388
+
389
+ if (!arg || arg === '--help' || arg === '-h') {
390
+ console.log(`
391
+ Usage: npm create deckio <project-name>
392
+
393
+ Examples:
394
+ npm create deckio my-talk
395
+ npm create deckio quarterly-review
396
+ npx create-deckio cool-deck
397
+ `)
398
+ process.exit(0)
399
+ }
400
+
401
+ const slug = slugify(arg)
402
+ const dir = resolve(slug)
403
+
404
+ const defaultTitle = slug.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase())
405
+ const isInteractive = process.stdin.isTTY
406
+
407
+ let title, subtitle, accent, icon
408
+
409
+ console.log()
410
+ console.log(` 🎴 Creating deck project: ${slug}`)
411
+ console.log()
412
+
413
+ if (isInteractive) {
414
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
415
+ title = await ask(rl, ' Title:', defaultTitle)
416
+ subtitle = await ask(rl, ' Subtitle:', 'A presentation built with deck-engine')
417
+ accent = await ask(rl, ' Accent color:', '#3fb950')
418
+ icon = await ask(rl, ' Icon emoji:', '🎴')
419
+ rl.close()
420
+ } else {
421
+ title = defaultTitle
422
+ subtitle = 'A presentation built with deck-engine'
423
+ accent = '#3fb950'
424
+ icon = '🎴'
425
+ console.log(` Using defaults (non-interactive mode)`)
426
+ }
427
+ console.log()
428
+
429
+ // Write files
430
+ write(dir, 'package.json', packageJson(slug))
431
+ write(dir, 'vite.config.js', VITE_CONFIG)
432
+ write(dir, 'index.html', INDEX_HTML)
433
+ write(dir, 'src/main.jsx', MAIN_JSX)
434
+ write(dir, 'src/App.jsx', APP_JSX)
435
+ write(dir, 'src/data/.gitkeep', '')
436
+ write(dir, 'src/slides/CoverSlide.jsx', coverSlideJsx(title, subtitle, slug))
437
+ write(dir, 'src/slides/CoverSlide.module.css', COVER_SLIDE_CSS)
438
+ write(dir, 'deck.config.js', deckConfig(slug, title, subtitle, icon, accent))
439
+ write(dir, 'AGENTS.md', agentsMd(title))
440
+ write(dir, 'README.md', README(title, subtitle, slug))
441
+ write(dir, '.gitignore', 'node_modules\ndist\n.vite\n')
442
+
443
+ console.log(' 📁 Project scaffolded!')
444
+ console.log()
445
+
446
+ // Install dependencies
447
+ console.log(' 📦 Installing dependencies...')
448
+ console.log()
449
+ try {
450
+ execSync('npm install', { cwd: dir, stdio: 'inherit' })
451
+ } catch {
452
+ console.log()
453
+ console.log(' ⚠️ npm install failed — run it manually inside the project folder.')
454
+ }
455
+
456
+ // Run init-project to copy skills & instructions from the engine
457
+ console.log()
458
+ console.log(' 🔧 Initializing engine skills & instructions...')
459
+ try {
460
+ const initScript = join(dir, 'node_modules', '@deckio', 'deck-engine', 'scripts', 'init-project.mjs')
461
+ execSync(`node "${initScript}"`, { cwd: dir, stdio: 'inherit' })
462
+ } catch {
463
+ console.log(' ⚠️ Could not run init-project — run it manually: npx deck-init')
464
+ }
465
+
466
+ console.log()
467
+ console.log(` ✅ Done! Your deck is ready.`)
468
+ console.log()
469
+ console.log(` Next steps:`)
470
+ console.log(` cd ${slug}`)
471
+ console.log(` npm run dev`)
472
+ console.log()
473
+ console.log(` Open http://localhost:5173 and start adding slides!`)
474
+ console.log()
475
+ }
476
+
477
+ main()
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "create-deckio",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a new deck presentation project powered by @deckio/deck-engine",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-deckio": "./index.mjs"
8
+ },
9
+ "files": [
10
+ "index.mjs"
11
+ ],
12
+ "keywords": [
13
+ "deck",
14
+ "presentation",
15
+ "slides",
16
+ "scaffolding",
17
+ "create"
18
+ ],
19
+ "author": "Leandro Lopez",
20
+ "license": "MIT",
21
+ "publishConfig": {
22
+ "registry": "https://registry.npmjs.org"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/lopezleandro03/create-deck-project.git"
27
+ }
28
+ }