make-slide 2.1.1 → 2.2.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.
@@ -111,15 +111,16 @@ Ask the user which image approach they prefer:
111
111
 
112
112
  - **Option A** → Use CSS placeholders matching the theme (emoji, SVG icons, CSS shapes)
113
113
  - **Option B** → Mark image positions in the outline, ask user for URLs, use `<img src>` with `loading="lazy"` and descriptive `alt` text
114
- - **Option C** → For each slide that would benefit from an image:
115
- 1. Determine a relevant English search keyword based on the slide content
116
- 2. Use Unsplash source URLs which are guaranteed to resolve: `https://source.unsplash.com/featured/1200x800/?{keyword}`
117
- - Example: `https://source.unsplash.com/featured/1200x800/?technology,ai`
118
- - Example: `https://source.unsplash.com/featured/1200x800/?teamwork,office`
119
- 3. Alternatively, search the web for images on Unsplash or Pexels and use the direct image URL (verify the URL returns 200 before inserting)
120
- 4. Insert as `<img src="URL" alt="description" loading="lazy">`
121
- 5. NEVER use AI-generated or guessed URLs only use URLs from actual search results or the Unsplash source pattern above
122
- 6. Inform the user that auto-searched images are royalty-free from Unsplash (Unsplash License)
114
+ - **Option C** → Automatically search and place images, but be selective:
115
+ 1. NOT every slide needs an image. Only add images to slides where a visual genuinely enhances understanding (e.g., concept slides, example slides, section dividers). Skip images for quote slides, code slides, data/chart slides, comparison slides, and agenda slides.
116
+ 2. Aim for roughly 30-40% of slides having images not all of them.
117
+ 3. Determine a relevant English search keyword based on the slide content.
118
+ 4. Use Unsplash direct photo URLs: `https://images.unsplash.com/photo-{PHOTO_ID}?w=800&h=600&fit=crop`
119
+ - To find valid photo IDs, search the web for `site:unsplash.com {keyword}` and extract the photo ID from the URL.
120
+ - NEVER guess or fabricate Unsplash photo IDs — every URL must come from an actual search result.
121
+ 5. After collecting image URLs, verify each one actually returns an image (HTTP 200) before inserting. Replace any 404 URLs with CSS placeholders.
122
+ 6. Insert as `<img src="URL" alt="description" loading="lazy">`
123
+ 7. Inform the user that images are from Unsplash (Unsplash License, free for commercial use).
123
124
 
124
125
  ### Step 4: Generate Outline
125
126
  Create a slide-by-slide outline with:
@@ -188,13 +189,42 @@ When the user selects PPTX output, follow this modified workflow:
188
189
  Follow the standard HTML generation workflow (Steps 1-7) but with these constraints from the PPTX spec. Read `references/pptx-spec.md` for the complete PPTX conversion rules before generating HTML.
189
190
 
190
191
  ### PPTX Step 2: Convert HTML to PPTX
191
- Use the html2pptx conversion pipeline:
192
- 1. Install dependencies if not present: `npm install pptxgenjs sharp puppeteer`
193
- 2. For each HTML slide, render and convert to PPTX using PptxGenJS
194
- 3. Combine all slides into a single `.pptx` file
192
+ Use the screenshot-based conversion pipeline with Puppeteer + PptxGenJS:
193
+ 1. Install dependencies if not present: `npm install pptxgenjs puppeteer`
194
+ 2. Write a conversion script that:
195
+ - Opens the HTML file in a headless browser with viewport 1280×720 (16:9)
196
+ - For EACH slide:
197
+ a. Navigate to that slide (set it as active, remove active class from others)
198
+ b. **Wait for ALL animations to complete** — add a delay of at least 1000ms after activating the slide, or better: disable all CSS animations/transitions before capturing by injecting `* { animation: none !important; transition: none !important; }`
199
+ c. **Hide all other slides completely** — ensure only the current slide is visible (opacity:1, display:flex) and all others are hidden (opacity:0, display:none). This prevents ghost/residue from previous slides.
200
+ d. Take a full-page screenshot of the viewport at **2x resolution** (deviceScaleFactor: 2) for crisp text
201
+ - Insert each screenshot into PPTX as a **full-slide image** covering the entire slide area (x:0, y:0, w:'100%', h:'100%')
202
+ - Do NOT add margins or padding around the image — it should fill the entire slide
203
+ 3. Save as `.pptx`
204
+
205
+ **Critical: Preventing ghost slides and small content**
206
+ ```javascript
207
+ // Before each screenshot, inject this CSS to disable animations
208
+ await page.addStyleTag({ content: '*, *::before, *::after { animation: none !important; transition: none !important; animation-delay: 0s !important; }' });
209
+
210
+ // Hide all slides, then show only current
211
+ await page.evaluate((idx) => {
212
+ document.querySelectorAll('.slide').forEach((s, i) => {
213
+ s.style.display = i === idx ? 'flex' : 'none';
214
+ s.style.opacity = i === idx ? '1' : '0';
215
+ s.classList.toggle('active', i === idx);
216
+ });
217
+ }, slideIndex);
218
+
219
+ // Wait for layout to settle
220
+ await new Promise(r => setTimeout(r, 500));
221
+
222
+ // Screenshot at 2x for crisp text
223
+ await page.screenshot({ path: outputPath, type: 'png' });
224
+ ```
195
225
 
