From f55595d594517a69e16698ebf5f5508f8331e906 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 25 May 2026 14:31:22 -0700 Subject: [PATCH] refactor(design): board JS uses relative paths; drop __GSTACK_SERVER_URL injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Board JS in design/src/compare.ts now calls ./api/feedback and ./api/progress (relative to location.pathname) and feature-detects server mode via location.protocol instead of the injected window.__GSTACK_SERVER_URL global. The injection in design/src/serve.ts is removed (dead code now that nothing reads it). Tests updated to match the new contract: serve.test.ts asserts the relative-path JS is present and the global is gone; feedback-roundtrip asserts location.protocol detects HTTP mode. Why: prep for the multi-board daemon (design/src/daemon.ts upcoming) where the same generated HTML is served at /boards// instead of /. Relative paths resolve against location.pathname in both cases, so one HTML, two hosts. The injection was the only thing tying board JS to a specific serving path; removing it unblocks the daemon work without forking the generator. file:// fallback preserved via the location.protocol feature-detect — board opened directly as a file still falls through to the DOM-only success path. The 6 feedback-roundtrip browser tests continue to fail with session.clearLoadedHtml undefined; that failure pre-exists this branch (verified against HEAD with these edits stashed) and lives in browse/src/write-commands.ts, not in the design code path. Tracking separately. Co-Authored-By: Claude Opus 4.7 (1M context) --- design/src/compare.ts | 23 ++++++++++++++------ design/src/serve.ts | 29 ++++++++++++++------------ design/test/feedback-roundtrip.test.ts | 19 +++++++---------- design/test/serve.test.ts | 19 ++++++++++------- 4 files changed, 52 insertions(+), 38 deletions(-) diff --git a/design/src/compare.ts b/design/src/compare.ts index 547c85558..cd1be0ab6 100644 --- a/design/src/compare.ts +++ b/design/src/compare.ts @@ -391,6 +391,17 @@ export function generateCompareHtml(images: string[]): string {
\n`, - ); - return new Response(injected, { + return new Response(htmlContent, { headers: { "Content-Type": "text/html; charset=utf-8" }, }); } diff --git a/design/test/feedback-roundtrip.test.ts b/design/test/feedback-roundtrip.test.ts index cd757f38b..e8d63db23 100644 --- a/design/test/feedback-roundtrip.test.ts +++ b/design/test/feedback-roundtrip.test.ts @@ -55,7 +55,7 @@ beforeAll(async () => { serverState = 'serving'; // This server mirrors the real serve.ts behavior: - // - Injects __GSTACK_SERVER_URL into the HTML + // - Serves board HTML at / (board JS uses relative URLs) // - Handles POST /api/feedback with file writes // - Handles GET /api/progress for regeneration polling // - Handles POST /api/reload for board swapping @@ -67,11 +67,7 @@ beforeAll(async () => { const url = new URL(req.url); if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/index.html')) { - const injected = currentHtml.replace( - '', - `\n` - ); - return new Response(injected, { + return new Response(currentHtml, { headers: { 'Content-Type': 'text/html; charset=utf-8' }, }); } @@ -140,14 +136,15 @@ describe('Submit: browser click → feedback.json on disk', () => { if (fs.existsSync(feedbackPath)) fs.unlinkSync(feedbackPath); serverState = 'serving'; - // Navigate to the board (served with __GSTACK_SERVER_URL injected) + // Navigate to the board (board JS uses relative URLs + location.protocol detect) await handleWriteCommand('goto', [baseUrl], bm); - // Verify __GSTACK_SERVER_URL was injected - const hasServerUrl = await handleReadCommand('js', [ - '!!window.__GSTACK_SERVER_URL' + // Verify the board detects HTTP mode (so postFeedback will actually fetch + // instead of falling into the file:// DOM-only path) + const httpDetected = await handleReadCommand('js', [ + "location.protocol === 'http:' || location.protocol === 'https:'" ], bm); - expect(hasServerUrl).toBe('true'); + expect(httpDetected).toBe('true'); // User picks variant A, rates it 5 stars await handleReadCommand('js', [ diff --git a/design/test/serve.test.ts b/design/test/serve.test.ts index f222a6364..d3a30bcdc 100644 --- a/design/test/serve.test.ts +++ b/design/test/serve.test.ts @@ -65,11 +65,9 @@ describe('Serve HTTP endpoints', () => { const url = new URL(req.url); if (req.method === 'GET' && url.pathname === '/') { - const injected = htmlContent.replace( - '', - `\n` - ); - return new Response(injected, { + // Board JS uses relative URLs (./api/feedback, ./api/progress) + // and a location.protocol feature-detect; no injection needed. + return new Response(htmlContent, { headers: { 'Content-Type': 'text/html; charset=utf-8' }, }); } @@ -118,12 +116,17 @@ describe('Serve HTTP endpoints', () => { server.stop(); }); - test('GET / serves HTML with injected __GSTACK_SERVER_URL', async () => { + test('GET / serves HTML with relative-path board JS (no injection)', async () => { const res = await fetch(baseUrl); expect(res.status).toBe(200); const html = await res.text(); - expect(html).toContain('__GSTACK_SERVER_URL'); - expect(html).toContain(baseUrl); + // No more per-origin URL injection; board JS uses relative paths. + expect(html).not.toContain('__GSTACK_SERVER_URL'); + expect(html).not.toContain(baseUrl); + // Board JS calls relative endpoints so the same HTML works at / and at + // /boards// (daemon mode). + expect(html).toContain("fetch('./api/feedback'"); + expect(html).toContain("fetch('./api/progress')"); expect(html).toContain('Design Exploration'); });