Browser Extension Builder

Interactive tool to create browser extensions for Chrome and Edge

Size

16.9 KB

Version

1.0.1

Created

Dec 19, 2025

Updated

about 2 months ago

1// ==UserScript==
2// @name		Browser Extension Builder
3// @description		Interactive tool to create browser extensions for Chrome and Edge
4// @version		1.0.1
5// @match		https://*.stevefenton.co.uk/*
6// @icon		https://stevefenton.co.uk/icons/favicon.ico
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('Browser Extension Builder initialized');
12
13    // Debounce function for performance
14    function debounce(func, wait) {
15        let timeout;
16        return function executedFunction(...args) {
17            const later = () => {
18                clearTimeout(timeout);
19                func(...args);
20            };
21            clearTimeout(timeout);
22            timeout = setTimeout(later, wait);
23        };
24    }
25
26    // Create the builder UI
27    function createBuilderUI() {
28        console.log('Creating builder UI');
29
30        // Check if UI already exists
31        if (document.getElementById('extension-builder-panel')) {
32            console.log('Builder UI already exists');
33            return;
34        }
35
36        // Create main panel
37        const panel = document.createElement('div');
38        panel.id = 'extension-builder-panel';
39        panel.innerHTML = `
40            <div class="builder-header">
41                <h3>🔧 Extension Builder</h3>
42                <button class="close-btn" id="close-builder">✕</button>
43            </div>
44            <div class="builder-content">
45                <div class="form-section">
46                    <label for="ext-name">Extension Name:</label>
47                    <input type="text" id="ext-name" placeholder="My Awesome Extension" value="My Extension">
48                </div>
49                
50                <div class="form-section">
51                    <label for="ext-version">Version:</label>
52                    <input type="text" id="ext-version" placeholder="1.0.0" value="1.0.0">
53                </div>
54                
55                <div class="form-section">
56                    <label for="ext-description">Description:</label>
57                    <textarea id="ext-description" placeholder="What does your extension do?" rows="2">A helpful browser extension</textarea>
58                </div>
59                
60                <div class="form-section">
61                    <label for="ext-match">Match URL Pattern:</label>
62                    <input type="text" id="ext-match" placeholder="https://*/*" value="https://*/*">
63                    <small>Which websites should this extension work on?</small>
64                </div>
65                
66                <div class="form-section">
67                    <label for="ext-permissions">Permissions (comma-separated):</label>
68                    <input type="text" id="ext-permissions" placeholder="activeTab, scripting" value="activeTab, scripting">
69                </div>
70                
71                <div class="form-section">
72                    <label for="ext-code">Extension Code (JavaScript):</label>
73                    <textarea id="ext-code" placeholder="Your extension code here..." rows="8">// Your extension code
74console.log('Extension loaded!');
75
76// Example: Change background color
77document.body.style.backgroundColor = '#f0f0f0';</textarea>
78                </div>
79                
80                <div class="button-group">
81                    <button class="generate-btn" id="generate-extension">Generate Extension Files</button>
82                </div>
83                
84                <div class="output-section" id="output-section" style="display: none;">
85                    <h4>Generated Files:</h4>
86                    <div class="file-output">
87                        <div class="file-header">
88                            <strong>manifest.json</strong>
89                            <button class="copy-file-btn" data-file="manifest">Copy</button>
90                        </div>
91                        <pre id="manifest-output"></pre>
92                    </div>
93                    
94                    <div class="file-output">
95                        <div class="file-header">
96                            <strong>worker.js</strong>
97                            <button class="copy-file-btn" data-file="worker">Copy</button>
98                        </div>
99                        <pre id="worker-output"></pre>
100                    </div>
101                    
102                    <div class="instructions">
103                        <h4>📦 Next Steps:</h4>
104                        <ol>
105                            <li>Create a new folder for your extension</li>
106                            <li>Save the manifest.json file</li>
107                            <li>Save the worker.js file</li>
108                            <li>Add a 128x128 pixel icon named "icon-128.png"</li>
109                            <li>Open Chrome/Edge and go to Extensions</li>
110                            <li>Enable "Developer mode"</li>
111                            <li>Click "Load unpacked" and select your folder</li>
112                        </ol>
113                    </div>
114                    
115                    <button class="download-btn" id="download-all">Download All Files as ZIP</button>
116                </div>
117            </div>
118        `;
119
120        // Add styles
121        const styles = `
122            #extension-builder-panel {
123                position: fixed;
124                top: 20px;
125                right: 20px;
126                width: 450px;
127                max-height: 90vh;
128                background: white;
129                border: 2px solid #007bff;
130                border-radius: 12px;
131                box-shadow: 0 8px 32px rgba(0,0,0,0.2);
132                z-index: 999999;
133                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
134                overflow: hidden;
135                display: flex;
136                flex-direction: column;
137            }
138            
139            .builder-header {
140                background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
141                color: white;
142                padding: 16px 20px;
143                display: flex;
144                justify-content: space-between;
145                align-items: center;
146            }
147            
148            .builder-header h3 {
149                margin: 0;
150                font-size: 18px;
151                font-weight: 600;
152            }
153            
154            .close-btn {
155                background: rgba(255,255,255,0.2);
156                border: none;
157                color: white;
158                width: 28px;
159                height: 28px;
160                border-radius: 50%;
161                cursor: pointer;
162                font-size: 18px;
163                display: flex;
164                align-items: center;
165                justify-content: center;
166                transition: background 0.2s;
167            }
168            
169            .close-btn:hover {
170                background: rgba(255,255,255,0.3);
171            }
172            
173            .builder-content {
174                padding: 20px;
175                overflow-y: auto;
176                flex: 1;
177            }
178            
179            .form-section {
180                margin-bottom: 16px;
181            }
182            
183            .form-section label {
184                display: block;
185                margin-bottom: 6px;
186                font-weight: 600;
187                color: #333;
188                font-size: 13px;
189            }
190            
191            .form-section input,
192            .form-section textarea {
193                width: 100%;
194                padding: 10px;
195                border: 2px solid #e0e0e0;
196                border-radius: 6px;
197                font-size: 13px;
198                font-family: inherit;
199                box-sizing: border-box;
200                transition: border-color 0.2s;
201            }
202            
203            .form-section input:focus,
204            .form-section textarea:focus {
205                outline: none;
206                border-color: #007bff;
207            }
208            
209            .form-section textarea {
210                resize: vertical;
211                font-family: 'Courier New', monospace;
212            }
213            
214            .form-section small {
215                display: block;
216                margin-top: 4px;
217                color: #666;
218                font-size: 11px;
219            }
220            
221            .button-group {
222                margin: 20px 0;
223            }
224            
225            .generate-btn,
226            .download-btn {
227                width: 100%;
228                padding: 12px;
229                background: linear-gradient(135deg, #28a745 0%, #20873a 100%);
230                color: white;
231                border: none;
232                border-radius: 6px;
233                font-size: 14px;
234                font-weight: 600;
235                cursor: pointer;
236                transition: transform 0.2s, box-shadow 0.2s;
237            }
238            
239            .generate-btn:hover,
240            .download-btn:hover {
241                transform: translateY(-2px);
242                box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
243            }
244            
245            .download-btn {
246                background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
247                margin-top: 16px;
248            }
249            
250            .download-btn:hover {
251                box-shadow: 0 4px 12px rgba(0, 123, 255, 0.3);
252            }
253            
254            .output-section {
255                margin-top: 20px;
256                padding-top: 20px;
257                border-top: 2px solid #e0e0e0;
258            }
259            
260            .output-section h4 {
261                margin: 0 0 16px 0;
262                color: #333;
263                font-size: 15px;
264            }
265            
266            .file-output {
267                margin-bottom: 16px;
268                background: #f8f9fa;
269                border-radius: 6px;
270                overflow: hidden;
271            }
272            
273            .file-header {
274                display: flex;
275                justify-content: space-between;
276                align-items: center;
277                padding: 10px 12px;
278                background: #e9ecef;
279                border-bottom: 1px solid #dee2e6;
280            }
281            
282            .file-header strong {
283                color: #495057;
284                font-size: 13px;
285            }
286            
287            .copy-file-btn {
288                padding: 4px 12px;
289                background: #007bff;
290                color: white;
291                border: none;
292                border-radius: 4px;
293                font-size: 11px;
294                cursor: pointer;
295                transition: background 0.2s;
296            }
297            
298            .copy-file-btn:hover {
299                background: #0056b3;
300            }
301            
302            .file-output pre {
303                margin: 0;
304                padding: 12px;
305                background: #f8f9fa;
306                overflow-x: auto;
307                font-size: 11px;
308                line-height: 1.5;
309                color: #212529;
310            }
311            
312            .instructions {
313                background: #e7f3ff;
314                border-left: 4px solid #007bff;
315                padding: 16px;
316                border-radius: 6px;
317                margin: 20px 0;
318            }
319            
320            .instructions h4 {
321                margin: 0 0 12px 0;
322                color: #0056b3;
323                font-size: 14px;
324            }
325            
326            .instructions ol {
327                margin: 0;
328                padding-left: 20px;
329            }
330            
331            .instructions li {
332                margin-bottom: 6px;
333                color: #333;
334                font-size: 12px;
335                line-height: 1.5;
336            }
337            
338            #builder-toggle-btn {
339                position: fixed;
340                bottom: 20px;
341                right: 20px;
342                padding: 14px 20px;
343                background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
344                color: white;
345                border: none;
346                border-radius: 50px;
347                font-size: 14px;
348                font-weight: 600;
349                cursor: pointer;
350                box-shadow: 0 4px 16px rgba(0,0,0,0.2);
351                z-index: 999998;
352                transition: transform 0.2s, box-shadow 0.2s;
353            }
354            
355            #builder-toggle-btn:hover {
356                transform: translateY(-2px);
357                box-shadow: 0 6px 20px rgba(0,0,0,0.3);
358            }
359        `;
360
361        const styleSheet = document.createElement('style');
362        styleSheet.textContent = styles;
363        document.head.appendChild(styleSheet);
364
365        document.body.appendChild(panel);
366
367        // Add event listeners
368        document.getElementById('close-builder').addEventListener('click', () => {
369            panel.style.display = 'none';
370        });
371
372        document.getElementById('generate-extension').addEventListener('click', generateExtension);
373
374        // Add copy button listeners
375        document.querySelectorAll('.copy-file-btn').forEach(btn => {
376            btn.addEventListener('click', (e) => {
377                const fileType = e.target.getAttribute('data-file');
378                const outputId = fileType === 'manifest' ? 'manifest-output' : 'worker-output';
379                const content = document.getElementById(outputId).textContent;
380                
381                navigator.clipboard.writeText(content).then(() => {
382                    const originalText = e.target.textContent;
383                    e.target.textContent = '✓ Copied!';
384                    setTimeout(() => {
385                        e.target.textContent = originalText;
386                    }, 2000);
387                });
388            });
389        });
390
391        console.log('Builder UI created successfully');
392    }
393
394    // Generate extension files
395    function generateExtension() {
396        console.log('Generating extension files');
397
398        const name = document.getElementById('ext-name').value.trim();
399        const version = document.getElementById('ext-version').value.trim();
400        const description = document.getElementById('ext-description').value.trim();
401        const matchPattern = document.getElementById('ext-match').value.trim();
402        const permissionsInput = document.getElementById('ext-permissions').value.trim();
403        const code = document.getElementById('ext-code').value;
404
405        // Parse permissions
406        const permissions = permissionsInput.split(',').map(p => p.trim()).filter(p => p);
407
408        // Generate manifest.json
409        const manifest = {
410            "name": name,
411            "version": version,
412            "description": description,
413            "action": {},
414            "manifest_version": 3,
415            "icons": {
416                "128": "icon-128.png"
417            },
418            "background": {
419                "service_worker": "worker.js"
420            },
421            "permissions": permissions,
422            "host_permissions": [matchPattern]
423        };
424
425        // Generate worker.js
426        const worker = `// ${name} - Background Service Worker
427// Generated by Browser Extension Builder
428
429console.log('${name} extension loaded');
430
431// Listen for extension icon click
432chrome.action.onClicked.addListener((tab) => {
433    console.log('Extension icon clicked on tab:', tab.id);
434    
435    // Execute the extension code on the active tab
436    chrome.scripting.executeScript({
437        target: { tabId: tab.id },
438        func: extensionCode
439    });
440});
441
442// The main extension code
443function extensionCode() {
444${code.split('\n').map(line => '    ' + line).join('\n')}
445}
446`;
447
448        // Display the generated files
449        document.getElementById('manifest-output').textContent = JSON.stringify(manifest, null, 2);
450        document.getElementById('worker-output').textContent = worker;
451        document.getElementById('output-section').style.display = 'block';
452
453        // Scroll to output
454        document.getElementById('output-section').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
455
456        console.log('Extension files generated successfully');
457    }
458
459    // Create toggle button
460    function createToggleButton() {
461        if (document.getElementById('builder-toggle-btn')) {
462            return;
463        }
464
465        const toggleBtn = document.createElement('button');
466        toggleBtn.id = 'builder-toggle-btn';
467        toggleBtn.textContent = '🔧 Build Extension';
468        toggleBtn.addEventListener('click', () => {
469            const panel = document.getElementById('extension-builder-panel');
470            if (panel) {
471                panel.style.display = panel.style.display === 'none' ? 'flex' : 'none';
472            } else {
473                createBuilderUI();
474            }
475        });
476        document.body.appendChild(toggleBtn);
477    }
478
479    // Initialize when DOM is ready
480    function init() {
481        console.log('Initializing Browser Extension Builder');
482        
483        if (document.body) {
484            createToggleButton();
485            console.log('Toggle button created');
486        } else {
487            console.log('Body not ready, waiting...');
488            setTimeout(init, 100);
489        }
490    }
491
492    // Start the extension
493    if (document.readyState === 'loading') {
494        document.addEventListener('DOMContentLoaded', init);
495    } else {
496        init();
497    }
498
499})();
Browser Extension Builder | Robomonkey