mirror of
https://github.com/n30nex/CartoLite-Server.git
synced 2026-10-17 09:19:55 +00:00
Keep dock controls reachable and avoid redundant backdrop rendering
This commit is contained in:
@@ -55,7 +55,7 @@ test('Netgraph renders stable topology, inspection, and synchronized musical hop
|
||||
await expect(stage).toHaveAttribute('data-last-region-traffic', 'long-haul');
|
||||
await expect.poll(() => stage.getAttribute('data-active-region-labels').then(Number), { timeout: 8_000 }).toBeGreaterThan(0);
|
||||
await expect(stage).toHaveAttribute('data-active-region-roles', /OUT/);
|
||||
await expect.poll(() => canvasHasPixels(page, '#packet-canvas'), { timeout: 5_000 }).toBe(true);
|
||||
await expect.poll(async () => await canvasHasPixels(page, '#packet-canvas') || await canvasHasPixels(page, '#netgraph-residue-canvas') || await gpuHasPixels(page), { timeout: 5_000 }).toBe(true);
|
||||
|
||||
await page.locator('#find-button').click();
|
||||
await page.locator('#node-search').fill('Alpha');
|
||||
@@ -399,3 +399,14 @@ async function instrumentAudioContext(page: Page): Promise<void> {
|
||||
Object.defineProperty(window, 'AudioContext', { configurable: true, value: TestAudioContext });
|
||||
});
|
||||
}
|
||||
|
||||
async function gpuHasPixels(page: Page): Promise<boolean> {
|
||||
return page.locator('.gpu-packet-canvas').evaluate(element => new Promise<boolean>(resolve => requestAnimationFrame(() => {
|
||||
const canvas = element as HTMLCanvasElement;
|
||||
const gl = canvas.getContext('webgl2');
|
||||
if (!gl || gl.isContextLost()) { resolve(false); return; }
|
||||
const pixels = new Uint8Array(canvas.width * canvas.height * 4);
|
||||
gl.readPixels(0,0,canvas.width,canvas.height,gl.RGBA,gl.UNSIGNED_BYTE,pixels);
|
||||
resolve(pixels.some((value,index)=>index%4===3&&value>0));
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ test('focuses recent route neighbors and clears selection on the map', async ({
|
||||
await openMapOptions(page);
|
||||
await routesButton.click();
|
||||
await expect(map).toHaveAttribute('data-routes-visible', 'true');
|
||||
if (mobile) await closeLayers(page);
|
||||
await closeLayers(page);
|
||||
const box = await canvas.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
if (!box) return;
|
||||
@@ -512,7 +512,7 @@ test('focuses recent route neighbors and clears selection on the map', async ({
|
||||
if (mobile) await openLayers(page);
|
||||
await openMapOptions(page);
|
||||
await page.locator('#route-window').selectOption('24h');
|
||||
if (mobile) await closeLayers(page);
|
||||
await closeLayers(page);
|
||||
await expect(map).toHaveAttribute('data-neighbor-route-count', '2');
|
||||
await expect(map).toHaveAttribute('data-focused-route-count', '2');
|
||||
await expect(inspector.locator('.neighbor-row')).toHaveCount(2);
|
||||
@@ -556,7 +556,7 @@ test('focuses recent route neighbors and clears selection on the map', async ({
|
||||
await routesButton.click();
|
||||
await expect(map).toHaveAttribute('data-routes-visible', 'true');
|
||||
await expect(map).toHaveAttribute('data-render-state', 'idle');
|
||||
if (mobile) await closeLayers(page);
|
||||
await closeLayers(page);
|
||||
await inspectRoute(page, alphaPoint, charliePoint, mobile);
|
||||
await expect(map).toHaveAttribute('data-hovered-route-id', 'a-c');
|
||||
await expect(tooltip).toHaveAttribute('data-kind', 'route');
|
||||
@@ -568,7 +568,7 @@ test('focuses recent route neighbors and clears selection on the map', async ({
|
||||
await expect(tooltip).toBeHidden();
|
||||
await openMapOptions(page);
|
||||
await page.locator('#route-window').selectOption('24h');
|
||||
if (mobile) await closeLayers(page);
|
||||
await closeLayers(page);
|
||||
|
||||
await clickPoint(page, bravoPoint, mobile);
|
||||
await expect(map).toHaveAttribute('data-selected-node-id', 'b');
|
||||
|
||||
@@ -77,3 +77,8 @@
|
||||
:root[data-cinematic] .controls #layers-panel { height:min(750px,calc(100dvh - var(--dock-height) - var(--dock-bottom) - 80px)); max-height:calc(100dvh - var(--dock-height) - var(--dock-bottom) - 80px); }
|
||||
:root[data-cinematic] .controls :is(.find-panel,.sound-panel,.display-panel) { max-height:calc(100dvh - var(--dock-height) - var(--dock-bottom) - 80px); }
|
||||
@media(max-width:900px),(pointer:coarse) { :root[data-cinematic] :is(.node-inspector-sheet,.follow-card,.legend,.route-legend) { bottom:calc(var(--dock-height) + var(--dock-bottom) + 12px); } }
|
||||
|
||||
/* Opaque panels need no backdrop readback over animated geography. */
|
||||
:root[data-cinematic] .glass { backdrop-filter:none; -webkit-backdrop-filter:none; }
|
||||
:root[data-cinematic] .controls .zoom-controls { position:static; display:flex; flex:none; width:88px; padding:0; border:0; background:transparent; box-shadow:none; border-radius:10px; }
|
||||
:root[data-cinematic] .controls .zoom-controls .control-button { width:44px; min-width:44px; height:44px; padding:0; font-size:20px; }
|
||||
|
||||
@@ -20,6 +20,8 @@ export function mountCinematicDock(): void {
|
||||
const labs = topbar.querySelector<HTMLAnchorElement>('#labs-link');
|
||||
if (labs) { labs.classList.add('control-button'); navigation.append(labs); }
|
||||
controls.prepend(navigation);
|
||||
const zoomControls = document.querySelector<HTMLElement>('.zoom-controls');
|
||||
if (zoomControls) controls.append(zoomControls);
|
||||
const sizeDock=()=>document.documentElement.style.setProperty('--dock-height',`${controls.offsetHeight}px`);
|
||||
const dockResize=new ResizeObserver(sizeDock);dockResize.observe(controls);sizeDock();
|
||||
window.addEventListener('pagehide',()=>dockResize.disconnect());
|
||||
@@ -43,7 +45,7 @@ export function mountCinematicDock(): void {
|
||||
let closingURL = location.href;
|
||||
let pendingNavigation: string | undefined;
|
||||
const syncHistory = () => {
|
||||
if (closingHistory) return;
|
||||
if (closingHistory) { closingURL = location.href; return; }
|
||||
const open = openMenu();
|
||||
const current = history.state?.cartolitePanel;
|
||||
if (open && current !== open) {
|
||||
|
||||
Reference in New Issue
Block a user