Rork Project Code Exporter

AI-powered tool to export all project code files into a zip file in chronological order

Size

14.1 KB

Version

1.1.1

Created

Feb 4, 2026

Updated

13 days ago

1// ==UserScript==
2// @name		Rork Project Code Exporter
3// @description		AI-powered tool to export all project code files into a zip file in chronological order
4// @version		1.1.1
5// @match		https://*.rork.com/*
6// @icon		https://rork.com/favicon.ico
7// @require		https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    console.log('Rork Project Code Exporter initialized');
13
14    // Utility function to create a debounced function
15    function debounce(func, wait) {
16        let timeout;
17        return function executedFunction(...args) {
18            const later = () => {
19                clearTimeout(timeout);
20                func(...args);
21            };
22            clearTimeout(timeout);
23            timeout = setTimeout(later, wait);
24        };
25    }
26
27    // Function to show loading indicator
28    function showLoadingIndicator(message = 'Processing...') {
29        let loader = document.getElementById('rork-export-loader');
30        if (!loader) {
31            loader = document.createElement('div');
32            loader.id = 'rork-export-loader';
33            loader.style.cssText = `
34                position: fixed;
35                top: 20px;
36                right: 20px;
37                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
38                color: white;
39                padding: 16px 24px;
40                border-radius: 12px;
41                z-index: 100000;
42                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
43                font-size: 14px;
44                font-weight: 500;
45                box-shadow: 0 10px 40px rgba(0,0,0,0.3);
46                display: flex;
47                align-items: center;
48                gap: 12px;
49            `;
50            document.body.appendChild(loader);
51        }
52        loader.innerHTML = `
53            <div style="width: 20px; height: 20px; border: 3px solid rgba(255,255,255,0.3); border-top-color: white; border-radius: 50%; animation: spin 1s linear infinite;"></div>
54            <span>${message}</span>
55        `;
56        
57        // Add animation
58        if (!document.getElementById('rork-export-styles')) {
59            const style = document.createElement('style');
60            style.id = 'rork-export-styles';
61            style.textContent = `
62                @keyframes spin {
63                    to { transform: rotate(360deg); }
64                }
65            `;
66            document.head.appendChild(style);
67        }
68    }
69
70    // Function to hide loading indicator
71    function hideLoadingIndicator() {
72        const loader = document.getElementById('rork-export-loader');
73        if (loader) {
74            loader.remove();
75        }
76    }
77
78    // Function to show success message
79    function showSuccessMessage(message) {
80        const successDiv = document.createElement('div');
81        successDiv.style.cssText = `
82            position: fixed;
83            top: 20px;
84            right: 20px;
85            background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
86            color: white;
87            padding: 16px 24px;
88            border-radius: 12px;
89            z-index: 100000;
90            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
91            font-size: 14px;
92            font-weight: 500;
93            box-shadow: 0 10px 40px rgba(0,0,0,0.3);
94        `;
95        successDiv.textContent = message;
96        document.body.appendChild(successDiv);
97        
98        setTimeout(() => {
99            successDiv.remove();
100        }, 3000);
101    }
102
103    // Function to show error message
104    function showErrorMessage(message) {
105        const errorDiv = document.createElement('div');
106        errorDiv.style.cssText = `
107            position: fixed;
108            top: 20px;
109            right: 20px;
110            background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
111            color: white;
112            padding: 16px 24px;
113            border-radius: 12px;
114            z-index: 100000;
115            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
116            font-size: 14px;
117            font-weight: 500;
118            box-shadow: 0 10px 40px rgba(0,0,0,0.3);
119        `;
120        errorDiv.textContent = message;
121        document.body.appendChild(errorDiv);
122        
123        setTimeout(() => {
124            errorDiv.remove();
125        }, 5000);
126    }
127
128    // Function to extract all code from the page using AI
129    async function extractCodeFilesWithAI() {
130        try {
131            showLoadingIndicator('AI is analyzing the page...');
132            
133            // Get all text content from the page
134            const pageContent = document.body.innerText;
135            
136            // Get project ID from URL
137            const projectId = window.location.pathname.split('/').pop();
138            
139            console.log('Extracting code files from project:', projectId);
140            console.log('Page content length:', pageContent.length);
141            
142            // Use AI to analyze and extract file structure with more comprehensive prompt
143            const prompt = `You are analyzing a Rork project page. Your task is to extract EVERY SINGLE code file visible on this page.
144
145CRITICAL INSTRUCTIONS:
146- Extract ALL files you can find - do not skip any files
147- Include the COMPLETE content of each file - do not truncate or summarize
148- Look for file names, code blocks, file tabs, file lists, or any indication of files
149- Include configuration files, source code files, data files, everything
150- Preserve all code exactly as shown including whitespace, indentation, and formatting
151- If you see partial content, include what you can see
152- Look for files in sidebars, tabs, panels, modals, or any UI element
153
154Page content (first 10000 characters):
155${pageContent.substring(0, 10000)}
156
157${pageContent.length > 10000 ? `\n\nPage content (next 10000 characters):\n${pageContent.substring(10000, 20000)}` : ''}
158
159${pageContent.length > 20000 ? `\n\nPage content (remaining):\n${pageContent.substring(20000, 30000)}` : ''}
160
161Extract EVERY file with:
1621. Exact file name (with extension)
1632. Complete file content (all code, no truncation)
1643. File type/extension
1654. Any timestamp or order information if visible
166
167Return ALL files you find - missing files is not acceptable.`;
168
169            showLoadingIndicator('AI is extracting ALL files...');
170            
171            const fileStructure = await RM.aiCall(prompt, {
172                type: 'json_schema',
173                json_schema: {
174                    name: 'code_files_extraction',
175                    schema: {
176                        type: 'object',
177                        properties: {
178                            projectName: { type: 'string' },
179                            files: {
180                                type: 'array',
181                                items: {
182                                    type: 'object',
183                                    properties: {
184                                        fileName: { type: 'string' },
185                                        content: { type: 'string' },
186                                        fileType: { type: 'string' },
187                                        timestamp: { type: 'string' }
188                                    },
189                                    required: ['fileName', 'content', 'fileType']
190                                }
191                            }
192                        },
193                        required: ['projectName', 'files']
194                    }
195                }
196            });
197
198            console.log('AI extracted files:', fileStructure);
199            console.log('Total files found:', fileStructure.files?.length || 0);
200            
201            if (!fileStructure.files || fileStructure.files.length === 0) {
202                throw new Error('No files found in the project. Please make sure all files are visible on the page.');
203            }
204
205            return fileStructure;
206            
207        } catch (error) {
208            console.error('Error extracting files with AI:', error);
209            throw error;
210        }
211    }
212
213    // Function to create and download zip file
214    async function createAndDownloadZip(fileStructure) {
215        try {
216            showLoadingIndicator('Creating zip file...');
217            
218            const zip = new JSZip();
219            
220            // Sort files chronologically if timestamp is available
221            const sortedFiles = fileStructure.files.sort((a, b) => {
222                if (a.timestamp && b.timestamp) {
223                    return new Date(a.timestamp) - new Date(b.timestamp);
224                }
225                return 0;
226            });
227
228            // Add files to zip
229            sortedFiles.forEach((file, index) => {
230                const fileName = file.fileName || `file_${index + 1}.${file.fileType}`;
231                console.log('Adding file to zip:', fileName);
232                zip.file(fileName, file.content);
233            });
234
235            // Add a README with file order
236            const readmeContent = `# ${fileStructure.projectName || 'Project'} - Code Export
237
238Files exported in chronological order:
239
240${sortedFiles.map((file, index) => `${index + 1}. ${file.fileName} ${file.timestamp ? `(${file.timestamp})` : ''}`).join('\n')}
241
242Total files: ${sortedFiles.length}
243Export date: ${new Date().toISOString()}
244`;
245            
246            zip.file('README.md', readmeContent);
247
248            showLoadingIndicator('Generating zip file...');
249            
250            // Generate zip file
251            const content = await zip.generateAsync({
252                type: 'blob',
253                compression: 'DEFLATE',
254                compressionOptions: {
255                    level: 9
256                }
257            });
258
259            // Download the zip file
260            const projectName = fileStructure.projectName || 'project';
261            const fileName = `${projectName.replace(/[^a-z0-9]/gi, '_')}_code_export_${Date.now()}.zip`;
262            
263            const url = URL.createObjectURL(content);
264            const a = document.createElement('a');
265            a.href = url;
266            a.download = fileName;
267            document.body.appendChild(a);
268            a.click();
269            document.body.removeChild(a);
270            URL.revokeObjectURL(url);
271
272            hideLoadingIndicator();
273            showSuccessMessage(`✓ Exported ${sortedFiles.length} files successfully!`);
274            
275            console.log('Zip file downloaded:', fileName);
276            
277        } catch (error) {
278            console.error('Error creating zip file:', error);
279            hideLoadingIndicator();
280            showErrorMessage('Failed to create zip file');
281            throw error;
282        }
283    }
284
285    // Main export function
286    async function exportProjectCode() {
287        try {
288            console.log('Starting project code export...');
289            
290            // Extract files using AI
291            const fileStructure = await extractCodeFilesWithAI();
292            
293            // Create and download zip
294            await createAndDownloadZip(fileStructure);
295            
296        } catch (error) {
297            console.error('Export failed:', error);
298            hideLoadingIndicator();
299            showErrorMessage('Export failed: ' + error.message);
300        }
301    }
302
303    // Create export button
304    function createExportButton() {
305        // Check if button already exists
306        if (document.getElementById('rork-export-button')) {
307            return;
308        }
309
310        const button = document.createElement('button');
311        button.id = 'rork-export-button';
312        button.innerHTML = `
313            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
314                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
315                <polyline points="7 10 12 15 17 10"></polyline>
316                <line x1="12" y1="15" x2="12" y2="3"></line>
317            </svg>
318            <span>Export Code</span>
319        `;
320        button.style.cssText = `
321            position: fixed;
322            bottom: 30px;
323            right: 30px;
324            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
325            color: white;
326            border: none;
327            padding: 14px 24px;
328            border-radius: 12px;
329            cursor: pointer;
330            z-index: 99999;
331            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
332            font-size: 14px;
333            font-weight: 600;
334            box-shadow: 0 8px 32px rgba(102, 126, 234, 0.4);
335            display: flex;
336            align-items: center;
337            gap: 8px;
338            transition: all 0.3s ease;
339        `;
340
341        button.addEventListener('mouseenter', () => {
342            button.style.transform = 'translateY(-2px)';
343            button.style.boxShadow = '0 12px 40px rgba(102, 126, 234, 0.5)';
344        });
345
346        button.addEventListener('mouseleave', () => {
347            button.style.transform = 'translateY(0)';
348            button.style.boxShadow = '0 8px 32px rgba(102, 126, 234, 0.4)';
349        });
350
351        button.addEventListener('click', async () => {
352            button.disabled = true;
353            button.style.opacity = '0.7';
354            button.style.cursor = 'not-allowed';
355            
356            await exportProjectCode();
357            
358            button.disabled = false;
359            button.style.opacity = '1';
360            button.style.cursor = 'pointer';
361        });
362
363        document.body.appendChild(button);
364        console.log('Export button created');
365    }
366
367    // Initialize the extension
368    function init() {
369        console.log('Initializing Rork Project Code Exporter...');
370        
371        // Wait for page to be fully loaded
372        if (document.readyState === 'loading') {
373            document.addEventListener('DOMContentLoaded', () => {
374                setTimeout(createExportButton, 1000);
375            });
376        } else {
377            setTimeout(createExportButton, 1000);
378        }
379
380        // Re-create button on navigation (for SPAs)
381        const debouncedCreateButton = debounce(createExportButton, 500);
382        const observer = new MutationObserver(debouncedCreateButton);
383        observer.observe(document.body, {
384            childList: true,
385            subtree: true
386        });
387    }
388
389    // Start the extension
390    init();
391
392})();
Rork Project Code Exporter | Robomonkey