1616 * - Extension assets served from `./output/chrome-mv3-dev` via `/chrome-mv3-dev` route
1717 */
1818import { error } from 'node:console'
19+ import { spawn } from 'node:child_process'
1920import fs from 'node:fs/promises'
2021import path from 'node:path'
2122import { fileURLToPath } from 'node:url'
@@ -27,6 +28,9 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
2728const app = express ( )
2829const PORT = 3001
2930
31+ // Middleware to parse JSON bodies
32+ app . use ( express . json ( ) )
33+
3034// Store HAR json
3135const harCache = new Map < keyof typeof PAGES , Har > ( )
3236
@@ -239,6 +243,63 @@ app.get('/asset/:key/*', async (req, res) => {
239243// Serve extension assets from filesystem
240244app . use ( '/chrome-mv3-dev' , express . static ( path . join ( __dirname , '..' , '.output' , 'chrome-mv3-dev' ) ) )
241245
246+ // Rebuild endpoint
247+ app . post ( '/rebuild' , async ( req , res ) => {
248+ try {
249+ console . log ( 'Rebuild triggered via API' )
250+
251+ // Run npx wxt build --mode development
252+ const buildProcess = spawn ( 'npx' , [ 'wxt' , 'build' , '--mode' , 'development' ] , {
253+ cwd : path . join ( __dirname , '..' ) ,
254+ stdio : [ 'pipe' , 'pipe' , 'pipe' ]
255+ } )
256+
257+ let stdout = ''
258+ let stderr = ''
259+
260+ buildProcess . stdout . on ( 'data' , ( data ) => {
261+ stdout += data . toString ( )
262+ console . log ( '[BUILD]' , data . toString ( ) . trim ( ) )
263+ } )
264+
265+ buildProcess . stderr . on ( 'data' , ( data ) => {
266+ stderr += data . toString ( )
267+ console . error ( '[BUILD ERROR]' , data . toString ( ) . trim ( ) )
268+ } )
269+
270+ buildProcess . on ( 'close' , ( code ) => {
271+ if ( code === 0 ) {
272+ console . log ( 'Build completed successfully' )
273+ res . json ( { success : true , message : 'Build completed successfully' } )
274+ } else {
275+ console . error ( 'Build failed with code:' , code )
276+ res . status ( 500 ) . json ( {
277+ success : false ,
278+ message : 'Build failed' ,
279+ error : stderr || stdout
280+ } )
281+ }
282+ } )
283+
284+ buildProcess . on ( 'error' , ( error ) => {
285+ console . error ( 'Failed to start build process:' , error )
286+ res . status ( 500 ) . json ( {
287+ success : false ,
288+ message : 'Failed to start build process' ,
289+ error : error . message
290+ } )
291+ } )
292+
293+ } catch ( error ) {
294+ console . error ( 'Rebuild endpoint error:' , error )
295+ res . status ( 500 ) . json ( {
296+ success : false ,
297+ message : 'Internal server error' ,
298+ error : error instanceof Error ? error . message : 'Unknown error'
299+ } )
300+ }
301+ } )
302+
242303app . listen ( PORT , ( ) => {
243304 console . log ( `HAR Page Viewer running at http://localhost:${ PORT } ` )
244305 console . log ( 'Click the links to view recorded pages' )
@@ -296,6 +357,90 @@ function injectGitcassoScript(key: keyof typeof PAGES, html: string) {
296357 .catch(error => {
297358 console.error('Failed to load and patch content script:', error);
298359 });
360+
361+ // Create floating rebuild button
362+ const rebuildButton = document.createElement('div');
363+ rebuildButton.id = 'gitcasso-rebuild-btn';
364+ rebuildButton.innerHTML = '🔄';
365+ rebuildButton.title = 'Rebuild Extension';
366+ rebuildButton.style.cssText = \`
367+ position: fixed;
368+ top: 20px;
369+ right: 20px;
370+ width: 50px;
371+ height: 50px;
372+ background: #007acc;
373+ color: white;
374+ border-radius: 50%;
375+ display: flex;
376+ align-items: center;
377+ justify-content: center;
378+ font-size: 20px;
379+ cursor: pointer;
380+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
381+ z-index: 999999;
382+ user-select: none;
383+ transition: all 0.2s ease;
384+ font-family: system-ui, -apple-system, sans-serif;
385+ \`;
386+
387+ rebuildButton.addEventListener('mouseenter', () => {
388+ rebuildButton.style.transform = 'scale(1.1)';
389+ rebuildButton.style.backgroundColor = '#005a9e';
390+ });
391+
392+ rebuildButton.addEventListener('mouseleave', () => {
393+ rebuildButton.style.transform = 'scale(1)';
394+ rebuildButton.style.backgroundColor = '#007acc';
395+ });
396+
397+ rebuildButton.addEventListener('click', async () => {
398+ try {
399+ rebuildButton.innerHTML = '⏳';
400+ rebuildButton.style.backgroundColor = '#ffa500';
401+ rebuildButton.title = 'Rebuilding...';
402+
403+ const response = await fetch('/rebuild', {
404+ method: 'POST',
405+ headers: { 'Content-Type': 'application/json' }
406+ });
407+
408+ const result = await response.json();
409+
410+ if (result.success) {
411+ rebuildButton.innerHTML = '✅';
412+ rebuildButton.style.backgroundColor = '#28a745';
413+ rebuildButton.title = 'Build successful! Reloading...';
414+
415+ setTimeout(() => {
416+ location.reload(true);
417+ }, 1000);
418+ } else {
419+ rebuildButton.innerHTML = '❌';
420+ rebuildButton.style.backgroundColor = '#dc3545';
421+ rebuildButton.title = 'Build failed: ' + (result.error || result.message);
422+
423+ setTimeout(() => {
424+ rebuildButton.innerHTML = '🔄';
425+ rebuildButton.style.backgroundColor = '#007acc';
426+ rebuildButton.title = 'Rebuild Extension';
427+ }, 3000);
428+ }
429+ } catch (error) {
430+ console.error('Rebuild failed:', error);
431+ rebuildButton.innerHTML = '❌';
432+ rebuildButton.style.backgroundColor = '#dc3545';
433+ rebuildButton.title = 'Network error: ' + error.message;
434+
435+ setTimeout(() => {
436+ rebuildButton.innerHTML = '🔄';
437+ rebuildButton.style.backgroundColor = '#007acc';
438+ rebuildButton.title = 'Rebuild Extension';
439+ }, 3000);
440+ }
441+ });
442+
443+ document.body.appendChild(rebuildButton);
299444 </script>
300445 `
301446 if ( ! html . includes ( '</body>' ) ) {
0 commit comments