From e2bcbac903b2053b6bba62c27629664f64fa6fac Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 6 Sep 2025 12:09:21 -0700 Subject: [PATCH 1/6] Remove dev server detection. --- browser-extension/tests/har-view.ts | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/browser-extension/tests/har-view.ts b/browser-extension/tests/har-view.ts index e2b9f4e..9304237 100644 --- a/browser-extension/tests/har-view.ts +++ b/browser-extension/tests/har-view.ts @@ -25,19 +25,6 @@ function getUrlParts(key: keyof typeof PAGES) { } } -// Check if WXT dev server is running -async function checkDevServer(): Promise { - try { - const response = await fetch('http://localhost:3000/@vite/client', { - method: 'HEAD', - signal: AbortSignal.timeout(2000), - }) - return response.ok - } catch { - return false - } -} - // Load and cache HAR file async function loadHar(key: keyof typeof PAGES): Promise { if (harCache.has(key)) { @@ -65,16 +52,6 @@ app.get('/', async (_req, res) => { const harDir = path.join(__dirname, 'har') const files = await fs.readdir(harDir) const harFiles = files.filter((file) => file.endsWith('.har')) - const devServerRunning = await checkDevServer() - - const devServerWarning = !devServerRunning - ? ` -
- ⚠️ Warning: WXT dev server is not running on localhost:3000
- Gitcasso-enabled links won't work. Run npm run dev to start the server and then refresh this page. -
- ` - : '' const links = harFiles .map((file) => { @@ -84,9 +61,7 @@ app.get('/', async (_req, res) => {
${basename}
🔍 Clean - - 🚀 Gitcasso-enabled - + 🚀 Gitcasso
` @@ -124,7 +99,6 @@ app.get('/', async (_req, res) => {

📄 HAR Page Viewer

- ${devServerWarning}

Select a recorded page to view:

    ${links}
From d4f160cb4cbd850f55f6a6b819eb31276602cf39 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 6 Sep 2025 12:13:19 -0700 Subject: [PATCH 2/6] Add some self-documentation. --- browser-extension/tests/har-view.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/browser-extension/tests/har-view.ts b/browser-extension/tests/har-view.ts index 9304237..1f18051 100644 --- a/browser-extension/tests/har-view.ts +++ b/browser-extension/tests/har-view.ts @@ -1,3 +1,20 @@ +/** + * HAR Page Viewer Test Server + * + * This Express server serves recorded HAR files as live web pages for testing. + * It provides two viewing modes: 'clean' (original page) and 'gitcasso' (with extension injected). + * + * Key components: + * - Loads HAR files from ./har/ directory based on PAGES index in `./har/_har_index.ts` + * - Patches URLs in HTML to serve assets locally from HAR data + * - Handles asset serving by matching HAR entries to requested paths + * + * Development notes: + * - Injects Gitcasso content script in 'gitcasso' mode with location patching + * - Location patching uses history.pushState to simulate original URLs + * - Chrome APIs are mocked for extension testing outside browser context + * - Content script is fetched from http://localhost:3000 (dev server) + */ import { error } from 'node:console' import fs from 'node:fs/promises' import path from 'node:path' From 015d5835c7d451eca7c9c0f8a47d5dfb0caf52af Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 6 Sep 2025 13:03:47 -0700 Subject: [PATCH 3/6] Serve the extension from disk, not from hotreload server. --- browser-extension/tests/har-view.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser-extension/tests/har-view.ts b/browser-extension/tests/har-view.ts index 1f18051..1025012 100644 --- a/browser-extension/tests/har-view.ts +++ b/browser-extension/tests/har-view.ts @@ -13,7 +13,7 @@ * - Injects Gitcasso content script in 'gitcasso' mode with location patching * - Location patching uses history.pushState to simulate original URLs * - Chrome APIs are mocked for extension testing outside browser context - * - Content script is fetched from http://localhost:3000 (dev server) + * - Extension assets served from `./output/chrome-mv3-dev` via `/chrome-mv3-dev` route */ import { error } from 'node:console' import fs from 'node:fs/promises' @@ -236,6 +236,8 @@ app.get('/asset/:key/*', async (req, res) => { return res.status(404).send('Asset not found') } }) +// Serve extension assets from filesystem +app.use('/chrome-mv3-dev', express.static(path.join(__dirname, '..', '.output', 'chrome-mv3-dev'))) app.listen(PORT, () => { console.log(`HAR Page Viewer running at http://localhost:${PORT}`) @@ -262,7 +264,7 @@ function injectGitcassoScript(key: keyof typeof PAGES, html: string) { }); // Fetch and patch the content script to remove webextension-polyfill issues - fetch('http://localhost:3000/.output/chrome-mv3-dev/content-scripts/content.js') + fetch('/chrome-mv3-dev/content-scripts/content.js') .then(response => response.text()) .then(code => { console.log('Fetched content script, patching webextension-polyfill...'); From 2a8d8d5f89b2e381ee6715bb505e0b2042a48668 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 6 Sep 2025 13:17:26 -0700 Subject: [PATCH 4/6] Add in-page reload. --- browser-extension/tests/har-view.ts | 145 ++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/browser-extension/tests/har-view.ts b/browser-extension/tests/har-view.ts index 1025012..53d0087 100644 --- a/browser-extension/tests/har-view.ts +++ b/browser-extension/tests/har-view.ts @@ -16,6 +16,7 @@ * - Extension assets served from `./output/chrome-mv3-dev` via `/chrome-mv3-dev` route */ import { error } from 'node:console' +import { spawn } from 'node:child_process' import fs from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' @@ -27,6 +28,9 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)) const app = express() const PORT = 3001 +// Middleware to parse JSON bodies +app.use(express.json()) + // Store HAR json const harCache = new Map() @@ -239,6 +243,63 @@ app.get('/asset/:key/*', async (req, res) => { // Serve extension assets from filesystem app.use('/chrome-mv3-dev', express.static(path.join(__dirname, '..', '.output', 'chrome-mv3-dev'))) +// Rebuild endpoint +app.post('/rebuild', async (req, res) => { + try { + console.log('Rebuild triggered via API') + + // Run npx wxt build --mode development + const buildProcess = spawn('npx', ['wxt', 'build', '--mode', 'development'], { + cwd: path.join(__dirname, '..'), + stdio: ['pipe', 'pipe', 'pipe'] + }) + + let stdout = '' + let stderr = '' + + buildProcess.stdout.on('data', (data) => { + stdout += data.toString() + console.log('[BUILD]', data.toString().trim()) + }) + + buildProcess.stderr.on('data', (data) => { + stderr += data.toString() + console.error('[BUILD ERROR]', data.toString().trim()) + }) + + buildProcess.on('close', (code) => { + if (code === 0) { + console.log('Build completed successfully') + res.json({ success: true, message: 'Build completed successfully' }) + } else { + console.error('Build failed with code:', code) + res.status(500).json({ + success: false, + message: 'Build failed', + error: stderr || stdout + }) + } + }) + + buildProcess.on('error', (error) => { + console.error('Failed to start build process:', error) + res.status(500).json({ + success: false, + message: 'Failed to start build process', + error: error.message + }) + }) + + } catch (error) { + console.error('Rebuild endpoint error:', error) + res.status(500).json({ + success: false, + message: 'Internal server error', + error: error instanceof Error ? error.message : 'Unknown error' + }) + } +}) + app.listen(PORT, () => { console.log(`HAR Page Viewer running at http://localhost:${PORT}`) console.log('Click the links to view recorded pages') @@ -296,6 +357,90 @@ function injectGitcassoScript(key: keyof typeof PAGES, html: string) { .catch(error => { console.error('Failed to load and patch content script:', error); }); + + // Create floating rebuild button + const rebuildButton = document.createElement('div'); + rebuildButton.id = 'gitcasso-rebuild-btn'; + rebuildButton.innerHTML = '🔄'; + rebuildButton.title = 'Rebuild Extension'; + rebuildButton.style.cssText = \` + position: fixed; + top: 20px; + right: 20px; + width: 50px; + height: 50px; + background: #007acc; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + cursor: pointer; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + z-index: 999999; + user-select: none; + transition: all 0.2s ease; + font-family: system-ui, -apple-system, sans-serif; + \`; + + rebuildButton.addEventListener('mouseenter', () => { + rebuildButton.style.transform = 'scale(1.1)'; + rebuildButton.style.backgroundColor = '#005a9e'; + }); + + rebuildButton.addEventListener('mouseleave', () => { + rebuildButton.style.transform = 'scale(1)'; + rebuildButton.style.backgroundColor = '#007acc'; + }); + + rebuildButton.addEventListener('click', async () => { + try { + rebuildButton.innerHTML = '⏳'; + rebuildButton.style.backgroundColor = '#ffa500'; + rebuildButton.title = 'Rebuilding...'; + + const response = await fetch('/rebuild', { + method: 'POST', + headers: { 'Content-Type': 'application/json' } + }); + + const result = await response.json(); + + if (result.success) { + rebuildButton.innerHTML = '✅'; + rebuildButton.style.backgroundColor = '#28a745'; + rebuildButton.title = 'Build successful! Reloading...'; + + setTimeout(() => { + location.reload(true); + }, 1000); + } else { + rebuildButton.innerHTML = '❌'; + rebuildButton.style.backgroundColor = '#dc3545'; + rebuildButton.title = 'Build failed: ' + (result.error || result.message); + + setTimeout(() => { + rebuildButton.innerHTML = '🔄'; + rebuildButton.style.backgroundColor = '#007acc'; + rebuildButton.title = 'Rebuild Extension'; + }, 3000); + } + } catch (error) { + console.error('Rebuild failed:', error); + rebuildButton.innerHTML = '❌'; + rebuildButton.style.backgroundColor = '#dc3545'; + rebuildButton.title = 'Network error: ' + error.message; + + setTimeout(() => { + rebuildButton.innerHTML = '🔄'; + rebuildButton.style.backgroundColor = '#007acc'; + rebuildButton.title = 'Rebuild Extension'; + }, 3000); + } + }); + + document.body.appendChild(rebuildButton); ` if (!html.includes('')) { From 21e4edbb2296fc6ae8d29ae3116dcae0b287263d Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 6 Sep 2025 13:19:31 -0700 Subject: [PATCH 5/6] biome:fix --- browser-extension/package.json | 1 + browser-extension/tests/har-view.ts | 46 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/browser-extension/package.json b/browser-extension/package.json index fedf210..fc5e5a4 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -37,6 +37,7 @@ "scripts": { "biome": "biome check .", "biome:fix": "biome check --write .", + "biome:fix:unsafe": "biome check --write --unsafe .", "wxt:build": "wxt build", "wxt:build:firefox": "wxt build -b firefox", "compile": "tsc --noEmit", diff --git a/browser-extension/tests/har-view.ts b/browser-extension/tests/har-view.ts index 53d0087..563189a 100644 --- a/browser-extension/tests/har-view.ts +++ b/browser-extension/tests/har-view.ts @@ -15,8 +15,9 @@ * - Chrome APIs are mocked for extension testing outside browser context * - Extension assets served from `./output/chrome-mv3-dev` via `/chrome-mv3-dev` route */ -import { error } from 'node:console' + import { spawn } from 'node:child_process' +import { error } from 'node:console' import fs from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' @@ -244,58 +245,57 @@ app.get('/asset/:key/*', async (req, res) => { app.use('/chrome-mv3-dev', express.static(path.join(__dirname, '..', '.output', 'chrome-mv3-dev'))) // Rebuild endpoint -app.post('/rebuild', async (req, res) => { +app.post('/rebuild', async (_req, res) => { try { console.log('Rebuild triggered via API') - + // Run npx wxt build --mode development const buildProcess = spawn('npx', ['wxt', 'build', '--mode', 'development'], { cwd: path.join(__dirname, '..'), - stdio: ['pipe', 'pipe', 'pipe'] + stdio: ['pipe', 'pipe', 'pipe'], }) - + let stdout = '' let stderr = '' - + buildProcess.stdout.on('data', (data) => { stdout += data.toString() console.log('[BUILD]', data.toString().trim()) }) - + buildProcess.stderr.on('data', (data) => { stderr += data.toString() console.error('[BUILD ERROR]', data.toString().trim()) }) - + buildProcess.on('close', (code) => { if (code === 0) { console.log('Build completed successfully') - res.json({ success: true, message: 'Build completed successfully' }) + res.json({ message: 'Build completed successfully', success: true }) } else { console.error('Build failed with code:', code) - res.status(500).json({ - success: false, - message: 'Build failed', - error: stderr || stdout + res.status(500).json({ + error: stderr || stdout, + message: 'Build failed', + success: false, }) } }) - + buildProcess.on('error', (error) => { console.error('Failed to start build process:', error) - res.status(500).json({ - success: false, - message: 'Failed to start build process', - error: error.message + res.status(500).json({ + error: error.message, + message: 'Failed to start build process', + success: false, }) }) - } catch (error) { console.error('Rebuild endpoint error:', error) - res.status(500).json({ - success: false, - message: 'Internal server error', - error: error instanceof Error ? error.message : 'Unknown error' + res.status(500).json({ + error: error instanceof Error ? error.message : 'Unknown error', + message: 'Internal server error', + success: false, }) } }) From cdc6d346da75aad5e7aa9637255547aa9b595782 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sat, 6 Sep 2025 13:20:52 -0700 Subject: [PATCH 6/6] Update docs. --- browser-extension/tests/har-view.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/browser-extension/tests/har-view.ts b/browser-extension/tests/har-view.ts index 563189a..b78cb8b 100644 --- a/browser-extension/tests/har-view.ts +++ b/browser-extension/tests/har-view.ts @@ -14,6 +14,7 @@ * - Location patching uses history.pushState to simulate original URLs * - Chrome APIs are mocked for extension testing outside browser context * - Extension assets served from `./output/chrome-mv3-dev` via `/chrome-mv3-dev` route + * - Floating rebuild button in gitcasso mode triggers `npx wxt build --mode development` and then refresh */ import { spawn } from 'node:child_process'