diff --git a/public/antipattern-examples/bad-contrast.html b/public/antipattern-examples/bad-contrast.html index 65548336b..c77c9d83e 100644 --- a/public/antipattern-examples/bad-contrast.html +++ b/public/antipattern-examples/bad-contrast.html @@ -202,6 +202,6 @@ - + diff --git a/public/antipattern-examples/cardocalypse.html b/public/antipattern-examples/cardocalypse.html index 6264b23db..01e2a24c0 100644 --- a/public/antipattern-examples/cardocalypse.html +++ b/public/antipattern-examples/cardocalypse.html @@ -370,6 +370,6 @@ - + diff --git a/public/antipattern-examples/inter-everywhere.html b/public/antipattern-examples/inter-everywhere.html index f657978ea..5fe854475 100644 --- a/public/antipattern-examples/inter-everywhere.html +++ b/public/antipattern-examples/inter-everywhere.html @@ -312,6 +312,6 @@ - + diff --git a/public/antipattern-examples/layout-templates.html b/public/antipattern-examples/layout-templates.html index 06067e8b0..9dbda4b12 100644 --- a/public/antipattern-examples/layout-templates.html +++ b/public/antipattern-examples/layout-templates.html @@ -317,6 +317,6 @@ - + diff --git a/public/antipattern-examples/lazy-cool.html b/public/antipattern-examples/lazy-cool.html index 745cc5ef0..e80b0d4a1 100644 --- a/public/antipattern-examples/lazy-cool.html +++ b/public/antipattern-examples/lazy-cool.html @@ -315,6 +315,6 @@ - + diff --git a/public/antipattern-examples/lazy-impact.html b/public/antipattern-examples/lazy-impact.html index c4a465f9f..e6bbbc8af 100644 --- a/public/antipattern-examples/lazy-impact.html +++ b/public/antipattern-examples/lazy-impact.html @@ -373,6 +373,6 @@ - + diff --git a/public/antipattern-examples/massive-icons.html b/public/antipattern-examples/massive-icons.html index 3c569db27..aa439e7ec 100644 --- a/public/antipattern-examples/massive-icons.html +++ b/public/antipattern-examples/massive-icons.html @@ -194,6 +194,6 @@ - + diff --git a/public/antipattern-examples/modal-abuse.html b/public/antipattern-examples/modal-abuse.html index ed207434a..367a03801 100644 --- a/public/antipattern-examples/modal-abuse.html +++ b/public/antipattern-examples/modal-abuse.html @@ -431,6 +431,6 @@ - + diff --git a/public/antipattern-examples/purple-gradients.html b/public/antipattern-examples/purple-gradients.html index 88bbe5d31..d5abc3c45 100644 --- a/public/antipattern-examples/purple-gradients.html +++ b/public/antipattern-examples/purple-gradients.html @@ -236,6 +236,6 @@ - + diff --git a/public/antipattern-examples/redundant-ux-writing.html b/public/antipattern-examples/redundant-ux-writing.html index 81de38ae9..fd426029e 100644 --- a/public/antipattern-examples/redundant-ux-writing.html +++ b/public/antipattern-examples/redundant-ux-writing.html @@ -202,6 +202,6 @@ - + diff --git a/public/antipattern-examples/thick-border-cards.html b/public/antipattern-examples/thick-border-cards.html index 9f9136d28..5c6740a49 100644 --- a/public/antipattern-examples/thick-border-cards.html +++ b/public/antipattern-examples/thick-border-cards.html @@ -200,6 +200,6 @@ - + diff --git a/scripts/build.js b/scripts/build.js index be4f29866..1b25324f9 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -322,13 +322,6 @@ async function build() { // Build CSS with Tailwind CLI (handles @theme directive) buildTailwindCSS(); - // Generate browser anti-pattern detector from core module - try { - execSync('node scripts/build-browser-detector.js', { cwd: ROOT_DIR, stdio: 'inherit' }); - } catch (error) { - console.error('Failed to build browser detector:', error.message); - } - // Bundle HTML, JS, and compiled CSS with Bun await buildStaticSite(); @@ -395,6 +388,13 @@ async function build() { console.log(`šŸ“‹ Synced to .claude/: skills`); + // Generate browser anti-pattern detector (after skill sync so it doesn't get overwritten) + try { + execSync('node scripts/build-browser-detector.js', { cwd: ROOT_DIR, stdio: 'inherit' }); + } catch (error) { + console.error('Failed to build browser detector:', error.message); + } + console.log('\n✨ Build complete!'); } diff --git a/server/index.js b/server/index.js index 3c49bc071..84739b911 100644 --- a/server/index.js +++ b/server/index.js @@ -19,19 +19,6 @@ const server = serve({ "/cheatsheet": cheatsheet, "/gallery": gallery, - // Built skill scripts (.claude/skills/) - "/.claude/skills/*": async (req) => { - const url = new URL(req.url); - if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - const filePath = `.${url.pathname}`; - const assetFile = file(filePath); - if (await assetFile.exists()) { - return new Response(assetFile, { - headers: { "Content-Type": "application/javascript", "X-Content-Type-Options": "nosniff" } - }); - } - return new Response("Not Found", { status: 404 }); - }, // Static assets - all public subdirectories "/assets/*": async (req) => { const url = new URL(req.url); @@ -60,13 +47,14 @@ const server = serve({ "/js/*": async (req) => { const url = new URL(req.url); if (url.pathname.includes('..')) return new Response("Bad Request", { status: 400 }); - const filePath = `./public${url.pathname}`; - const assetFile = file(filePath); - if (await assetFile.exists()) { - return new Response(assetFile, { - headers: { "Content-Type": "application/javascript", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" } - }); - } + // Check public/js/ first, then fall back to built artifacts + const headers = { "Content-Type": "application/javascript", "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY" }; + const publicFile = file(`./public${url.pathname}`); + if (await publicFile.exists()) return new Response(publicFile, { headers }); + // Built browser detector served at /js/detect-antipatterns-browser.js + const basename = url.pathname.split('/').pop(); + const builtFile = file(`./.claude/skills/critique/scripts/${basename}`); + if (await builtFile.exists()) return new Response(builtFile, { headers }); return new Response("Not Found", { status: 404 }); }, // Test fixtures (for browser visual testing) diff --git a/tests/detect-antipatterns-browser.test.js b/tests/detect-antipatterns-browser.test.js index 275c7d3a3..ac55aa7c8 100644 --- a/tests/detect-antipatterns-browser.test.js +++ b/tests/detect-antipatterns-browser.test.js @@ -38,8 +38,13 @@ describeIf('browser script parity with CLI', () => { let filePath; if (req.url.startsWith('/fixtures/')) { filePath = path.join(${JSON.stringify(path.join(import.meta.dir))}, req.url); - } else if (req.url.startsWith('/.claude/')) { - filePath = path.join(${JSON.stringify(path.join(import.meta.dir, '..'))}, req.url); + } else if (req.url.startsWith('/js/')) { + // Check public/js/ first, then built artifacts + const basename = req.url.split('/').pop(); + filePath = path.join(${JSON.stringify(path.join(import.meta.dir, '..', 'public'))}, req.url); + if (!fs.existsSync(filePath)) { + filePath = path.join(${JSON.stringify(path.join(import.meta.dir, '..', '.claude', 'skills', 'critique', 'scripts'))}, basename); + } } else { res.writeHead(404); res.end(); return; } diff --git a/tests/fixtures/antipatterns/color-should-flag.html b/tests/fixtures/antipatterns/color-should-flag.html index 764d2c237..af0df8aa8 100644 --- a/tests/fixtures/antipatterns/color-should-flag.html +++ b/tests/fixtures/antipatterns/color-should-flag.html @@ -75,6 +75,6 @@

Purple-to-indigo gradient

- + diff --git a/tests/fixtures/antipatterns/color-should-pass.html b/tests/fixtures/antipatterns/color-should-pass.html index dc02dbe60..71afbce66 100644 --- a/tests/fixtures/antipatterns/color-should-pass.html +++ b/tests/fixtures/antipatterns/color-should-pass.html @@ -44,6 +44,6 @@

Red heading — not AI purple

Amber heading — distinctive

- + diff --git a/tests/fixtures/antipatterns/layout-should-flag.html b/tests/fixtures/antipatterns/layout-should-flag.html index f0aa63bb5..9054099aa 100644 --- a/tests/fixtures/antipatterns/layout-should-flag.html +++ b/tests/fixtures/antipatterns/layout-should-flag.html @@ -132,6 +132,6 @@ - + diff --git a/tests/fixtures/antipatterns/layout-should-pass.html b/tests/fixtures/antipatterns/layout-should-pass.html index a6bfe872f..02a1c75ec 100644 --- a/tests/fixtures/antipatterns/layout-should-pass.html +++ b/tests/fixtures/antipatterns/layout-should-pass.html @@ -237,6 +237,6 @@ - + diff --git a/tests/fixtures/antipatterns/legitimate-borders.html b/tests/fixtures/antipatterns/legitimate-borders.html index bade3aaaa..4b89da377 100644 --- a/tests/fixtures/antipatterns/legitimate-borders.html +++ b/tests/fixtures/antipatterns/legitimate-borders.html @@ -108,6 +108,6 @@ Warning: Your trial expires in 3 days. Upgrade now - + diff --git a/tests/fixtures/antipatterns/linked-stylesheet.html b/tests/fixtures/antipatterns/linked-stylesheet.html index 8c9aa1fa4..a679dd432 100644 --- a/tests/fixtures/antipatterns/linked-stylesheet.html +++ b/tests/fixtures/antipatterns/linked-stylesheet.html @@ -43,6 +43,6 @@

Uniform 1px border — should NOT flag.

- + diff --git a/tests/fixtures/antipatterns/partial-component.html b/tests/fixtures/antipatterns/partial-component.html index 62c76d8e6..03324748b 100644 --- a/tests/fixtures/antipatterns/partial-component.html +++ b/tests/fixtures/antipatterns/partial-component.html @@ -8,4 +8,4 @@

Card description with close font sizes.

- + diff --git a/tests/fixtures/antipatterns/should-flag.html b/tests/fixtures/antipatterns/should-flag.html index 77b401c64..714bff2ed 100644 --- a/tests/fixtures/antipatterns/should-flag.html +++ b/tests/fixtures/antipatterns/should-flag.html @@ -131,6 +131,6 @@

Inline dark card with side-tab.

- + diff --git a/tests/fixtures/antipatterns/should-pass.html b/tests/fixtures/antipatterns/should-pass.html index 3f51b35f2..5ea9f3baf 100644 --- a/tests/fixtures/antipatterns/should-pass.html +++ b/tests/fixtures/antipatterns/should-pass.html @@ -79,6 +79,6 @@

Shadow only. Clean.

- + diff --git a/tests/fixtures/antipatterns/typography-should-flag.html b/tests/fixtures/antipatterns/typography-should-flag.html index 536f047d2..e51ece009 100644 --- a/tests/fixtures/antipatterns/typography-should-flag.html +++ b/tests/fixtures/antipatterns/typography-should-flag.html @@ -34,6 +34,6 @@

A Subheading

Can you tell this is a subheading? Exactly.

- + diff --git a/tests/fixtures/antipatterns/typography-should-pass.html b/tests/fixtures/antipatterns/typography-should-pass.html index fc40add35..f4a91c47f 100644 --- a/tests/fixtures/antipatterns/typography-should-pass.html +++ b/tests/fixtures/antipatterns/typography-should-pass.html @@ -40,6 +40,6 @@

Strong Size Hierarchy

Sizes range from 12px to 48px — a 4:1 ratio with clear visual steps.

Caption text is clearly distinct from body.

- +