mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-11 12:54:41 +00:00
Fixes #1141 follow-up — the visible-on-staging SCOPE→SCOP clip that the prior PRs (#1137, #1141) intended to address but didn't. ## What was actually broken (ground truth from staging) Staging at `http://20.109.157.39:80/` renders the inline navbar SVG correctly — duotone CORE/SCOPE fills inherit page CSS vars, mobile mark-only swap fires at ≤400px, customizer logo override path works. Those parts of #1137 + #1141 landed cleanly. What did **NOT** land: the SVG `viewBox` was never widened to fit the rendered Aldrich wordmark. At every desktop viewport the SCOPE `<text text-anchor="start" x="773.8">` produces a bbox extending to user-space x≈1112, but the navbar `viewBox="170 10 860 280"` ends at x=1030. Result: SCOPE renders as **SCOP** on every desktop load. CORE also slightly overflows the left edge (bbox.x=153.7 < viewBox.x=170). The original brief premise (mushroom emoji still in `index.html` + `<img>`-loaded SVG monotone fallback on staging) does not match current state — `public/index.html:45` already has the inline SVG, staging renders it, and computed fills are duotone (`rgb(74,158,255)` vs `rgb(109,179,255)`). The visible bug is geometric clipping, not CSS-var inheritance or a mushroom revert. ## Fix (one-liner SVG geometry change) - `public/index.html` — navbar `svg.brand-logo`: `viewBox="170 10 860 280"` → `viewBox="150 10 970 280"`; intrinsic `width="111"` → `width="125"` (preserves ~36px nav row height). - `public/style.css` — `.brand-logo { width }` 111px → 125px (desktop), tablet `@media (max-width:900px)` pin 99px → 112px to keep the new aspect ratio so wordmark still doesn't clip on tablets. - `public/customize-v2.js` — `_setBrandLogoUrl` `<img>` swap dimensions updated to match (when an operator overrides `branding.logoUrl`). The `≤400px` mobile mark-only swap is unchanged — at narrow widths the wordmark still hides entirely and the dedicated `.brand-mark-only` SVG (no `<text>`) renders. ## TDD (red → green) | commit | role | |---|---| | `16b7a60` | **RED** — `test-logo-theme-e2e.js` assertion #7: every `CORE`/`SCOPE` `<text>` bbox must fit inside the SVG `viewBox`. Master fails: `[{text:CORE, bboxX:153.7, bboxRight:426.2, vbX:170}, {text:SCOPE, bboxX:773.8, bboxRight:1111.5, vbRight:1030}]` | | `0db473b` | **GREEN** — widen viewBox + width to fit | Test exercises real `getBBox()` measurement on a headless Chromium DOM with the Aldrich webfont loaded — not a unit-test fill string check. The earlier #1141 tests asserted computed `fill` colors (which were correct) but never measured rendered geometry; that's the gap. ## Visual proof **Before** (master HEAD against staging, viewport 1280): `/tmp/staging-logo-before-1280.png` — SCOPE clearly clipped to "SCOP". **After** (this branch against local server, viewport 1280): `/tmp/local-after-1280-screen.png` — full CORE / SCOPE rendered. **Mobile (after, 375px)**: `/tmp/local-after-mobile.png` — mark-only SVG (no wordmark, no clip). ## Preflight `bash ~/.openclaw/skills/pr-preflight/scripts/run-all.sh origin/master` — all hard gates clean (PII, branch-scope, red-commit-genuine, css-vars-defined, css-self-fallback, like-on-json, sync-migration), all warnings clean (img-svg-ratio, themed-img-svg, fixture-coverage). E2E assertion added: `test-logo-theme-e2e.js:286-310` Browser verified: `/tmp/local-after-1280-screen.png` (local server) + `/tmp/staging-logo-before-1280.png` (staging baseline). --------- Co-authored-by: corescope-bot <bot@corescope.local>
This commit is contained in:
@@ -74,7 +74,7 @@
|
||||
img.className = 'brand-logo';
|
||||
img.setAttribute('src', url);
|
||||
img.setAttribute('alt', alt || node.getAttribute('aria-label') || 'Brand');
|
||||
img.setAttribute('width', '111');
|
||||
img.setAttribute('width', '125');
|
||||
img.setAttribute('height', '36');
|
||||
node.parentNode.replaceChild(img, node);
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@
|
||||
<nav class="top-nav" role="navigation" aria-label="Main navigation">
|
||||
<div class="nav-left">
|
||||
<a href="#/" class="nav-brand" aria-label="CoreScope home">
|
||||
<svg class="brand-logo" xmlns="http://www.w3.org/2000/svg" width="111" height="36" viewBox="170 10 860 280" aria-hidden="true" focusable="false"><path d="M540 100 A 30 30 0 1 0 540 160" fill="none" stroke="var(--logo-accent)" stroke-width="8" opacity="1.00"/>
|
||||
<svg class="brand-logo" xmlns="http://www.w3.org/2000/svg" width="125" height="36" viewBox="150 10 970 280" aria-hidden="true" focusable="false"><path d="M540 100 A 30 30 0 1 0 540 160" fill="none" stroke="var(--logo-accent)" stroke-width="8" opacity="1.00"/>
|
||||
<path d="M540 73 A 57 57 0 1 0 540 187" fill="none" stroke="var(--logo-accent)" stroke-width="8" opacity="0.82"/>
|
||||
<path d="M540 46 A 84 84 0 1 0 540 214" fill="none" stroke="var(--logo-accent)" stroke-width="8" opacity="0.64"/>
|
||||
<path d="M540 19 A 111 111 0 1 0 540 241" fill="none" stroke="var(--logo-accent)" stroke-width="8" opacity="0.46"/>
|
||||
|
||||
+5
-4
@@ -497,9 +497,10 @@ input[type="week"] {
|
||||
height: 36px;
|
||||
/* Width matches the inline SVG's effective content aspect (~3.08:1)
|
||||
after cropping to the wordmark+nodes region in the navbar viewBox.
|
||||
Pinned explicitly so layout doesn't shift if the SVG is replaced
|
||||
(issue #1046 1115px viewport regression). */
|
||||
width: 111px;
|
||||
/* Pinned to match the SVG intrinsic width (#1141 followup: viewBox
|
||||
widened to 150 10 970 280 to fit CORE/SCOPE wordmark; intrinsic
|
||||
width 125 keeps the text legible at the same ~36px height). */
|
||||
width: 125px;
|
||||
max-height: 36px;
|
||||
/* The logo is inlined into the DOM (PR #1137) so it inherits page
|
||||
CSS variables; theme via --logo-text / --logo-accent / etc. defined
|
||||
@@ -1469,7 +1470,7 @@ button.ch-item:hover .ch-icon-btn { opacity: 1; }
|
||||
@media (max-width: 900px) {
|
||||
.panel-right { width: 320px; min-width: 320px; }
|
||||
.nav-stats { display: none; }
|
||||
.brand-logo { height: 32px; width: 99px; }
|
||||
.brand-logo { height: 32px; width: 112px; }
|
||||
.nav-link { padding: 14px 8px; font-size: 13px; }
|
||||
.map-controls { width: 180px; font-size: 12px; }
|
||||
}
|
||||
|
||||
+37
-1
@@ -58,7 +58,7 @@ async function main() {
|
||||
}
|
||||
|
||||
let passed = 0;
|
||||
const total = 6;
|
||||
const total = 7;
|
||||
try {
|
||||
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
||||
const page = await context.newPage();
|
||||
@@ -283,6 +283,42 @@ async function main() {
|
||||
console.log(` ✅ mobile (360px): mark-only swap active (full hidden, mark visible, right=${mobile.visRectRight}px ≤ viewport ${mobile.viewportWidth}px)`);
|
||||
passed++;
|
||||
|
||||
// 7. Desktop wordmark must NOT clip — every <text> element's bbox in
|
||||
// user-space coords must lie fully inside the SVG's viewBox. The
|
||||
// original navbar SVG ships with viewBox "170 10 860 280" (right
|
||||
// edge x=1030), but the SCOPE <text> with text-anchor="start" at
|
||||
// x=773.8 + width≈338 extends to x≈1111 — clipped to "SCOP" at
|
||||
// every desktop viewport width. Fix: widen the viewBox so the
|
||||
// wordmark fits.
|
||||
await page.setViewportSize({ width: 1280, height: 800 });
|
||||
await page.evaluate(() => { window.location.hash = '#/'; });
|
||||
await page.waitForFunction(() => location.hash === '#/');
|
||||
await page.waitForSelector('.nav-brand svg.brand-logo', { timeout: 8000 });
|
||||
await page.waitForTimeout(150);
|
||||
const clip = await page.evaluate(() => {
|
||||
const svg = document.querySelector('.nav-brand svg.brand-logo');
|
||||
if (!svg) return { error: '.nav-brand svg.brand-logo missing' };
|
||||
const vb = (svg.getAttribute('viewBox') || '').split(/\s+/).map(Number);
|
||||
if (vb.length !== 4) return { error: 'viewBox malformed: ' + svg.getAttribute('viewBox') };
|
||||
const [vx, vy, vw, vh] = vb;
|
||||
const offenders = [];
|
||||
svg.querySelectorAll('text').forEach((t) => {
|
||||
const tc = (t.textContent || '').trim();
|
||||
if (tc !== 'CORE' && tc !== 'SCOPE') return;
|
||||
const bb = t.getBBox();
|
||||
if (bb.x < vx - 0.5 || bb.x + bb.width > vx + vw + 0.5) {
|
||||
offenders.push({ text: tc, bboxX: bb.x, bboxRight: bb.x + bb.width, vbX: vx, vbRight: vx + vw });
|
||||
}
|
||||
});
|
||||
return { viewBox: vb, offenders };
|
||||
});
|
||||
if (clip.error) fail(clip.error);
|
||||
if (clip.offenders && clip.offenders.length) {
|
||||
fail(`desktop: wordmark <text> overflows SVG viewBox (will be clipped): ${JSON.stringify(clip.offenders)}`);
|
||||
}
|
||||
console.log(` ✅ desktop (1280px): CORE/SCOPE bboxes fit inside viewBox ${JSON.stringify(clip.viewBox)}`);
|
||||
passed++;
|
||||
|
||||
await browser.close();
|
||||
console.log(`\ntest-logo-theme-e2e.js: ${passed}/${total} PASS`);
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user