@deckio/deck-engine 1.7.4
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/components/BottomBar.jsx +9 -0
- package/components/BottomBar.module.css +17 -0
- package/components/Navigation.jsx +106 -0
- package/components/Navigation.module.css +145 -0
- package/components/Slide.jsx +15 -0
- package/context/SlideContext.jsx +171 -0
- package/index.js +5 -0
- package/instructions/AGENTS.md +26 -0
- package/instructions/deck-config.instructions.md +34 -0
- package/instructions/deck-project.instructions.md +34 -0
- package/instructions/slide-css.instructions.md +91 -0
- package/instructions/slide-jsx.instructions.md +34 -0
- package/package.json +45 -0
- package/scripts/capture-screen.mjs +110 -0
- package/scripts/export-pdf.mjs +287 -0
- package/scripts/generate-image.mjs +110 -0
- package/scripts/init-project.mjs +188 -0
- package/skills/deck-add-slide/SKILL.md +217 -0
- package/skills/deck-delete-slide/SKILL.md +51 -0
- package/skills/deck-generate-image/SKILL.md +85 -0
- package/skills/deck-inspect/SKILL.md +60 -0
- package/skills/deck-sketch/SKILL.md +91 -0
- package/skills/deck-validate-project/SKILL.md +80 -0
- package/slides/GenericThankYouSlide.jsx +31 -0
- package/slides/ThankYouSlide.module.css +131 -0
- package/styles/global.css +191 -0
- package/vite.js +26 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* deck-engine init — provisions Copilot skills and state into a deck project.
|
|
4
|
+
*
|
|
5
|
+
* Copies .github/skills/ from the engine package and bootstraps
|
|
6
|
+
* .github/memory/state.md with project metadata from deck.config.js.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node node_modules/@deckio/deck-engine/scripts/init-project.mjs
|
|
10
|
+
* npx deck-init (if bin is configured)
|
|
11
|
+
*
|
|
12
|
+
* Idempotent — safe to re-run. Updates skills, preserves state.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, copyFileSync, rmSync } from 'fs'
|
|
15
|
+
import { join, dirname } from 'path'
|
|
16
|
+
import { fileURLToPath } from 'url'
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
19
|
+
const engineRoot = join(__dirname, '..')
|
|
20
|
+
const projectRoot = process.cwd()
|
|
21
|
+
|
|
22
|
+
// ── Discover project metadata from deck.config.js ──
|
|
23
|
+
|
|
24
|
+
function readProjectMeta() {
|
|
25
|
+
const configPath = join(projectRoot, 'deck.config.js')
|
|
26
|
+
if (!existsSync(configPath)) {
|
|
27
|
+
console.error('❌ No deck.config.js found in', projectRoot)
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
const content = readFileSync(configPath, 'utf-8')
|
|
31
|
+
const str = (key) => {
|
|
32
|
+
const m = content.match(new RegExp(`${key}:\\s*['"\`]([^'"\`]+)['"\`]`))
|
|
33
|
+
return m ? m[1] : null
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
id: str('id') || 'unknown',
|
|
37
|
+
title: str('title') || 'Deck Project',
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Copy skills ──
|
|
42
|
+
|
|
43
|
+
function copySkills() {
|
|
44
|
+
const srcSkills = join(engineRoot, 'skills')
|
|
45
|
+
if (!existsSync(srcSkills)) {
|
|
46
|
+
console.warn('⚠️ No skills directory in engine package')
|
|
47
|
+
return 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const destSkills = join(projectRoot, '.github', 'skills')
|
|
51
|
+
let count = 0
|
|
52
|
+
|
|
53
|
+
// Collect engine skill names to detect stale skills
|
|
54
|
+
const engineSkillNames = new Set()
|
|
55
|
+
for (const entry of readdirSync(srcSkills, { withFileTypes: true })) {
|
|
56
|
+
if (!entry.isDirectory()) continue
|
|
57
|
+
const skillFile = join(srcSkills, entry.name, 'SKILL.md')
|
|
58
|
+
if (!existsSync(skillFile)) continue
|
|
59
|
+
engineSkillNames.add(entry.name)
|
|
60
|
+
|
|
61
|
+
const destDir = join(destSkills, entry.name)
|
|
62
|
+
mkdirSync(destDir, { recursive: true })
|
|
63
|
+
copyFileSync(skillFile, join(destDir, 'SKILL.md'))
|
|
64
|
+
count++
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Remove stale skills no longer in the engine
|
|
68
|
+
if (existsSync(destSkills)) {
|
|
69
|
+
for (const entry of readdirSync(destSkills, { withFileTypes: true })) {
|
|
70
|
+
if (!entry.isDirectory()) continue
|
|
71
|
+
if (!engineSkillNames.has(entry.name)) {
|
|
72
|
+
rmSync(join(destSkills, entry.name), { recursive: true, force: true })
|
|
73
|
+
console.log(` Removed stale skill: ${entry.name}`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return count
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Copy instructions ──
|
|
82
|
+
|
|
83
|
+
function copyInstructions() {
|
|
84
|
+
const srcInstructions = join(engineRoot, 'instructions')
|
|
85
|
+
if (!existsSync(srcInstructions)) {
|
|
86
|
+
console.warn('⚠️ No instructions directory in engine package')
|
|
87
|
+
return 0
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const destInstructions = join(projectRoot, '.github', 'instructions')
|
|
91
|
+
mkdirSync(destInstructions, { recursive: true })
|
|
92
|
+
let count = 0
|
|
93
|
+
|
|
94
|
+
// Collect engine instruction names to detect stale instructions
|
|
95
|
+
const engineInstrNames = new Set()
|
|
96
|
+
for (const entry of readdirSync(srcInstructions, { withFileTypes: true })) {
|
|
97
|
+
if (!entry.isFile() || !entry.name.endsWith('.instructions.md')) continue
|
|
98
|
+
engineInstrNames.add(entry.name)
|
|
99
|
+
copyFileSync(
|
|
100
|
+
join(srcInstructions, entry.name),
|
|
101
|
+
join(destInstructions, entry.name)
|
|
102
|
+
)
|
|
103
|
+
count++
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Remove stale instructions no longer in the engine
|
|
107
|
+
for (const entry of readdirSync(destInstructions, { withFileTypes: true })) {
|
|
108
|
+
if (!entry.isFile() || !entry.name.endsWith('.instructions.md')) continue
|
|
109
|
+
if (!engineInstrNames.has(entry.name)) {
|
|
110
|
+
rmSync(join(destInstructions, entry.name), { force: true })
|
|
111
|
+
console.log(` Removed stale instruction: ${entry.name}`)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return count
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Copy AGENTS.md ──
|
|
119
|
+
|
|
120
|
+
function copyAgentsMd() {
|
|
121
|
+
const src = join(engineRoot, 'instructions', 'AGENTS.md')
|
|
122
|
+
if (!existsSync(src)) return false
|
|
123
|
+
copyFileSync(src, join(projectRoot, 'AGENTS.md'))
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Bootstrap state.md ──
|
|
128
|
+
|
|
129
|
+
function bootstrapState(meta) {
|
|
130
|
+
const stateDir = join(projectRoot, '.github', 'memory')
|
|
131
|
+
const statePath = join(stateDir, 'state.md')
|
|
132
|
+
|
|
133
|
+
// Don't overwrite existing state (preserves user's port/url config)
|
|
134
|
+
if (existsSync(statePath)) {
|
|
135
|
+
console.log(' state.md already exists — preserved')
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
mkdirSync(stateDir, { recursive: true })
|
|
140
|
+
const content = `# Deck State
|
|
141
|
+
|
|
142
|
+
## Project
|
|
143
|
+
id: ${meta.id}
|
|
144
|
+
title: ${meta.title}
|
|
145
|
+
|
|
146
|
+
## Dev Server
|
|
147
|
+
port: 5173
|
|
148
|
+
url: http://localhost:5173/
|
|
149
|
+
`
|
|
150
|
+
writeFileSync(statePath, content, 'utf-8')
|
|
151
|
+
console.log(' Created .github/memory/state.md')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ── Create eyes directory ──
|
|
155
|
+
|
|
156
|
+
function createEyesDir() {
|
|
157
|
+
const eyesDir = join(projectRoot, '.github', 'eyes')
|
|
158
|
+
mkdirSync(eyesDir, { recursive: true })
|
|
159
|
+
|
|
160
|
+
// Add to .gitignore if not already there
|
|
161
|
+
const gitignorePath = join(projectRoot, '.gitignore')
|
|
162
|
+
if (existsSync(gitignorePath)) {
|
|
163
|
+
const gitignore = readFileSync(gitignorePath, 'utf-8')
|
|
164
|
+
if (!gitignore.includes('.github/eyes')) {
|
|
165
|
+
writeFileSync(gitignorePath, gitignore.trimEnd() + '\n.github/eyes/\n', 'utf-8')
|
|
166
|
+
console.log(' Added .github/eyes/ to .gitignore')
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── Main ──
|
|
172
|
+
|
|
173
|
+
const meta = readProjectMeta()
|
|
174
|
+
console.log(`\n🎯 Initializing deck project: ${meta.title} (${meta.id})`)
|
|
175
|
+
|
|
176
|
+
const skillCount = copySkills()
|
|
177
|
+
console.log(` Copied ${skillCount} Copilot skills to .github/skills/`)
|
|
178
|
+
|
|
179
|
+
const instrCount = copyInstructions()
|
|
180
|
+
console.log(` Copied ${instrCount} Copilot instructions to .github/instructions/`)
|
|
181
|
+
|
|
182
|
+
const agentsCopied = copyAgentsMd()
|
|
183
|
+
if (agentsCopied) console.log(' Copied AGENTS.md to project root')
|
|
184
|
+
|
|
185
|
+
bootstrapState(meta)
|
|
186
|
+
createEyesDir()
|
|
187
|
+
|
|
188
|
+
console.log(`\n✅ Done. Run \`copilot --yolo\` to start editing with Copilot skills.\n`)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deck-add-slide
|
|
3
|
+
description: Guide for adding a new slide to a deck project. Use this when asked to create, add, or build a new slide component.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Adding a Slide to a Deck Project
|
|
7
|
+
|
|
8
|
+
## A. Slide Component Structure (mandatory skeleton)
|
|
9
|
+
|
|
10
|
+
Every slide **must** follow this structure:
|
|
11
|
+
|
|
12
|
+
```jsx
|
|
13
|
+
import { BottomBar, Slide } from '@deckio/deck-engine'
|
|
14
|
+
import styles from './MyNewSlide.module.css'
|
|
15
|
+
|
|
16
|
+
export default function MyNewSlide({ index, project }) {
|
|
17
|
+
return (
|
|
18
|
+
<Slide index={index} className={styles.myNewSlide}>
|
|
19
|
+
{/* 1. Decorative elements — always first */}
|
|
20
|
+
<div className="accent-bar" />
|
|
21
|
+
<div className={`orb ${styles.orb1}`} />
|
|
22
|
+
<div className={`orb ${styles.orb2}`} />
|
|
23
|
+
|
|
24
|
+
{/* 2. Content area — vertically centered */}
|
|
25
|
+
<div className={`${styles.body} content-frame content-gutter`}>
|
|
26
|
+
{/* Slide content */}
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
{/* 3. Footer — always last child */}
|
|
30
|
+
<BottomBar text="Project Footer Text" />
|
|
31
|
+
</Slide>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Mandatory elements (in order inside `<Slide>`):
|
|
37
|
+
|
|
38
|
+
1. **`<div className="accent-bar" />`** — Left gradient accent bar. Include on every slide.
|
|
39
|
+
2. **Orbs** — 2–4 `<div className={\`orb ${styles.orbN}\`} />` for ambient background glow.
|
|
40
|
+
3. **Content wrapper** — `<div className="content-frame content-gutter">` constrains width to `1280px` and adds `72px` horizontal padding. **All visible content goes inside this wrapper.**
|
|
41
|
+
4. **`<BottomBar text="..." />`** — Sticky footer, always the last child. The `text` must match the project's convention (check existing slides).
|
|
42
|
+
|
|
43
|
+
### Import paths (standalone project):
|
|
44
|
+
|
|
45
|
+
| Resource | Import Path |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `Slide`, `BottomBar`, `Navigation`, `SlideProvider`, `useSlides` | `'@deckio/deck-engine'` |
|
|
48
|
+
| `GenericThankYouSlide` | `'@deckio/deck-engine'` |
|
|
49
|
+
| Data / logos | `'../data/<file>'` |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## B. CSS Module Rules
|
|
54
|
+
|
|
55
|
+
Create a companion `.module.css` file matching the JSX filename (e.g., `MyNewSlide.module.css`).
|
|
56
|
+
|
|
57
|
+
### Required root class properties
|
|
58
|
+
|
|
59
|
+
```css
|
|
60
|
+
.myNewSlide {
|
|
61
|
+
background: var(--bg-deep);
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
padding: 0 0 44px 0;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- `background: var(--bg-deep)` — dark background on every slide
|
|
68
|
+
- `flex-direction: column` — global `.slide` sets `display: flex`; this orients content vertically
|
|
69
|
+
- `padding: 0 0 44px 0` — reserves space for the 44px BottomBar
|
|
70
|
+
|
|
71
|
+
Optional: add `justify-content: center` to vertically center content (cover slides, thank-you slides).
|
|
72
|
+
|
|
73
|
+
### Orb positioning (standard recipe)
|
|
74
|
+
|
|
75
|
+
```css
|
|
76
|
+
.orb1 {
|
|
77
|
+
width: 420px; height: 420px;
|
|
78
|
+
top: -100px; right: -60px;
|
|
79
|
+
background: radial-gradient(circle at 40% 40%, var(--accent), var(--blue-glow) 50%, transparent 70%);
|
|
80
|
+
}
|
|
81
|
+
.orb2 {
|
|
82
|
+
width: 320px; height: 320px;
|
|
83
|
+
bottom: -40px; right: 100px;
|
|
84
|
+
background: radial-gradient(circle at 50% 50%, var(--purple-deep), rgba(110,64,201,0.25) 60%, transparent 75%);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Vertical centering body wrapper
|
|
89
|
+
|
|
90
|
+
```css
|
|
91
|
+
.body {
|
|
92
|
+
position: relative;
|
|
93
|
+
z-index: 10;
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
justify-content: center;
|
|
97
|
+
flex: 1;
|
|
98
|
+
min-height: 0;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Available CSS custom properties
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
--bg-deep: #080b10 --surface: #161b22 --border: #30363d
|
|
106
|
+
--text: #e6edf3 --text-muted: #8b949e --accent: #58a6ff
|
|
107
|
+
--blue-glow: #1f6feb --purple: #bc8cff --purple-deep: #6e40c9
|
|
108
|
+
--pink: #f778ba --cyan: #56d4dd --green: #3fb950
|
|
109
|
+
--orange: #d29922
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Available global CSS classes (no import needed)
|
|
113
|
+
|
|
114
|
+
| Class | Purpose |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `accent-bar` | Left gradient accent bar |
|
|
117
|
+
| `orb` | Base decorative orb (absolute, rounded, blur, opacity) |
|
|
118
|
+
| `grid-dots` | Dot grid pattern (200×200px) |
|
|
119
|
+
| `content-frame` | Width constraint to `1280px`, centered |
|
|
120
|
+
| `content-gutter` | `72px` left/right padding |
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## C. Typography Conventions
|
|
125
|
+
|
|
126
|
+
| Element | Size | Weight | Spacing | Usage |
|
|
127
|
+
|---|---|---|---|---|
|
|
128
|
+
| `h1` | `clamp(42px, 5vw, 72px)` | 900 | `-2px` | Cover slides only |
|
|
129
|
+
| `h2` | `clamp(28px, 3.2vw, 36px)` | 700 | `-0.8px` | Main slide heading |
|
|
130
|
+
| `h3` | `16px–20px` | 700 | `-0.3px` | Card titles |
|
|
131
|
+
| Subtitle | `17px` | 300–400 | — | `color: var(--text-muted)`, below heading |
|
|
132
|
+
| Body text | `13px–14px` | 400 | — | `color: var(--text-muted)` |
|
|
133
|
+
| Badge/label | `10px–11px` | 600–700 | `1.5px` | Uppercase, rounded bg |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## D. Content Layout Patterns
|
|
138
|
+
|
|
139
|
+
### Card grid
|
|
140
|
+
```css
|
|
141
|
+
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Standard card
|
|
145
|
+
```css
|
|
146
|
+
.card {
|
|
147
|
+
background: var(--surface);
|
|
148
|
+
border: 1px solid var(--border);
|
|
149
|
+
border-radius: 16px;
|
|
150
|
+
padding: 24px;
|
|
151
|
+
overflow: hidden;
|
|
152
|
+
transition: transform 0.3s ease, border-color 0.3s ease;
|
|
153
|
+
}
|
|
154
|
+
.card::before {
|
|
155
|
+
content: '';
|
|
156
|
+
position: absolute;
|
|
157
|
+
top: 0; left: 0; right: 0; height: 3px;
|
|
158
|
+
background: linear-gradient(90deg, var(--purple), var(--accent));
|
|
159
|
+
opacity: 0.6;
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## E. Registration in deck.config.js
|
|
166
|
+
|
|
167
|
+
After creating the slide files, register the slide in `deck.config.js`:
|
|
168
|
+
|
|
169
|
+
1. **Add an import** at the top: `import MyNewSlide from './src/slides/MyNewSlide.jsx'`
|
|
170
|
+
2. **Add the component** to the `slides` array at the desired position.
|
|
171
|
+
|
|
172
|
+
The generic `App.jsx` renders slides from this array, passing `index` as a prop automatically. **You do NOT need to manage index numbers manually** — they are assigned by array position.
|
|
173
|
+
|
|
174
|
+
### Example: adding before ThankYou
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
import MyNewSlide from './src/slides/MyNewSlide.jsx'
|
|
178
|
+
// ... other imports
|
|
179
|
+
|
|
180
|
+
export default {
|
|
181
|
+
// ... metadata
|
|
182
|
+
slides: [
|
|
183
|
+
CoverSlide,
|
|
184
|
+
// ... existing slides
|
|
185
|
+
MyNewSlide, // ← insert here
|
|
186
|
+
ThankYouSlide, // stays last
|
|
187
|
+
],
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## F. Anti-Patterns to Avoid
|
|
194
|
+
|
|
195
|
+
1. **Missing `accent-bar`** — include on every slide.
|
|
196
|
+
2. **Missing `content-frame content-gutter`** — content will be full-width without standard margins.
|
|
197
|
+
3. **Missing `BottomBar`** — every slide needs it as the last child.
|
|
198
|
+
4. **String paths for images** — always use `import logo from '../data/...'` (Vite resolves to URL).
|
|
199
|
+
5. **Missing `padding: 0 0 44px 0`** on the slide root CSS class — content will overlap the BottomBar.
|
|
200
|
+
6. **Inconsistent `BottomBar text`** — check existing slides and match their footer text.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## G. Complete Step-by-Step
|
|
205
|
+
|
|
206
|
+
1. **Create** `src/slides/<SlideName>Slide.jsx` following the mandatory skeleton (section A).
|
|
207
|
+
2. **Create** `src/slides/<SlideName>Slide.module.css` with required root properties (section B).
|
|
208
|
+
3. **Register** in `deck.config.js` — add import + add to `slides` array (section E).
|
|
209
|
+
4. **Verify** — the dev server hot-reloads automatically. Navigate to the new slide and check layout.
|
|
210
|
+
|
|
211
|
+
### Quick checklist
|
|
212
|
+
|
|
213
|
+
- [ ] Created `<SlideName>Slide.jsx` with Slide, accent-bar, orbs, content-frame, BottomBar
|
|
214
|
+
- [ ] Created `<SlideName>Slide.module.css` with `background: var(--bg-deep)`, `flex-direction: column`, `padding: 0 0 44px 0`, body centering wrapper
|
|
215
|
+
- [ ] Import added to `deck.config.js`
|
|
216
|
+
- [ ] Component added to `slides` array at correct position
|
|
217
|
+
- [ ] `BottomBar text` matches project convention
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deck-delete-slide
|
|
3
|
+
description: Guide for removing a slide from a deck project. Use this when asked to delete, remove, or drop a slide.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Removing a Slide from a Deck Project
|
|
7
|
+
|
|
8
|
+
## Step 1: Identify the slide to remove
|
|
9
|
+
|
|
10
|
+
Open `deck.config.js` and review the `slides` array to see all slides and their order. If the user didn't specify which slide to remove, list them so the user can choose.
|
|
11
|
+
|
|
12
|
+
## Step 2: Remove from deck.config.js
|
|
13
|
+
|
|
14
|
+
1. **Remove the import statement** for the slide being deleted.
|
|
15
|
+
2. **Remove the component** from the `slides` array.
|
|
16
|
+
|
|
17
|
+
No index management is needed — the generic `App.jsx` assigns indexes by array position automatically.
|
|
18
|
+
|
|
19
|
+
## Step 3: Delete the slide files
|
|
20
|
+
|
|
21
|
+
Delete from `src/slides/`:
|
|
22
|
+
|
|
23
|
+
- `<SlideName>Slide.jsx`
|
|
24
|
+
- `<SlideName>Slide.module.css`
|
|
25
|
+
|
|
26
|
+
**Do not delete** shared slides from `@deckio/deck-engine` (like `GenericThankYouSlide`) — only remove the import.
|
|
27
|
+
|
|
28
|
+
## Step 4: Check for references
|
|
29
|
+
|
|
30
|
+
Search `src/slides/` for any remaining references to the deleted slide:
|
|
31
|
+
|
|
32
|
+
- Other slides that import or reference the deleted component
|
|
33
|
+
- Data files specific to the deleted slide (if any)
|
|
34
|
+
- Remove any orphaned references
|
|
35
|
+
|
|
36
|
+
## Step 5: Verify
|
|
37
|
+
|
|
38
|
+
The dev server hot-reloads automatically. Navigate through all slides and confirm:
|
|
39
|
+
|
|
40
|
+
- The deleted slide no longer appears
|
|
41
|
+
- All remaining slides are navigable (← → arrows, keyboard)
|
|
42
|
+
- Progress bar reflects the correct new total
|
|
43
|
+
- No console errors
|
|
44
|
+
|
|
45
|
+
## Quick checklist
|
|
46
|
+
|
|
47
|
+
- [ ] Removed import from `deck.config.js`
|
|
48
|
+
- [ ] Removed component from `slides` array
|
|
49
|
+
- [ ] Deleted slide `.jsx` and `.module.css` files
|
|
50
|
+
- [ ] No orphaned references to the deleted slide
|
|
51
|
+
- [ ] No console errors, all slides navigable
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deck-generate-image
|
|
3
|
+
description: Generate images (icons, illustrations, diagrams) using chatgpt-image-latest for use in slide components. Use this when a slide needs a visual element too complex for pure HTML/CSS.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Generate Images for Slides
|
|
7
|
+
|
|
8
|
+
Generate images via the OpenAI **Image API** with `chatgpt-image-latest` for specific visual elements within slides.
|
|
9
|
+
|
|
10
|
+
> If `chatgpt-image-latest` returns a 403, pass `--model gpt-image-1.5` as a fallback.
|
|
11
|
+
|
|
12
|
+
## When to use
|
|
13
|
+
|
|
14
|
+
- A slide needs an **icon or illustration** that doesn't exist in the codebase.
|
|
15
|
+
- A sketch element is too detailed to reproduce with CSS.
|
|
16
|
+
- The user explicitly asks for a generated graphic.
|
|
17
|
+
|
|
18
|
+
## When NOT to use
|
|
19
|
+
|
|
20
|
+
- For entire slides — use **deck-add-slide** instead.
|
|
21
|
+
- For simple shapes — use CSS or inline SVG.
|
|
22
|
+
- For logos or photos — source them directly.
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
25
|
+
|
|
26
|
+
The `OPENAI_API_KEY` must be set. Create/edit `.env` in the project root:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
OPENAI_API_KEY=sk-proj-...
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Workflow
|
|
33
|
+
|
|
34
|
+
### Step 1 — Craft the prompt
|
|
35
|
+
|
|
36
|
+
Write a detailed prompt including:
|
|
37
|
+
|
|
38
|
+
- **Subject** — what the image depicts.
|
|
39
|
+
- **Style** — flat icon, line art, illustration, isometric, etc.
|
|
40
|
+
- **Colors** — match the design system: `#0d1117` (bg-deep), `#58a6ff` (accent), `#3fb950` (green), `#bc8cff` (purple).
|
|
41
|
+
- **Background** — almost always "transparent background".
|
|
42
|
+
- **Aspect ratio** — square (1024x1024) for icons, wide (1536x1024) for banners.
|
|
43
|
+
|
|
44
|
+
### Step 2 — Generate
|
|
45
|
+
|
|
46
|
+
Run from the project root:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node node_modules/@deckio/deck-engine/scripts/generate-image.mjs --prompt "your prompt" --name my-icon
|
|
50
|
+
node node_modules/@deckio/deck-engine/scripts/generate-image.mjs --prompt "..." --name hero --size 1536x1024
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
| Flag | Description | Default |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `--prompt` | Image description (required) | — |
|
|
56
|
+
| `--name` | Filename without extension (required) | — |
|
|
57
|
+
| `--size` | `1024x1024`, `1536x1024`, `1024x1536`, `auto` | `1024x1024` |
|
|
58
|
+
| `--quality` | `low`, `medium`, `high`, `auto` | `auto` |
|
|
59
|
+
| `--model` | OpenAI model | `chatgpt-image-latest` |
|
|
60
|
+
|
|
61
|
+
Images are saved to `src/data/generated/`.
|
|
62
|
+
|
|
63
|
+
### Step 3 — Use in a slide
|
|
64
|
+
|
|
65
|
+
```jsx
|
|
66
|
+
import myIcon from '../data/generated/my-icon.png'
|
|
67
|
+
|
|
68
|
+
<img src={myIcon} alt="Bridge icon" style={{ width: 120, height: 120 }} />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Step 4 — Iterate
|
|
72
|
+
|
|
73
|
+
If the image doesn't match expectations, refine the prompt and re-run with the same `--name` to overwrite. Use **deck-eyes** to verify how it looks in the slide.
|
|
74
|
+
|
|
75
|
+
## SVG alternative
|
|
76
|
+
|
|
77
|
+
For simple icons, write SVG markup directly in JSX:
|
|
78
|
+
|
|
79
|
+
```jsx
|
|
80
|
+
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
81
|
+
<circle cx="24" cy="24" r="20" stroke="#58a6ff" strokeWidth="2" />
|
|
82
|
+
</svg>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Prefer inline SVG for geometric shapes. Use image generation for complex illustrations.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deck-inspect
|
|
3
|
+
description: Capture a screenshot of the deck app to visually inspect slides. Use this when asked to look at, see, view, inspect, check visually, or preview a slide.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Visual Inspection — Capture a Slide Screenshot
|
|
7
|
+
|
|
8
|
+
Captures a screenshot of the running deck using VS Code browser tools (Edge "Sharing with Agent").
|
|
9
|
+
|
|
10
|
+
## Deciding what to capture
|
|
11
|
+
|
|
12
|
+
1. **Slide** — resolve in this order:
|
|
13
|
+
- If the user said "slide 3" or "the cover slide" → map to a 1-based number.
|
|
14
|
+
- If you just created or edited a specific slide → use that slide's array position + 1.
|
|
15
|
+
- If not specified → capture slide 1.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
The dev server must be running. Check `.github/memory/state.md` for the port. Default is `5173`.
|
|
20
|
+
|
|
21
|
+
## Workflow
|
|
22
|
+
|
|
23
|
+
### Step 1 — Open or reuse a browser page
|
|
24
|
+
|
|
25
|
+
Check the attached browser pages for an existing deck tab. If none exists, open one:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
open_browser_page → http://localhost:<port>/#/<project-id>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Read `project` and `port` from `.github/memory/state.md` if not known.
|
|
32
|
+
|
|
33
|
+
### Step 2 — Navigate to the target slide
|
|
34
|
+
|
|
35
|
+
The deck opens on slide 1. To reach slide N, press `ArrowRight` (N − 1) times:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
// run_playwright_code on the page
|
|
39
|
+
for (let i = 0; i < N - 1; i++) {
|
|
40
|
+
await page.keyboard.press('ArrowRight');
|
|
41
|
+
await page.waitForTimeout(300);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If the page is already on a different slide, navigate to slide 1 first by pressing `Home`, then advance forward.
|
|
46
|
+
|
|
47
|
+
### Step 3 — Take a screenshot
|
|
48
|
+
|
|
49
|
+
Use `screenshot_page` with the page ID to capture the current view. The screenshot is returned inline — no file path needed.
|
|
50
|
+
|
|
51
|
+
### Step 4 — Inspect and report
|
|
52
|
+
|
|
53
|
+
Study the screenshot and check for:
|
|
54
|
+
- Layout alignment and spacing
|
|
55
|
+
- Typography (size, weight, color)
|
|
56
|
+
- Missing or broken elements
|
|
57
|
+
- Color and contrast issues
|
|
58
|
+
- Overflow or clipping
|
|
59
|
+
|
|
60
|
+
Report any issues found to the user.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deck-sketch
|
|
3
|
+
description: Sketch a slide on Whiteboard, capture the sketch, and use it as inspiration to create a new slide. Use this when the user wants to draw, sketch, or wireframe a slide before building it.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Sketch a Slide
|
|
7
|
+
|
|
8
|
+
Use Whiteboard to sketch a slide layout, capture the result, and translate it into a real slide component.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
### Step 1 — Open Whiteboard
|
|
13
|
+
|
|
14
|
+
```powershell
|
|
15
|
+
Start-Process "ms-whiteboard-cmd:"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Tell the user:
|
|
19
|
+
|
|
20
|
+
> **Whiteboard is open. Sketch your slide layout. When you're done, use "Fit to screen" (Ctrl+Shift+F) so the entire sketch is visible, then tell me you're ready.**
|
|
21
|
+
|
|
22
|
+
**STOP here.** Do NOT proceed until the user explicitly says the sketch is ready.
|
|
23
|
+
|
|
24
|
+
### Step 2 — Capture the sketch
|
|
25
|
+
|
|
26
|
+
When the user says the sketch is ready:
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
Add-Type @"
|
|
30
|
+
using System;
|
|
31
|
+
using System.Runtime.InteropServices;
|
|
32
|
+
using System.Drawing;
|
|
33
|
+
public class WinCapture {
|
|
34
|
+
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
|
|
35
|
+
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
|
|
36
|
+
[DllImport("user32.dll")] public static extern bool SetProcessDPIAware();
|
|
37
|
+
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int L, T, R, B; }
|
|
38
|
+
}
|
|
39
|
+
"@
|
|
40
|
+
[WinCapture]::SetProcessDPIAware() | Out-Null
|
|
41
|
+
Add-Type -AssemblyName System.Drawing
|
|
42
|
+
$h = (Get-Process | Where-Object { $_.MainWindowTitle -like '*Whiteboard*' } | Select-Object -First 1).MainWindowHandle
|
|
43
|
+
if (!$h -or $h -eq [IntPtr]::Zero) { Write-Error "Whiteboard window not found"; return }
|
|
44
|
+
[WinCapture]::SetForegroundWindow($h) | Out-Null
|
|
45
|
+
Start-Sleep -Milliseconds 500
|
|
46
|
+
$r = New-Object WinCapture+RECT
|
|
47
|
+
[WinCapture]::GetWindowRect($h, [ref]$r) | Out-Null
|
|
48
|
+
$w = $r.R - $r.L; $ht = $r.B - $r.T
|
|
49
|
+
$bmp = New-Object Drawing.Bitmap $w, $ht
|
|
50
|
+
$g = [Drawing.Graphics]::FromImage($bmp)
|
|
51
|
+
$g.CopyFromScreen($r.L, $r.T, 0, 0, (New-Object Drawing.Size $w, $ht))
|
|
52
|
+
$dir = ".github\eyes"
|
|
53
|
+
if (!(Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
|
54
|
+
$file = Join-Path $dir "sketch-$(Get-Date -Format 'yyyy-MM-ddTHH-mm-ss').png"
|
|
55
|
+
$bmp.Save($file)
|
|
56
|
+
$g.Dispose(); $bmp.Dispose()
|
|
57
|
+
Write-Host "Sketch saved: $file"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Step 3 — Analyze the sketch
|
|
61
|
+
|
|
62
|
+
Reference the saved screenshot image. Study it carefully and identify:
|
|
63
|
+
|
|
64
|
+
- **Layout structure** — columns/rows, content zones, header/footer areas.
|
|
65
|
+
- **Text elements** — headings, labels, bullet points, callouts.
|
|
66
|
+
- **Visual elements** — boxes, icons, images, dividers, backgrounds.
|
|
67
|
+
- **Data patterns** — tables, grids, lists, charts, metrics.
|
|
68
|
+
|
|
69
|
+
Describe what you see back to the user and confirm your interpretation.
|
|
70
|
+
|
|
71
|
+
### Step 4 — Create the slide
|
|
72
|
+
|
|
73
|
+
Use the **deck-add-slide** skill to build the slide, guided by the sketch:
|
|
74
|
+
|
|
75
|
+
1. Map sketch regions to CSS Grid or Flexbox layout.
|
|
76
|
+
2. Translate hand-drawn text into real content with proper typography.
|
|
77
|
+
3. Replace rough shapes with styled `<div>` containers using CSS Modules.
|
|
78
|
+
4. Follow all deck-add-slide conventions (accent-bar, content-frame, BottomBar, imports from `@deckio/deck-engine`).
|
|
79
|
+
5. Register the slide in `deck.config.js`.
|
|
80
|
+
|
|
81
|
+
### Step 5 — Visual verification
|
|
82
|
+
|
|
83
|
+
After creating the slide, use **deck-eyes** to capture a screenshot of the rendered result.
|
|
84
|
+
|
|
85
|
+
Compare the rendered slide against the original sketch. If the user is present, show both and ask if adjustments are needed.
|
|
86
|
+
|
|
87
|
+
## Notes
|
|
88
|
+
|
|
89
|
+
- Whiteboard sketches are rough — interpret intent, not exact pixels.
|
|
90
|
+
- If text is hard to read, ask the user to clarify.
|
|
91
|
+
- Screenshots are saved under `.github/eyes/` (gitignored) with a `sketch-` prefix.
|