diff --git a/e2e/support/open-search.ts b/e2e/support/open-search.ts index 24e9ca4..3cf4d07 100644 --- a/e2e/support/open-search.ts +++ b/e2e/support/open-search.ts @@ -5,6 +5,10 @@ import { Locator, Page } from '@playwright/test'; * type has to open it first — the way a user does, or with the `/` shortcut. Returns the field. */ export async function openSearch(page: Page): Promise { - await page.getByRole('tab', { name: 'Search' }).click(); + // Clicking the active tab folds it closed, so only click when it is not already open. + const tab = page.getByRole('tab', { name: 'Search' }); + if ((await tab.getAttribute('aria-selected')) !== 'true') { + await tab.click(); + } return page.getByPlaceholder('Search stars, planets, exoplanets…'); } diff --git a/src/app/features/galaxy-system/galaxy-system-scene.component.ts b/src/app/features/galaxy-system/galaxy-system-scene.component.ts index 6d09c30..4004f33 100644 --- a/src/app/features/galaxy-system/galaxy-system-scene.component.ts +++ b/src/app/features/galaxy-system/galaxy-system-scene.component.ts @@ -648,14 +648,20 @@ export class GalaxySystemSceneComponent implements AfterViewInit, OnDestroy { /** Refreshes the readout panel for whichever scale the view is currently at. */ /** * Shows or hides the layers that hold still between frames: the label layer and the system - * view's orbits and grid. The galaxy grids, deep-sky shell and skybox are crossfaded every - * frame instead, so their toggles live in `updateGalacticCrossfade`. + * view's orbits and grid. The galaxy grids and deep-sky shell are crossfaded every frame + * instead, so their toggles live in `updateGalacticCrossfade`. The skybox is both: the + * crossfade rewrites its intensity while the galaxy is up, but the crossfade is parked in + * system view, where the sky is still on screen — so it is also set here, once, on toggle. */ private applyDisplay(display: HudDisplay): void { if (this.labelOverlay) { this.labelOverlay.domElement.style.display = display.labels ? '' : 'none'; } this.systemRenderer?.setLayerVisibility({ orbits: display.orbits, grid: display.grid }); + // The effect that calls this fires once at construction, before the engine has a scene. + if (this.engine.isInitialized) { + this.engine.getScene().backgroundIntensity = display.sky ? 1 - this.galacticStrength : 0; + } } private updateHud(camera: THREE.PerspectiveCamera): void { diff --git a/src/app/features/hud/hud-dock.component.ts b/src/app/features/hud/hud-dock.component.ts index 7ad9a09..ad27185 100644 --- a/src/app/features/hud/hud-dock.component.ts +++ b/src/app/features/hud/hud-dock.component.ts @@ -38,9 +38,11 @@ const TAB_LABELS: Record = { search: 'Search', readout: 'Readou /** Tailwind's `sm` breakpoint: below it the dock is a bare tab strip and its panel is a sheet. */ const WIDE_VIEWPORT = '(min-width: 640px)'; +/** One live query, read on every pointer-down, rather than a new MediaQueryList per read. */ +const wideViewportQuery = typeof window !== 'undefined' && typeof window.matchMedia === 'function' ? window.matchMedia(WIDE_VIEWPORT) : null; function isWideViewport(): boolean { - return typeof window === 'undefined' || typeof window.matchMedia !== 'function' ? true : window.matchMedia(WIDE_VIEWPORT).matches; + return wideViewportQuery?.matches ?? true; } /**