make-slide 2.1.0 → 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.
@@ -74,11 +74,13 @@ Follow these steps in order:
74
74
  - 30-min talk → 30–40 slides
75
75
  - Identify the target audience and tone (technical, business, casual, academic)
76
76
  - Detect the user's language from the conversation — generate content in that language
77
- - Detect or ask about the desired output format:
78
- - **HTML** (default) Interactive single-file presentation with navigation, animations, and speaker notes
79
- - **PPTX** — PowerPoint file for traditional presentation software (Office, Google Slides, Keynote)
80
- - If the user mentions "PowerPoint", "pptx", "PPT", ".pptx", "Google Slides", or "Keynote" → use PPTX mode
81
- - If unclear, default to HTML and mention PPTX is also available
77
+ - Ask the user about the desired output format (always ask, do not skip):
78
+ > **Which output format would you like?**
79
+ > - **HTML** — Interactive single-file presentation you can open in a browser (navigation, animations, speaker notes built-in)
80
+ > - **PPTX** PowerPoint file for Office, Google Slides, or Keynote
81
+
82
+ - If the user already mentioned "PowerPoint", "pptx", "PPT", ".pptx", "Google Slides", or "Keynote" in their request → skip the question and use PPTX mode
83
+ - Otherwise, always ask explicitly before proceeding
82
84
 
83
85
  ### Step 2: Choose a Theme
84
86
  Present the user with the theme gallery link for browsing:
@@ -109,12 +111,16 @@ Ask the user which image approach they prefer:
109
111
 
110
112
  - **Option A** → Use CSS placeholders matching the theme (emoji, SVG icons, CSS shapes)
111
113
  - **Option B** → Mark image positions in the outline, ask user for URLs, use `<img src>` with `loading="lazy"` and descriptive `alt` text
112
- - **Option C** → For each slide that would benefit from an image:
113
- 1. Determine a relevant search query based on the slide content
114
- 2. Search for images using web search (prefer Unsplash, Pexels, or other royalty-free sources)
115
- 3. Select the most relevant, high-quality result
116
- 4. Insert as `<img src="URL" alt="description" loading="lazy">`
117
- 5. Note: Inform the user that auto-searched images are from the web and they should verify licensing for commercial use
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).
118
124
 
119
125
  ### Step 4: Generate Outline
120
126
  Create a slide-by-slide outline with:
@@ -156,6 +162,8 @@ Add speaker notes as `data-notes` attributes on each slide's `<div>`:
156
162
  - Expand on the slide text — don't just repeat it
157
163
  - Include transitions between slides (e.g., "Now let's move on to...")
158
164
 
165
+ **Speaker Notes Panel must be a separate popup window** using `window.open()`. Do NOT render notes inline at the bottom of the slide — this breaks the slide layout. The `S` key should toggle a popup window that shows the current slide's notes and auto-updates on slide change. See `references/html-spec.md` for the implementation pattern.
166
+
159
167
  ### Step 8: Generate Script (Mode A and B only)
160
168
  For Mode A and B, also generate a separate `script.md` file containing:
161
169
  - Full speaking script organized by slide
@@ -168,13 +176,8 @@ For Mode C, the user already has a script — skip this step.
168
176
  ### Step 9: Save and Deliver
169
177
  - Save the presentation as `index.html` (or user-specified filename)
170
178
  - Save the script as `script.md` (if generated)
171
- - Offer to start a local server for preview:
172
- ```bash
173
- # Python
174
- python -m http.server 8000
175
- # Node.js
176
- npx serve .
177
- ```
179
+ - Tell the user they can open `index.html` directly in their browser to view the presentation — no server needed
180
+ - The file is fully self-contained (all CSS/JS inlined), so it works by simply double-clicking the file or dragging it into a browser
178
181
 
179
182
  ---
180
183
 
@@ -186,13 +189,42 @@ When the user selects PPTX output, follow this modified workflow:
186
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.
187
190
 
188
191
  ### PPTX Step 2: Convert HTML to PPTX
189
- Use the html2pptx conversion pipeline:
190
- 1. Install dependencies if not present: `npm install pptxgenjs sharp puppeteer`
191
- 2. For each HTML slide, render and convert to PPTX using PptxGenJS
192
- 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
+ ```
193
225
 
194
226
  ### PPTX Step 3: Validate and Deliver
195
- - Open the PPTX to verify content and formatting
227
+ - Verify the PPTX opens correctly and content fills each slide without excess margins
196
228
  - Save as `presentation.pptx` (or user-specified filename)
197
229
  - Offer to also generate the HTML version for preview
198
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.0",
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"