From a08065f2826f046c83a507adf121fb7dbca182cb Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Wed, 9 Sep 2026 01:45:06 -0400 Subject: [PATCH] zik try dis --- .../the_galaxy/assets/mobile/css/material.css | 100 ++++++++++++++++-- .../the_galaxy/assets/mobile/index.html | 1 + .../system/the_galaxy/assets/mobile/js/app.js | 77 ++++++++++++-- .../the_galaxy/tests/test_ui_vue_frontend.py | 74 +++++++++++++ 4 files changed, 236 insertions(+), 16 deletions(-) diff --git a/starpilot/system/the_galaxy/assets/mobile/css/material.css b/starpilot/system/the_galaxy/assets/mobile/css/material.css index d3c371518..ac27f505e 100644 --- a/starpilot/system/the_galaxy/assets/mobile/css/material.css +++ b/starpilot/system/the_galaxy/assets/mobile/css/material.css @@ -36,6 +36,7 @@ --glass-border-light: rgba(255, 255, 255, 0.2); --glass-shadow: 0 8px 24px 0 rgba(0, 0, 0, 0.35); --glass-blur: blur(4px); + --card-scroll-bg: rgba(22, 18, 38, 0.95); --glass-text: #f3f2f8; --glass-accent: #9d72ff; @@ -105,6 +106,7 @@ --surface-container: rgba(255, 255, 255, 0.75); --surface-container-high: rgba(255, 255, 255, 0.95); --surface-container-low: rgba(240, 243, 252, 0.75); + --card-scroll-bg: rgba(245, 246, 252, 0.96); --on-surface: #171526; --on-surface-variant: #5f5b7d; @@ -186,6 +188,8 @@ body { pointer-events: none; transform: translateZ(0); will-change: transform; + isolation: isolate; + contain: strict; } #galaxy-bg::before { @@ -204,7 +208,6 @@ body { opacity: 0.5; will-change: transform; transform: translate3d(var(--gx-para-x), 0, 0); - animation: galaxy-drift-smooth 160s linear infinite; } #galaxy-bg::after { @@ -221,7 +224,6 @@ body { opacity: 0.6; will-change: transform; transform: translate3d(var(--gx-para-x), 0, 0); - animation: galaxy-drift-smooth 100s linear infinite; } #galaxy-bg .galaxy-hero { @@ -234,18 +236,14 @@ body { animation: galaxy-twinkle 6s ease-in-out infinite alternate; } -@keyframes galaxy-drift-smooth { - from { transform: translate3d(0, 0, 0); } - to { transform: translate3d(0, -400px, 0); } -} - @keyframes galaxy-twinkle { 0%, 100% { opacity: 0.3; transform: scale(0.9); } 50% { opacity: 1; transform: scale(1.2); } } @media (prefers-reduced-motion: reduce) { - #galaxy-bg, #galaxy-bg::before, #galaxy-bg::after, #galaxy-bg .galaxy-hero { + #galaxy-bg, #galaxy-bg::before, #galaxy-bg::after, #galaxy-bg .galaxy-hero, + #galaxy-stars, #galaxy-stars .galaxy-hero { animation: none !important; } } @@ -306,6 +304,8 @@ ul { list-style: none; margin: 0; padding: 0; } position: sticky; top: 0; z-index: var(--z-appbar); + transform: translateZ(0); + isolation: isolate; } .gx-appbar__pill { @@ -513,7 +513,10 @@ ul { list-style: none; margin: 0; padding: 0; } position: fixed; bottom: calc(18px + env(safe-area-inset-bottom, 0px)); left: 50%; - transform: translateX(-50%); + transform: translateX(-50%) translateZ(0); + will-change: transform; + isolation: isolate; + contain: layout style; z-index: 1000; display: flex; @@ -589,7 +592,81 @@ ul { list-style: none; margin: 0; padding: 0; } box-shadow: var(--elev-1); margin-bottom: var(--sp-4); overflow: hidden; - transition: transform var(--motion-fast), box-shadow var(--motion-fast); + transition: transform var(--motion-fast), box-shadow var(--motion-fast), background-color 0.15s ease-out; +} + +/* Surgical content-visibility for top-level non-collapsible cards */ +.dh-card:not(.dh-stat), +.gx-content > div > .gx-card { + content-visibility: auto; + contain-intrinsic-size: 280px; + contain-intrinsic-size: auto 280px; +} + +.dh-stat { + content-visibility: auto; + contain-intrinsic-size: 110px; + contain-intrinsic-size: auto 110px; +} + +/* Disarm containment on collapsible sections and nested cards */ +.gx-card:has(.gx-section__body), +.gx-card .gx-card, +.gx-tree-node .gx-card { + content-visibility: visible !important; + contain-intrinsic-size: none !important; +} + +/* Velocity-Adaptive Glass: bypass blur on scrolling cards with contrast boost */ +body.is-scrolling .gx-card, +body.is-scrolling .gx-tile { + backdrop-filter: none !important; + -webkit-backdrop-filter: none !important; + background: var(--card-scroll-bg) !important; +} + +/* Layer 2: Celestial Starlight Canopy */ +#galaxy-stars { + position: fixed; + inset: 0; + z-index: 20; + pointer-events: none; + overflow: hidden; + contain: strict; + transform: translateZ(0); +} + +#galaxy-stars .galaxy-hero { + position: absolute; + border-radius: 50%; + background: radial-gradient( + circle, + rgba(255, 255, 255, 0.95) 0%, + rgba(157, 114, 255, 0.65) 35%, + rgba(77, 232, 232, 0.2) 65%, + transparent 100% + ); + box-shadow: + 0 0 8px rgba(157, 114, 255, 0.4), + 0 0 3px rgba(255, 255, 255, 0.8); + mix-blend-mode: screen; + will-change: transform, opacity; + animation: galaxy-twinkle 6s ease-in-out infinite alternate; +} + +/* Keep decorative animation out of modal backdrops and drawer content. */ +body:has(.gx-scrim, .gx-underlay, .gx-drawer.open) #galaxy-stars { + display: none; +} + +[data-theme="light"] #galaxy-stars { + opacity: 0.4; +} + +[data-theme="light"] #galaxy-stars .galaxy-hero { + background: radial-gradient(circle at center, #7849e8 0%, #b38fff 60%, rgba(120, 73, 232, 0) 100%); + box-shadow: 0 0 4px rgba(120, 73, 232, 0.45), 0 0 8px rgba(77, 232, 232, 0.3); + mix-blend-mode: normal; } .gx-unit-note { @@ -1459,6 +1536,9 @@ input[type="color"].gx-color { .gx-fade-enter-active, .gx-fade-leave-active { transition: opacity var(--motion-base); } .gx-fade-enter-from, .gx-fade-leave-to { opacity: 0; } +/* Keep the underlay (and star suppression) until the drawer finishes closing. */ +.gx-underlay.gx-fade-leave-active { transition: opacity var(--motion-slow); } + .gx-slide-enter-active, .gx-slide-leave-active { transition: transform var(--motion-base), opacity var(--motion-base); } diff --git a/starpilot/system/the_galaxy/assets/mobile/index.html b/starpilot/system/the_galaxy/assets/mobile/index.html index 6bad72762..64e6eaa55 100644 --- a/starpilot/system/the_galaxy/assets/mobile/index.html +++ b/starpilot/system/the_galaxy/assets/mobile/index.html @@ -34,6 +34,7 @@
+
diff --git a/starpilot/system/the_galaxy/assets/mobile/js/app.js b/starpilot/system/the_galaxy/assets/mobile/js/app.js index bbda847e3..860be5c9e 100644 --- a/starpilot/system/the_galaxy/assets/mobile/js/app.js +++ b/starpilot/system/the_galaxy/assets/mobile/js/app.js @@ -90,17 +90,82 @@ app.mount("#galaxy-app") initRouter() +// Disable card blur during document scrolling. ;(() => { - const bg = document.getElementById("galaxy-bg") - if (!bg) return + let timer = null + let scrollEnded = true + const touches = new Set() + const nativeScrollEnd = "onscrollend" in document + const body = document.body + + const isModalEvent = (e) => { + const t = e.target + return t instanceof Element && t.closest(".gx-scrim, .gx-sheet, .gx-dialog, .gx-drawer") !== null + } + + const setScrolling = (active) => { + if (active) { + if (!body.classList.contains("is-scrolling")) body.classList.add("is-scrolling") + } else if (!touches.size && body.classList.contains("is-scrolling")) { + body.classList.remove("is-scrolling") + } + } + + const scheduleRestore = () => { + clearTimeout(timer) + timer = setTimeout(() => setScrolling(false), 120) + } + + document.addEventListener("scroll", () => { + scrollEnded = false + setScrolling(true) + if (!nativeScrollEnd) scheduleRestore() + }, { passive: true }) + + document.addEventListener("scrollend", () => { + scrollEnded = true + clearTimeout(timer) + setScrolling(false) + }, { passive: true }) + + window.addEventListener("touchstart", (e) => { + if (isModalEvent(e)) return + for (const touch of e.changedTouches) touches.add(touch.identifier) + }, { passive: true }) + + const releaseTouches = (e) => { + for (const touch of e.changedTouches) touches.delete(touch.identifier) + if (scrollEnded) setScrolling(false) + else if (!nativeScrollEnd && !touches.size) scheduleRestore() + } + window.addEventListener("touchend", releaseTouches, { passive: true }) + window.addEventListener("touchcancel", releaseTouches, { passive: true }) + + window.addEventListener("hashchange", () => { + scrollEnded = true + touches.clear() + clearTimeout(timer) + body.classList.remove("is-scrolling") + }, { passive: true }) +})() + +// Layer 2 Celestial Starlight Canopy Spawner +;(() => { + const container = document.getElementById("galaxy-stars") || document.getElementById("galaxy-bg") + if (!container) return for (let i = 0; i < 14; i++) { const s = document.createElement("i") s.className = "galaxy-hero" - s.style.left = (Math.random() * 100).toFixed(2) + "%" - s.style.top = (Math.random() * 100).toFixed(2) + "%" + const inTopCanopy = Math.random() < 0.7 + const topPercent = inTopCanopy ? Math.random() * 28 : Math.random() * 95 + const leftPercent = inTopCanopy + ? Math.random() * 96 + 2 + : Math.random() < 0.5 ? Math.random() * 12 + 2 : Math.random() * 12 + 86 + s.style.left = leftPercent.toFixed(2) + "%" + s.style.top = topPercent.toFixed(2) + "%" s.style.animationDelay = (Math.random() * 4).toFixed(2) + "s" - const size = Math.random() > 0.6 ? 3 : 2 + const size = Math.random() > 0.5 ? 6 : 4 s.style.width = s.style.height = size + "px" - bg.appendChild(s) + container.appendChild(s) } })() diff --git a/starpilot/system/the_galaxy/tests/test_ui_vue_frontend.py b/starpilot/system/the_galaxy/tests/test_ui_vue_frontend.py index 7e212a1f8..6f7952719 100644 --- a/starpilot/system/the_galaxy/tests/test_ui_vue_frontend.py +++ b/starpilot/system/the_galaxy/tests/test_ui_vue_frontend.py @@ -538,6 +538,80 @@ def test_ui_mobile_polish_regressions(): assert 'tr("Longitudinal Maneuver Mode")' not in c4_developer +@pytest.mark.parametrize("native_scrollend", [False, True]) +def test_scroll_coordinator_gesture_lifecycle(native_scrollend): + node = _node_exe() + if node is None: + pytest.skip("no node.js runtime available") + script = r""" +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const vm = require('node:vm'); +const native = process.argv[2] === 'true'; +const source = fs.readFileSync(process.argv[1], 'utf8'); +const handlers = { document: {}, window: {} }; +const classes = new Set(); +const timers = new Map(); +let nextTimer = 0; +class Element { + constructor(modal = false) { this.modal = modal; } + closest() { return this.modal ? this : null; } +} +const document = { + body: { classList: { + contains: k => classes.has(k), add: k => classes.add(k), remove: k => classes.delete(k), + } }, + addEventListener: (name, fn) => { handlers.document[name] = fn; }, +}; +if (native) document.onscrollend = null; +vm.runInNewContext(source.slice(source.indexOf('// Disable card blur'), source.indexOf('// Layer 2')), { + document, Element, + window: { addEventListener: (name, fn) => { handlers.window[name] = fn; } }, + setTimeout: fn => { timers.set(++nextTimer, fn); return nextTimer; }, + clearTimeout: id => timers.delete(id), +}); +const fire = (scope, name, ids = [], modal = false) => handlers[scope][name]?.({ + target: new Element(modal), changedTouches: ids.map(identifier => ({ identifier })), +}); +const tick = () => { const pending = [...timers.values()]; timers.clear(); pending.forEach(fn => fn()); }; +const active = () => classes.has('is-scrolling'); +fire('window', 'wheel'); +assert.equal(active(), false, 'wheel without document movement'); +fire('window', 'touchstart', [1, 2]); +fire('document', 'scroll'); +fire('window', 'touchend', [1]); +fire('window', 'touchstart', [3], true); +fire('window', 'touchend', [3], true); +tick(); +assert.equal(active(), true, 'remaining page finger holds blur disabled'); +fire('window', 'touchcancel', [2]); +tick(); +assert.equal(active(), native, 'native completion must be authoritative'); +fire('document', 'scrollend'); +assert.equal(active(), false); +fire('window', 'touchstart', [4]); +fire('document', 'scroll'); +fire('document', 'scrollend'); +assert.equal(active(), true, 'hold survives completion'); +fire('window', 'touchend', [4]); +assert.equal(active(), false, 'release after completion cannot leave state stuck'); +fire('document', 'scroll'); +fire('window', 'hashchange'); +tick(); +assert.equal(active(), false, 'navigation clears pending gesture'); +fire('document', 'scroll'); +tick(); +assert.equal(active(), native, 'fallback only on unsupported browsers'); +fire('document', 'scrollend'); +assert.equal(active(), false); +""" + result = subprocess.run( + [node, "-e", script, str(UI_ROOT / "js/app.js"), str(native_scrollend).lower()], + capture_output=True, text=True, + ) + assert result.returncode == 0, result.stdout + result.stderr + + def _node_exe(): candidates = [ shutil.which("node"),