mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-11 11:24:42 +00:00
364c5766fc
## Adds new logo and home hero Replaces the navbar mushroom emoji + "CoreScope" text spans with the new CoreScope SVG mark, and adds a hero SVG (with the MESH ANALYZER tagline) above the home page H1. ### What changed - `public/img/corescope-logo.svg` — navbar mark, no tagline (locked "aggressive low-amp chirp" variant: facing-arcs + low-amp chirp connector between the two nodes). - `public/img/corescope-hero.svg` — home hero version, includes the MESH ANALYZER tagline. - `public/index.html` — replaces `<span class="brand-icon">🍄</span><span class="brand-text">CoreScope</span>` with `<img class="brand-logo" src="img/corescope-logo.svg?__BUST__" …>`. `.nav-brand` link still routes to `#/`. `.live-dot` retained. - `public/style.css` — adds `.brand-logo { height: 36px }` (32px on tablet ≤900px). Existing 52px nav height unchanged. - `public/home.js` / `public/home.css` — adds `<img class="home-hero-logo">` above the hero `<h1>`, sized `max-width: min(720px, 90vw)` and centered. ### TDD Red→green is visible in the branch: - `3159b82` — `test(logo): add failing E2E …` (red commit). Adds `test-logo-rebrand-e2e.js` and wires it into the `e2e-test` job in `deploy.yml` with `CHROMIUM_REQUIRE=1`. On this commit `index.html` still has the emoji + text spans, `home.js` has no hero img, and the SVG asset files do not exist — the test asserts on each so CI fails on assertion. - `19434e1` — `feat(logo): wire new CoreScope SVG logo …` (green commit). Implements the fix. ### E2E asserts 1. `.nav-brand img` exists with `src` ending `corescope-logo.svg` 2. legacy `.brand-icon` / `.brand-text` are gone 3. `.live-dot` is present, visible, and to the right of the logo (no overlap) 4. `.home-hero img.home-hero-logo` exists with `src` ending `corescope-hero.svg`, positioned BEFORE the `<h1>` 5. both `/img/corescope-{logo,hero}.svg` return 200 with svg content-type ### Customizer compatibility - `customize.js` still does `querySelector('.brand-text')` / `.brand-icon` for live branding updates. Both now return `null`; existing `if (el)` guards make those branches silent no-ops. **No JS errors, but the customizer's `branding.siteName` and `branding.logoUrl` fields no longer rewrite the navbar brand** — the brand is now a fixed SVG asset. - **Theme accent does NOT recolor the SVG.** SVGs loaded via `<img src>` are isolated documents and cannot inherit document CSS variables; the SVG falls back to its embedded brand colors. This is appropriate for a brand mark; if recoloring per theme is desired later, swap to inline SVG (separate PR). ### Browser validation Local Chromium not available in this env; the E2E test soft-skips locally and hard-fails in CI (`CHROMIUM_REQUIRE=1`). Server-side checks done locally: - `curl http://localhost:13581/` → confirmed `<img class="brand-logo" src="img/corescope-logo.svg?<bust>" …>` rendered, no `.brand-icon`/`.brand-text` spans. - `curl -I /img/corescope-logo.svg` and `/img/corescope-hero.svg` → both 200. ### Performance No hot-path changes. Two new static SVG assets (~7.6KB each), served directly by the Go static handler. Cache-busted via `?__BUST__` (auto-replaced server-side). --------- Co-authored-by: OpenClaw Bot <bot@openclaw.local> Co-authored-by: Kpa-clawbot <bot@kpa-clawbot.local>
164 lines
7.0 KiB
JavaScript
164 lines
7.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/* Logo theme reactivity E2E — verifies that the navbar + hero logos
|
|
* inherit page CSS custom properties and remain visible when the user
|
|
* switches to the Light theme.
|
|
*
|
|
* Asserts:
|
|
* 1. With data-theme="light", the navbar wordmark CORE/SCOPE elements
|
|
* have a computed fill that is NOT the legacy hardcoded sage
|
|
* (#cfd9c9 / rgb(207,217,201)).
|
|
* 2. The hero SVG does NOT contain a full-canvas opaque background
|
|
* rect (no <rect width=1200 height=300> with a non-transparent fill
|
|
* reachable via the inline SVG in the home-hero region).
|
|
* 3. The hero wordmark CORE/SCOPE compute-fills also drop the legacy
|
|
* sage hex when the page theme is Light.
|
|
*
|
|
* Designed to FAIL on the pre-fix branch (where the SVGs are loaded as
|
|
* <img>, the wordmark fill is baked to #cfd9c9, and the hero SVG ships a
|
|
* solid <rect fill="var(--logo-bg, #0e1714)">).
|
|
*/
|
|
'use strict';
|
|
|
|
const { chromium } = require('playwright');
|
|
|
|
const BASE = process.env.BASE_URL || 'http://localhost:13581';
|
|
const LEGACY_SAGE = 'rgb(207, 217, 201)';
|
|
|
|
function fail(msg) {
|
|
console.error(`test-logo-theme-e2e.js: FAIL — ${msg}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
async function main() {
|
|
const requireChromium = process.env.CHROMIUM_REQUIRE === '1';
|
|
let browser;
|
|
try {
|
|
browser = await chromium.launch({
|
|
headless: true,
|
|
executablePath: process.env.CHROMIUM_PATH || undefined,
|
|
args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
|
|
});
|
|
} catch (err) {
|
|
if (requireChromium) {
|
|
console.error(`test-logo-theme-e2e.js: FAIL — Chromium required but unavailable: ${err.message}`);
|
|
process.exit(1);
|
|
}
|
|
console.log(`test-logo-theme-e2e.js: SKIP (Chromium unavailable: ${err.message.split('\n')[0]})`);
|
|
process.exit(0);
|
|
}
|
|
|
|
let passed = 0;
|
|
const total = 3;
|
|
try {
|
|
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
const page = await context.newPage();
|
|
page.setDefaultTimeout(10000);
|
|
|
|
// Force Light theme BEFORE first navigation so initial paint uses it.
|
|
await page.addInitScript(() => {
|
|
try { localStorage.setItem('meshcore-user-level', 'experienced'); } catch (_) {}
|
|
});
|
|
|
|
await page.goto(BASE + '/#/', { waitUntil: 'domcontentloaded' });
|
|
await page.waitForSelector('.nav-brand', { timeout: 8000 });
|
|
await page.evaluate(() => { document.documentElement.setAttribute('data-theme', 'light'); });
|
|
|
|
// 1. Navbar wordmark must be inline-SVG <text> (not <img>) and computed
|
|
// fill must NOT be the legacy hardcoded sage. We grep for any <text>
|
|
// with textContent CORE or SCOPE inside .nav-brand.
|
|
const navWordmarkFills = await page.evaluate(() => {
|
|
const out = [];
|
|
const root = document.querySelector('.nav-brand');
|
|
if (!root) return { error: '.nav-brand missing' };
|
|
const texts = root.querySelectorAll('svg text');
|
|
texts.forEach((t) => {
|
|
const tc = (t.textContent || '').trim();
|
|
if (tc === 'CORE' || tc === 'SCOPE') {
|
|
out.push({ tc, fill: getComputedStyle(t).fill });
|
|
}
|
|
});
|
|
return { out };
|
|
});
|
|
if (navWordmarkFills.error) fail(navWordmarkFills.error);
|
|
if (!navWordmarkFills.out || navWordmarkFills.out.length < 2) {
|
|
fail(`navbar inline-SVG wordmark <text> CORE/SCOPE not found (found: ${JSON.stringify(navWordmarkFills.out)}). Navbar logo must be inline <svg> so CSS vars apply.`);
|
|
}
|
|
for (const w of navWordmarkFills.out) {
|
|
if (w.fill === LEGACY_SAGE) {
|
|
fail(`navbar wordmark "${w.tc}" still computes legacy sage fill ${LEGACY_SAGE} — wordmark fill must theme via CSS var`);
|
|
}
|
|
}
|
|
console.log(` ✅ navbar wordmark fills are theme-reactive (${navWordmarkFills.out.map((w) => w.tc + '=' + w.fill).join(', ')})`);
|
|
passed++;
|
|
|
|
// 2. Hero SVG must NOT have a full-canvas opaque background rect.
|
|
await page.evaluate(() => { window.location.hash = '#/home'; });
|
|
await page.waitForFunction(() => location.hash === '#/home');
|
|
await page.waitForSelector('.home-hero', { timeout: 8000 });
|
|
// Ensure light theme survives reload.
|
|
await page.evaluate(() => { document.documentElement.setAttribute('data-theme', 'light'); });
|
|
|
|
const heroBg = await page.evaluate(() => {
|
|
const hero = document.querySelector('.home-hero');
|
|
if (!hero) return { error: '.home-hero missing' };
|
|
const svg = hero.querySelector('svg');
|
|
if (!svg) return { error: '.home-hero has no inline <svg> child (hero must be inline so CSS vars apply)' };
|
|
// Look for a child <rect> that covers the entire viewBox with a non-transparent fill.
|
|
const rects = svg.querySelectorAll('rect');
|
|
const offending = [];
|
|
rects.forEach((r) => {
|
|
const w = r.getAttribute('width') || '';
|
|
const h = r.getAttribute('height') || '';
|
|
const cs = getComputedStyle(r);
|
|
const fill = cs.fill || '';
|
|
const op = parseFloat(cs.fillOpacity || '1');
|
|
// legacy hero shipped <rect width=1200 height=300 fill=var(--logo-bg, #0e1714)>
|
|
if ((w === '1200' || w === '100%') && (h === '300' || h === '100%') && fill && fill !== 'none' && fill !== 'rgba(0, 0, 0, 0)' && op > 0.05) {
|
|
offending.push({ w, h, fill, op });
|
|
}
|
|
});
|
|
return { offending, rectCount: rects.length };
|
|
});
|
|
if (heroBg.error) fail(heroBg.error);
|
|
if (heroBg.offending && heroBg.offending.length > 0) {
|
|
fail(`hero SVG has full-canvas opaque background rect — paints over light theme: ${JSON.stringify(heroBg.offending)}`);
|
|
}
|
|
console.log(` ✅ hero SVG has no full-canvas opaque background rect`);
|
|
passed++;
|
|
|
|
// 3. Hero wordmark CORE/SCOPE must not compute legacy sage fill on light theme.
|
|
const heroWordmarkFills = await page.evaluate(() => {
|
|
const hero = document.querySelector('.home-hero');
|
|
if (!hero) return { error: '.home-hero missing' };
|
|
const out = [];
|
|
hero.querySelectorAll('svg text').forEach((t) => {
|
|
const tc = (t.textContent || '').trim();
|
|
if (tc === 'CORE' || tc === 'SCOPE') {
|
|
out.push({ tc, fill: getComputedStyle(t).fill });
|
|
}
|
|
});
|
|
return { out };
|
|
});
|
|
if (heroWordmarkFills.error) fail(heroWordmarkFills.error);
|
|
if (!heroWordmarkFills.out || heroWordmarkFills.out.length < 2) {
|
|
fail(`hero inline-SVG wordmark <text> CORE/SCOPE not found (found: ${JSON.stringify(heroWordmarkFills.out)})`);
|
|
}
|
|
for (const w of heroWordmarkFills.out) {
|
|
if (w.fill === LEGACY_SAGE) {
|
|
fail(`hero wordmark "${w.tc}" still computes legacy sage fill ${LEGACY_SAGE} — invisible on light theme`);
|
|
}
|
|
}
|
|
console.log(` ✅ hero wordmark fills are theme-reactive (${heroWordmarkFills.out.map((w) => w.tc + '=' + w.fill).join(', ')})`);
|
|
passed++;
|
|
|
|
await browser.close();
|
|
console.log(`\ntest-logo-theme-e2e.js: ${passed}/${total} PASS`);
|
|
} catch (err) {
|
|
try { await browser.close(); } catch (_) {}
|
|
console.error(`test-logo-theme-e2e.js: FAIL — ${err.message}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main();
|