@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.
@@ -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.