196
226
  ### PPTX Step 3: Validate and Deliver
197
- - Open the PPTX to verify content and formatting
227
+ - Verify the PPTX opens correctly and content fills each slide without excess margins
198
228
  - Save as `presentation.pptx` (or user-specified filename)
199
229
  - Offer to also generate the HTML version for preview
200
230
 
@@ -80,17 +80,64 @@ Workflow:
80
80
  - `<img>` → `add_picture()`
81
81
  4. Save as .pptx
82
82
 
83
- ### Method 3: Screenshot-based (Simplest but lowest quality)
83
+ ### Method 3: Screenshot-based (Recommended for visual fidelity)
84
84
  ```bash
85
85
  npm install puppeteer pptxgenjs
86
86
  ```
87
87
 
88
+ This is the recommended method when using make-slide themes, because it preserves the exact visual design including custom fonts, gradients, and complex layouts.
89
+
88
90
  Workflow:
89
91
  1. Generate full HTML presentation (standard mode, no PPTX constraints needed)
90
92
  2. Use Puppeteer to screenshot each slide as high-resolution PNG
91
93
  3. Insert each screenshot as a full-slide image in PPTX
92
- 4. Add text overlays for searchability (optional)
93
- - Note: This produces image-based slides (no editable text) but preserves exact visual design
94
+ 4. Note: This produces image-based slides (no editable text) but preserves exact visual design
95
+
96
+ **Critical implementation details to prevent common issues:**
97
+
98
+ **Problem: Ghost/residue from previous slides appearing in screenshots**
99
+ ```javascript
100
+ // BEFORE capturing, disable ALL animations and transitions
101
+ await page.addStyleTag({
102
+ content: '*, *::before, *::after { animation: none !important; transition: none !important; animation-delay: 0s !important; opacity: 1 !important; }'
103
+ });
104
+
105
+ // For each slide, hide ALL others completely
106
+ await page.evaluate((idx) => {
107
+ document.querySelectorAll('.slide').forEach((s, i) => {
108
+ if (i === idx) {
109
+ s.style.display = 'flex';
110
+ s.style.opacity = '1';
111
+ s.style.visibility = 'visible';
112
+ s.classList.add('active');
113
+ } else {
114
+ s.style.display = 'none';
115
+ s.style.opacity = '0';
116
+ s.style.visibility = 'hidden';
117
+ s.classList.remove('active');
118
+ }
119
+ });
120
+ }, slideIndex);
121
+
122
+ // Wait for layout to fully settle
123
+ await new Promise(r => setTimeout(r, 500));
124
+ ```
125
+
126
+ **Problem: Content appears too small with large margins in PPTX**
127
+ ```javascript
128
+ // Set viewport to exact 16:9 dimensions
129
+ await page.setViewport({ width: 1280, height: 720, deviceScaleFactor: 2 });
130
+
131
+ // Screenshot the full viewport
132
+ const screenshot = await page.screenshot({ type: 'png' });
133
+
134
+ // Insert as FULL-SLIDE image — no margins
135
+ slide.addImage({
136
+ data: `image/png;base64,${screenshot.toString('base64')}`,
137
+ x: 0, y: 0, w: '100%', h: '100%'
138
+ });
139
+ ```
140
+ Do NOT use percentage-based sizing with margins. The image must cover x:0, y:0 to w:100%, h:100%.
94
141
 
95
142
  ## Design Principles for PPTX
96
143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-slide",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "AI presentation skill — generate single-file HTML slides with 10 themes",
5
5
  "bin": {
6
6
  "make-slide": "./bin/cli.js"