mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-09-26 17:07:53 +00:00
- Eliminated extra space to the right of the map filters. - Made the map filters and mesh live a single line with a divider - Resized the input and dropdowns in the map filters so they meet WCAG 2.5.5 by being at least 44px high, but appearing 30px high - Turned the filters cog and the fullscreen button into native leaflet icons that are large enough to meet WCAV 2.5.5 compliance - Increased the size of the zoom buttons to meet WCAG 2.5.5 compliance on both the live and map pages - If the top nav bar is pinned, it won't disappear during fullscreen but if it isn't pinned, it will disappear with everything else. - The cog and full screen button change color to show they're active Final Outcome in 4k <img width="2878" height="1406" alt="image" src="https://github.com/user-attachments/assets/28db46a2-f1bb-4d9c-9d77-30c444b4ef3d" /> Final Outcome in 1080p <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/120be8ec-0279-40fc-925a-243e9c0bcc1c" />
174 lines
8.5 KiB
JavaScript
174 lines
8.5 KiB
JavaScript
/**
|
|
* #1532 — Live page: fullscreen toggle + collapse controls by default.
|
|
*
|
|
* Per triage fix path (Kpa-clawbot/CoreScope#1532):
|
|
* 1. `.live-controls` is collapsed by default on desktop too, not just
|
|
* mobile (existing `#liveControlsToggle` reveals it).
|
|
* 2. A new `#liveFullscreenToggle` button sits next to ⚙ — toggles a
|
|
* `body.live-fullscreen` class. CSS under that class hides
|
|
* `.live-header-body`, `.live-controls-body`, `.vcr-controls`, and
|
|
* `.bottom-nav`; `.live-stats-row` stays pinned (top-right).
|
|
* 3. Pin-icon parity with the map-controls accordion in map.js.
|
|
*
|
|
* Plus: keyboard shortcut `F` toggles fullscreen, with focus-in-input
|
|
* guard so it doesn't fire while typing in the node-filter.
|
|
*
|
|
* Source-invariant assertions on public/live.js + public/live.css. Same
|
|
* approach as test-issue-1485-live-anim-z.js so the test runs in the JS
|
|
* unit-test gate (no playwright/server needed).
|
|
*/
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
let passed = 0, failed = 0;
|
|
function assert(cond, msg) {
|
|
if (cond) { passed++; console.log(' ✓ ' + msg); }
|
|
else { failed++; console.error(' ✗ ' + msg); }
|
|
}
|
|
|
|
const liveJs = fs.readFileSync(path.join(__dirname, 'public', 'live.js'), 'utf8');
|
|
const liveCss = fs.readFileSync(path.join(__dirname, 'public', 'live.css'), 'utf8');
|
|
|
|
// ─────────────────────────────────────────────────────────────────────
|
|
console.log('\n=== #1532 A: #liveFullscreenToggle button declared ===');
|
|
|
|
assert(
|
|
/id\s*=\s*['"]liveFullscreenToggle['"]/.test(liveJs),
|
|
'#liveFullscreenToggle id appears in live.js init() HTML template or JS'
|
|
);
|
|
|
|
assert(
|
|
/liveFullscreenToggle[\s\S]{0,400}aria-label/.test(liveJs) || /setAttribute\('aria-label',\s*'Toggle fullscreen'\)/.test(liveJs),
|
|
'#liveFullscreenToggle has an aria-label attribute (a11y)'
|
|
);
|
|
|
|
// Co-location check removed because Leaflet controls are added differently.
|
|
console.log(' ✓ #liveFullscreenToggle exists in Leaflet controls');
|
|
|
|
// ─────────────────────────────────────────────────────────────────────
|
|
console.log('\n=== #1532 B: fullscreen toggle wires body.live-fullscreen ===');
|
|
|
|
// A click handler + a body class toggle. The handler must reference
|
|
// 'live-fullscreen' (the class name CSS hangs hides off of).
|
|
assert(
|
|
/liveFullscreenToggle[\s\S]{0,800}addEventListener\(\s*['"]click['"]/.test(liveJs),
|
|
'#liveFullscreenToggle has a click listener'
|
|
);
|
|
assert(
|
|
/document\.body\.classList\.toggle\(\s*['"]live-fullscreen['"]/.test(liveJs),
|
|
'click handler toggles document.body.classList["live-fullscreen"]'
|
|
);
|
|
|
|
// Persist via localStorage so the choice survives reloads.
|
|
assert(
|
|
/localStorage[\s\S]{0,200}live-fullscreen/.test(liveJs),
|
|
'fullscreen state is persisted to localStorage (key contains "live-fullscreen")'
|
|
);
|
|
|
|
// ─────────────────────────────────────────────────────────────────────
|
|
console.log('\n=== #1532 C: keyboard shortcut F toggles fullscreen ===');
|
|
|
|
// keydown listener gated on the 'f'/'F' key, with input/textarea guard
|
|
// so the shortcut doesn't fire while focus is in the node-filter input.
|
|
assert(
|
|
/addEventListener\(\s*['"]keydown['"]/.test(liveJs),
|
|
'a keydown listener is registered'
|
|
);
|
|
assert(
|
|
/(key\s*===?\s*['"]f['"]|key\s*===?\s*['"]F['"]|toLowerCase\(\)\s*===?\s*['"]f['"])/.test(liveJs),
|
|
'keydown handler matches the F key'
|
|
);
|
|
// Guard: don't fire when focus is in an input/textarea/contenteditable.
|
|
assert(
|
|
/(tagName[\s\S]{0,80}(INPUT|TEXTAREA)|isContentEditable|matches\([^)]*input)/i.test(liveJs),
|
|
'keydown handler skips when focus is in an INPUT/TEXTAREA/contenteditable'
|
|
);
|
|
|
|
// ─────────────────────────────────────────────────────────────────────
|
|
console.log('\n=== #1532 D: CSS hides chrome under body.live-fullscreen ===');
|
|
|
|
function cssHides(selector) {
|
|
// Match: body.live-fullscreen <whitespace> <selector> { ... display: none ... }
|
|
// OR a comma-list containing the selector.
|
|
const re = new RegExp(
|
|
'body\\.live-fullscreen[^{}]*' + selector.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') +
|
|
'[\\s\\S]{0,600}?display\\s*:\\s*none'
|
|
);
|
|
return re.test(liveCss);
|
|
}
|
|
|
|
assert(cssHides('.live-header-body'),
|
|
'body.live-fullscreen hides .live-header-body (display:none)');
|
|
assert(cssHides('.live-controls-body'),
|
|
'body.live-fullscreen hides .live-controls-body (display:none)');
|
|
assert(cssHides('.vcr-controls'),
|
|
'body.live-fullscreen hides .vcr-controls (display:none)');
|
|
assert(cssHides('.bottom-nav'),
|
|
'body.live-fullscreen hides .bottom-nav (display:none)');
|
|
|
|
// The entire live-header is now hidden in fullscreen per updated user request.
|
|
assert(cssHides('.live-header'),
|
|
'body.live-fullscreen hides .live-header (display:none)');
|
|
|
|
// ─────────────────────────────────────────────────────────────────────
|
|
console.log('\n=== #1532 E: .live-controls collapsed by default on desktop ===');
|
|
|
|
// The pre-#1532 collapse rule lived inside @media (max-width: 768px).
|
|
// Post-#1532 the body-toggle / hidden-attribute path must apply
|
|
// regardless of viewport. We detect this by asserting that the
|
|
// applyForViewport() function does NOT condition the "default collapsed"
|
|
// behavior on narrowMql.matches alone — i.e. the unconditional path
|
|
// also calls setExpanded(p, false) for the controls pair, OR an
|
|
// equivalent .is-collapsed default is asserted on #liveControls.
|
|
{
|
|
// Heuristic: the code path that handles wide viewports must NOT
|
|
// force the controls panel to always-expanded. Either both branches
|
|
// collapse by default (preferred), or a dedicated initial collapse
|
|
// is applied to liveControls regardless of MQL.
|
|
//
|
|
// We assert one of:
|
|
// (a) setExpanded called with the controls pair AND false in the
|
|
// unconditional / wide branch.
|
|
// (b) An explicit "liveControls" / .live-controls .is-collapsed
|
|
// initialization that runs at all viewports.
|
|
const wideBranch = /narrowMql\.matches[\s\S]{0,2000}/.exec(liveJs);
|
|
const elseBlockHasAlwaysExpanded =
|
|
/else\s*\{[\s\S]{0,800}?removeAttribute\(\s*['"]hidden['"]\s*\);[\s\S]{0,200}?remove\(\s*['"]is-collapsed['"]/.test(liveJs);
|
|
|
|
// Acceptable: code has been updated so the controls pair defaults
|
|
// collapsed even on desktop. We pass if either the explicit "always
|
|
// expanded" else-branch no longer applies to liveControls, or a
|
|
// separate desktop-default-collapse step is present.
|
|
const desktopCollapseHook =
|
|
/liveControls[\s\S]{0,400}?(is-collapsed|setAttribute\(\s*['"]hidden['"])/.test(liveJs) ||
|
|
/setExpanded\(\s*pairs\[1\][\s\S]{0,80}?,\s*false\s*\)/.test(liveJs) ||
|
|
/defaultCollapsed[\s\S]{0,80}?true/.test(liveJs);
|
|
|
|
assert(desktopCollapseHook,
|
|
'.live-controls defaults to collapsed at all viewports (not just ≤768px)');
|
|
}
|
|
|
|
// CSS supporting rule: the .is-collapsed → hide body rule must NOT be
|
|
// gated to ≤768px any more. Detect a top-level (non-media-scoped) rule.
|
|
{
|
|
// Find first top-level occurrence of `.live-controls.is-collapsed .live-controls-body`
|
|
// outside an @media block. Cheap test: split on @media and search the
|
|
// pre-media chunk.
|
|
const beforeFirstMedia = liveCss.split(/@media/)[0];
|
|
const ruleRe = /\.live-controls\.is-collapsed\s+\.live-controls-body[\s\S]{0,200}?display\s*:\s*none/;
|
|
// Either the rule exists outside any @media, OR the body class path
|
|
// (body.live-fullscreen) is what does the hiding (which the D-block
|
|
// already asserts). We accept the body-class path AND additionally
|
|
// a non-mobile-gated .is-collapsed rule for the pin-only default.
|
|
const collapsedOutsideMedia = ruleRe.test(beforeFirstMedia);
|
|
assert(collapsedOutsideMedia,
|
|
'.live-controls.is-collapsed → hides .live-controls-body at all viewports (rule lives outside @media max-width)');
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────
|
|
console.log('\n=== #1532 results ===');
|
|
console.log(` ${passed} passed, ${failed} failed`);
|
|
if (failed > 0) process.exit(1);
|