Genspark Slides Exporter

Export Genspark slides to PDF without upgrading

Size

11.3 KB

Version

1.0.0

Created

Dec 21, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		Genspark Slides Exporter
3// @description		Export Genspark slides to PDF without upgrading
4// @version		1.0.0
5// @match		https://*.genspark.ai/*
6// @icon		https://www.genspark.ai/favicon.ico
7// @require		https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js
8// @require		https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js
9// ==/UserScript==
10(function() {
11    'use strict';
12
13    console.log('Genspark Slides Exporter loaded');
14
15    // Wait for page to load
16    function waitForElement(selector, timeout = 10000) {
17        return new Promise((resolve, reject) => {
18            if (document.querySelector(selector)) {
19                return resolve(document.querySelector(selector));
20            }
21
22            const observer = new MutationObserver(() => {
23                if (document.querySelector(selector)) {
24                    observer.disconnect();
25                    resolve(document.querySelector(selector));
26                }
27            });
28
29            observer.observe(document.body, {
30                childList: true,
31                subtree: true
32            });
33
34            setTimeout(() => {
35                observer.disconnect();
36                reject(new Error('Timeout waiting for element: ' + selector));
37            }, timeout);
38        });
39    }
40
41    // Export slides to PDF
42    async function exportSlidesToPDF() {
43        try {
44            console.log('Starting export process...');
45            
46            // Show progress indicator
47            const progressDiv = document.createElement('div');
48            progressDiv.id = 'export-progress';
49            progressDiv.style.cssText = `
50                position: fixed;
51                top: 50%;
52                left: 50%;
53                transform: translate(-50%, -50%);
54                background: white;
55                padding: 30px;
56                border-radius: 10px;
57                box-shadow: 0 4px 20px rgba(0,0,0,0.3);
58                z-index: 999999;
59                text-align: center;
60                min-width: 300px;
61            `;
62            progressDiv.innerHTML = `
63                <h3 style="margin: 0 0 15px 0; color: #333;">Exporting Slides</h3>
64                <div style="color: #666; margin-bottom: 10px;">Processing slide <span id="current-slide">0</span> of <span id="total-slides">0</span></div>
65                <div style="background: #f0f0f0; height: 20px; border-radius: 10px; overflow: hidden;">
66                    <div id="progress-bar" style="background: linear-gradient(90deg, #4CAF50, #45a049); height: 100%; width: 0%; transition: width 0.3s;"></div>
67                </div>
68            `;
69            document.body.appendChild(progressDiv);
70
71            // Get all slide elements
72            const slideElements = document.querySelectorAll('.slide-content');
73            console.log(`Found ${slideElements.length} slides`);
74            
75            if (slideElements.length === 0) {
76                alert('No slides found on this page. Please make sure you are on the slides view.');
77                progressDiv.remove();
78                return;
79            }
80
81            document.getElementById('total-slides').textContent = slideElements.length;
82
83            // Initialize jsPDF
84            const { jsPDF } = window.jspdf;
85            const pdf = new jsPDF({
86                orientation: 'landscape',
87                unit: 'px',
88                format: [1920, 1080]
89            });
90
91            let isFirstPage = true;
92
93            // Process each slide
94            for (let i = 0; i < slideElements.length; i++) {
95                const slide = slideElements[i];
96                
97                // Update progress
98                document.getElementById('current-slide').textContent = i + 1;
99                document.getElementById('progress-bar').style.width = ((i + 1) / slideElements.length * 100) + '%';
100                
101                console.log(`Processing slide ${i + 1}/${slideElements.length}`);
102
103                // Scroll slide into view
104                slide.scrollIntoView({ behavior: 'instant', block: 'center' });
105                
106                // Wait a bit for iframe content to load
107                await new Promise(resolve => setTimeout(resolve, 500));
108
109                try {
110                    // Try to capture the iframe content first
111                    const iframe = slide.querySelector('iframe');
112                    let elementToCapture = slide;
113                    
114                    if (iframe && iframe.contentDocument && iframe.contentDocument.body) {
115                        // If we can access iframe content, capture it
116                        elementToCapture = iframe.contentDocument.body;
117                        console.log('Capturing iframe content for slide', i + 1);
118                    } else {
119                        // Otherwise capture the whole slide container
120                        console.log('Capturing slide container for slide', i + 1);
121                    }
122
123                    // Capture the slide using html2canvas
124                    const canvas = await html2canvas(elementToCapture, {
125                        scale: 2,
126                        useCORS: true,
127                        allowTaint: true,
128                        backgroundColor: '#ffffff',
129                        logging: false
130                    });
131
132                    // Add to PDF
133                    const imgData = canvas.toDataURL('image/jpeg', 0.95);
134                    
135                    if (!isFirstPage) {
136                        pdf.addPage();
137                    }
138                    isFirstPage = false;
139
140                    // Calculate dimensions to fit the page
141                    const pdfWidth = pdf.internal.pageSize.getWidth();
142                    const pdfHeight = pdf.internal.pageSize.getHeight();
143                    const imgWidth = canvas.width;
144                    const imgHeight = canvas.height;
145                    
146                    const ratio = Math.min(pdfWidth / imgWidth, pdfHeight / imgHeight);
147                    const width = imgWidth * ratio;
148                    const height = imgHeight * ratio;
149                    const x = (pdfWidth - width) / 2;
150                    const y = (pdfHeight - height) / 2;
151
152                    pdf.addImage(imgData, 'JPEG', x, y, width, height);
153                    
154                } catch (error) {
155                    console.error(`Error capturing slide ${i + 1}:`, error);
156                }
157            }
158
159            // Save the PDF
160            const projectId = new URLSearchParams(window.location.search).get('project_id') || 'slides';
161            const filename = `genspark-slides-${projectId}-${Date.now()}.pdf`;
162            pdf.save(filename);
163            
164            console.log('Export completed successfully!');
165            progressDiv.remove();
166            
167            // Show success message
168            const successDiv = document.createElement('div');
169            successDiv.style.cssText = `
170                position: fixed;
171                top: 20px;
172                right: 20px;
173                background: #4CAF50;
174                color: white;
175                padding: 15px 25px;
176                border-radius: 5px;
177                box-shadow: 0 2px 10px rgba(0,0,0,0.2);
178                z-index: 999999;
179                font-weight: bold;
180            `;
181            successDiv.textContent = '✓ Slides exported successfully!';
182            document.body.appendChild(successDiv);
183            
184            setTimeout(() => successDiv.remove(), 3000);
185            
186        } catch (error) {
187            console.error('Export error:', error);
188            alert('Error exporting slides: ' + error.message);
189            const progressDiv = document.getElementById('export-progress');
190            if (progressDiv) progressDiv.remove();
191        }
192    }
193
194    // Add export button to the page
195    async function addExportButton() {
196        try {
197            // Wait for the slides container to load
198            await waitForElement('.keynote-main');
199            
200            console.log('Adding export button...');
201
202            // Find the export button area
203            const exportButtonArea = document.querySelector('.button-play-slides');
204            
205            if (!exportButtonArea) {
206                console.log('Export button area not found, creating custom button');
207                // Create a floating button if we can't find the export area
208                const floatingBtn = document.createElement('button');
209                floatingBtn.textContent = '📥 Export to PDF (Free)';
210                floatingBtn.style.cssText = `
211                    position: fixed;
212                    bottom: 30px;
213                    right: 30px;
214                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
215                    color: white;
216                    border: none;
217                    padding: 15px 25px;
218                    border-radius: 8px;
219                    font-size: 16px;
220                    font-weight: bold;
221                    cursor: pointer;
222                    box-shadow: 0 4px 15px rgba(0,0,0,0.2);
223                    z-index: 999998;
224                    transition: transform 0.2s, box-shadow 0.2s;
225                `;
226                floatingBtn.onmouseover = () => {
227                    floatingBtn.style.transform = 'translateY(-2px)';
228                    floatingBtn.style.boxShadow = '0 6px 20px rgba(0,0,0,0.3)';
229                };
230                floatingBtn.onmouseout = () => {
231                    floatingBtn.style.transform = 'translateY(0)';
232                    floatingBtn.style.boxShadow = '0 4px 15px rgba(0,0,0,0.2)';
233                };
234                floatingBtn.onclick = exportSlidesToPDF;
235                document.body.appendChild(floatingBtn);
236                console.log('Floating export button added');
237                return;
238            }
239
240            // Clone the existing export button and modify it
241            const freeExportBtn = exportButtonArea.cloneNode(true);
242            freeExportBtn.querySelector('.play-slides-text').textContent = 'Export (Free)';
243            
244            // Remove the Plus badge
245            const plusBadge = freeExportBtn.querySelector('.plus-badge');
246            if (plusBadge) plusBadge.remove();
247            
248            // Style the button
249            freeExportBtn.style.cssText = `
250                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
251                cursor: pointer;
252                margin-left: 10px;
253            `;
254            
255            // Add click handler
256            freeExportBtn.onclick = (e) => {
257                e.preventDefault();
258                e.stopPropagation();
259                exportSlidesToPDF();
260            };
261            
262            // Insert the button next to the original export button
263            exportButtonArea.parentElement.insertBefore(freeExportBtn, exportButtonArea.nextSibling);
264            
265            console.log('Export button added successfully');
266            
267        } catch (error) {
268            console.error('Error adding export button:', error);
269        }
270    }
271
272    // Initialize
273    function init() {
274        console.log('Initializing Genspark Slides Exporter...');
275        
276        // Check if we're on the slides page
277        if (window.location.pathname.includes('/slides')) {
278            addExportButton();
279        }
280    }
281
282    // Run when page loads
283    if (document.readyState === 'loading') {
284        document.addEventListener('DOMContentLoaded', init);
285    } else {
286        init();
287    }
288
289})();
Genspark Slides Exporter | Robomonkey