diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 27bc9d0b..cd1ff88f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -246,6 +246,8 @@ jobs: CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-theme-e2e.js 2>&1 | tee -a e2e-output.txt CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-default-sage-teal-e2e.js 2>&1 | tee -a e2e-output.txt CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-issue-1109-hamburger-dropdown-visible-e2e.js 2>&1 | tee -a e2e-output.txt + CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-live-layout-1178-1179-e2e.js 2>&1 | tee -a e2e-output.txt + CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-live-mql-leak-1180-e2e.js 2>&1 | tee -a e2e-output.txt - name: Collect frontend coverage (parallel) if: success() && github.event_name == 'push' diff --git a/public/live.css b/public/live.css index c5e67ea7..1fa0aa22 100644 --- a/public/live.css +++ b/public/live.css @@ -57,23 +57,74 @@ left: 12px; display: flex; align-items: center; - gap: 14px; + gap: 10px; background: color-mix(in srgb, var(--surface-1) 92%, transparent); backdrop-filter: blur(12px); - padding: 8px 16px; - border-radius: 10px; + padding: 4px 10px; + border-radius: 8px; border: 1px solid var(--border); box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255,255,255,0.04); + max-height: 40px; + box-sizing: border-box; } - -.live-title { - font-size: 14px; - font-weight: 800; - letter-spacing: 2px; - color: var(--text); +.live-header-body { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; +} +/* Critical strip (Mesh-Operator review #1180): beacon + pkt count are + always visible even when the collapsible body is hidden at narrow + widths. This is the ingest-state cue (red beacon = WS down) + the + one number operators check while the header is otherwise collapsed. */ +.live-header-critical { display: flex; align-items: center; gap: 8px; + flex-shrink: 0; +} +/* Toggle buttons (#1178, #1179) — hidden at wide viewports, visible at ≤768px. + Mesh-Operator review #1180: tap target ≥48×48 (#1060 floor + AGENTS glove + operability rule). Visible glyph stays small (decorative); transparent + padding expands the hit area without changing the visual chrome. */ +.live-header-toggle, +.live-controls-toggle { + display: none; + align-items: center; + justify-content: center; + min-width: 48px; + min-height: 48px; + /* Visible chrome stays compact; padding grows the hit area. */ + width: 48px; + height: 48px; + padding: 8px; + border: 1px solid var(--border); + border-radius: 8px; + background: color-mix(in srgb, var(--text) 8%, transparent); + color: var(--text); + font-size: 16px; + line-height: 1; + cursor: pointer; + flex-shrink: 0; +} +.live-header-toggle:hover, +.live-controls-toggle:hover { + background: color-mix(in srgb, var(--text) 14%, transparent); +} +.live-header-toggle:focus-visible, +.live-controls-toggle:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.live-title { + font-size: 12px; + font-weight: 800; + letter-spacing: 1.5px; + color: var(--text); + display: flex; + align-items: center; + gap: 6px; text-transform: uppercase; } @@ -100,9 +151,9 @@ .live-stat-pill { background: color-mix(in srgb, var(--text) 8%, transparent); border: 1px solid var(--border); - padding: 3px 10px; - border-radius: 20px; - font-size: 12px; + padding: 1px 8px; + border-radius: 16px; + font-size: 11px; color: var(--text-muted); white-space: nowrap; } @@ -287,11 +338,42 @@ font-size: 11px; color: var(--text-muted); align-items: center; - margin-left: 8px; + flex-wrap: wrap; } .live-toggles label { display: flex; align-items: center; gap: 3px; cursor: pointer; white-space: nowrap; } .live-toggles input { margin: 0; } +/* ---- Live controls cluster (#1179) ---- + * Pinned to bottom-right, above the VCR bar and the global bottom-nav. + * Reserves space for both env(safe-area-inset-bottom) and the bottom-nav + * (#1061, currently in PR #1174). When the bottom-nav lands the layout + * tracks its custom property (--bottom-nav-height); otherwise the + * fallback (56px) keeps the cluster clear of the VCR bar / bottom-nav + * region. + */ +.live-controls { + position: fixed; + right: 12px; + bottom: calc(78px + var(--bottom-nav-height, 56px) + env(safe-area-inset-bottom, 0px)); + z-index: 1000; + background: color-mix(in srgb, var(--surface-1) 92%, transparent); + backdrop-filter: blur(12px); + padding: 8px 12px; + border-radius: 10px; + border: 1px solid var(--border); + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255,255,255,0.04); + max-width: min(620px, calc(100vw - 24px)); + display: flex; + align-items: center; + gap: 8px; +} +.live-controls-body { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; +} + /* Region filter (#1045) inline in live header toggles */ .live-toggles .live-region-filter-container { display: inline-flex; align-items: center; } .live-toggles .live-region-filter-container .region-dropdown-trigger { font-size: inherit; padding: 2px 6px; } @@ -307,14 +389,29 @@ background: rgba(59, 130, 246, 0.2) !important; } -/* ---- Medium breakpoint (#279) ---- */ +/* ---- Medium breakpoint (#279) + collapse toggles (#1178, #1179) ---- */ @media (max-width: 768px) { .live-feed { width: 280px; max-height: 200px; } .live-node-detail { width: 260px; } .live-legend { font-size: 10px; padding: 8px 10px; } - .live-header { gap: 8px; padding: 6px 12px; } - .live-stat-pill { font-size: 11px; padding: 2px 8px; } + .live-header { gap: 6px; padding: 4px 8px; max-height: none; min-height: 48px; } + .live-stat-pill { font-size: 11px; padding: 1px 7px; } .live-toggles { font-size: 10px; gap: 6px; } + + /* Show toggle buttons */ + .live-header-toggle, + .live-controls-toggle { display: inline-flex; } + + /* When collapsed, hide the body */ + .live-header.is-collapsed .live-header-body, + .live-controls.is-collapsed .live-controls-body { display: none; } + .live-header.is-collapsed { gap: 0; padding: 4px 6px; } + .live-controls.is-collapsed { padding: 6px; } + + /* Expanded body on narrow: stack so it never overflows the cluster */ + .live-controls.is-expanded { max-width: calc(100vw - 24px); } + .live-controls.is-expanded .live-controls-body { flex-wrap: wrap; } + .live-controls.is-expanded .live-toggles { flex-wrap: wrap; max-height: 50vh; overflow-y: auto; } } /* ---- Responsive ---- */ diff --git a/public/live.js b/public/live.js index a5a50bf3..b3b04b93 100644 --- a/public/live.js +++ b/public/live.js @@ -860,17 +860,27 @@