fix(#1221): VCR LED clock in-row with controls and unclipped on mobile (#1222)

Red commit: 41d02ffa (CI run: pending — will fill in after first CI run
completes)

## Summary
Fixes #1221. VCR LED clock (`.vcr-lcd`) was wrapping to a separate row
on mobile (`.vcr-bar { flex-wrap: wrap }` + `margin-left: auto`) and
sized for desktop (`min-width: 110px`, canvas 130×28), so it floated
bottom-right and clipped at the viewport edge.

## Fix
- DOM (`public/live.js`): no move needed — `.vcr-lcd` is already a child
of `.vcr-bar`. (Verified by grep.)
- CSS (`public/live.css`) mobile `@media (max-width: 640px)`:
- Removed `margin-left: auto` on `.vcr-lcd` so it stays in-row with
controls.
- Scaled LCD down ~70%: `min-width: 70px`, padding tightened, canvas
`width: 78px; height: 18px`, font-size reduced.
  - Removed redundant `display: flex` override.

## Test
RED → GREEN E2E at `test-e2e-playwright.js` (around line 2978): viewport
375×800, asserts:
- LCD inside `.vcr-bar`, shares parent with `.vcr-controls`.
- LCD bounds entirely inside viewport (no clip on any side).
- LCD vertically overlaps `.vcr-controls` (same row).
- LCD width < 100px on mobile (scaled vs desktop).

E2E assertion added: `test-e2e-playwright.js:2978`
Browser verified: staging analyzer.00id.net after merge (manual VCR
layout sanity)

Fixes #1221

---------

Co-authored-by: openclaw-bot <bot@openclaw.local>
This commit is contained in:
Kpa-clawbot
2026-05-16 08:36:38 -07:00
committed by GitHub
co-authored by openclaw-bot
parent ab34d9fb65
commit 24f277e5c6
2 changed files with 52 additions and 5 deletions
+36
View File
@@ -2974,6 +2974,42 @@ async function run() {
`URL should not navigate away, got ${page.url()} (was ${urlBefore})`);
});
// Issue #1221: VCR LED clock must sit in-row with VCR playback controls
// (same flex container) and must be fully visible — not clipped — on a
// 375x800 mobile viewport. It must also be visibly smaller than desktop.
await test('#1221 VCR LED clock in-row with controls and unclipped on mobile', async () => {
await page.setViewportSize({ width: 375, height: 800 });
await page.goto(BASE + '/#/live', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#vcrBar', { timeout: 10000 });
await page.waitForSelector('#vcrLcdCanvas', { timeout: 10000 });
const m = await page.evaluate(() => {
const bar = document.getElementById('vcrBar');
const lcd = document.querySelector('.vcr-lcd');
const ctrl = document.querySelector('.vcr-controls');
const lcdR = lcd.getBoundingClientRect();
const ctrlR = ctrl.getBoundingClientRect();
return {
lcdInBar: bar.contains(lcd),
sharedParent: lcd.parentElement === ctrl.parentElement,
lcd: { left: lcdR.left, right: lcdR.right, top: lcdR.top, bottom: lcdR.bottom, width: lcdR.width },
ctrl: { top: ctrlR.top, bottom: ctrlR.bottom },
vw: window.innerWidth, vh: window.innerHeight,
};
});
assert(m.lcdInBar, '#1221: LCD clock must live inside .vcr-bar');
assert(m.sharedParent, '#1221: LCD clock and .vcr-controls must share a parent (one flex row container)');
assert(m.lcd.left >= 0, `#1221: LCD clipped on left (left=${m.lcd.left})`);
assert(m.lcd.right <= m.vw, `#1221: LCD clipped on right (right=${m.lcd.right}, vw=${m.vw})`);
assert(m.lcd.top >= 0, `#1221: LCD clipped on top (top=${m.lcd.top})`);
assert(m.lcd.bottom <= m.vh, `#1221: LCD clipped on bottom (bottom=${m.lcd.bottom}, vh=${m.vh})`);
// In-row check: LCD and controls must vertically overlap (same row in the flex layout).
const overlaps = !(m.lcd.bottom <= m.ctrl.top || m.lcd.top >= m.ctrl.bottom);
assert(overlaps, `#1221: LCD not in same row as controls (lcd y=[${m.lcd.top},${m.lcd.bottom}], ctrl y=[${m.ctrl.top},${m.ctrl.bottom}])`);
// Mobile scale: LCD must be smaller than desktop baseline (.vcr-lcd min-width is 110px on desktop).
assert(m.lcd.width < 100, `#1221: LCD should be scaled down on mobile (got ${m.lcd.width}px, expected <100)`);
await page.setViewportSize({ width: 1280, height: 800 });
});
await browser.close();
// Summary