From e35483d37d63ee92b9a6e14d23b731529c29d28e Mon Sep 17 00:00:00 2001 From: Said Turksever Date: Mon, 2 Nov 2026 16:43:49 +0000 Subject: [PATCH] Give Utilities the room Data Layers gets in Cockpit Utilities stays in Cockpit for Directions, but only Data Layers folded the Cockpit context rail away when opened, set its top below the open panel and took Cyber's Cockpit size. Utilities now does all three, and Contact expanding closes it as it closes Data Layers. --- src/cockpitMarkup.test.mjs | 11 ++++++++--- src/ui/cockpitLayout.js | 3 ++- src/ui/cyberTheme.test.mjs | 2 +- src/ui/panelChrome.js | 9 +++++++-- src/ui/radioBindings.js | 2 ++ src/ui/styles/cyber.css | 9 ++++++--- 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/cockpitMarkup.test.mjs b/src/cockpitMarkup.test.mjs index 034cf3d00..70641642c 100644 --- a/src/cockpitMarkup.test.mjs +++ b/src/cockpitMarkup.test.mjs @@ -633,17 +633,22 @@ test('Cockpit side surfaces behave as two single-expanded accordions', () => { ); assert.match( radioBindings, - /'gev:cockpit-context-expanded'[\s\S]*?setPanelCollapsed\('data-panel', true\);/, + /'gev:cockpit-context-expanded'[\s\S]*?setPanelCollapsed\('data-panel', true\);\s*this\.actions\.setPanelCollapsed\('utilities-panel', true\);/, ); assert.match( ui, - /!nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel =\s*!this\.cockpitView\.contextCollapsed[\s\S]*?this\.cockpitView\.setContextCollapsed\(true\);/, + /!nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*COCKPIT_LEFT_PANEL_IDS\.has\(panelId\)[\s\S]*?_cockpitContextCollapsedForDataPanel =\s*!this\.cockpitView\.contextCollapsed[\s\S]*?this\.cockpitView\.setContextCollapsed\(true\);/, ); assert.match( ui, - /nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel[\s\S]*?this\.cockpitView\.setContextCollapsed\(false\);/, + /nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*COCKPIT_LEFT_PANEL_IDS\.has\(panelId\)[\s\S]*?_cockpitContextCollapsedForDataPanel[\s\S]*?this\.cockpitView\.setContextCollapsed\(false\);/, 'closing Data Layers must restore Contact only after an automatic collapse', ); + assert.match( + ui, + /const COCKPIT_LEFT_PANEL_IDS = new Set\(\['data-panel', 'utilities-panel'\]\);/, + 'Utilities folds Contact away in Cockpit as Data Layers does', + ); assert.match( setContextCollapsed.toString(), /const wasCollapsed = this\.contextCollapsed;[\s\S]*?this\.active && wasCollapsed && !this\.contextCollapsed[\s\S]*?'gev:cockpit-context-expanded'/, diff --git a/src/ui/cockpitLayout.js b/src/ui/cockpitLayout.js index c0a00299d..2c5300b8a 100644 --- a/src/ui/cockpitLayout.js +++ b/src/ui/cockpitLayout.js @@ -84,8 +84,9 @@ export function syncContextLayout() { return; } const defaultTop = Math.max(255, Math.min(300, window.innerHeight * 0.28)); + // An open left panel Cockpit keeps (Data Layers or Utilities). const dataPanel = document.querySelector( - '#left-panel-stack > #data-panel:not(.collapsed)', + '#left-panel-stack > :is(#data-panel, #utilities-panel):not(.collapsed)', ); let desiredTop = defaultTop; if (this.contextCollapsed && dataPanel) { diff --git a/src/ui/cyberTheme.test.mjs b/src/ui/cyberTheme.test.mjs index eabd3df38..75746b6e3 100644 --- a/src/ui/cyberTheme.test.mjs +++ b/src/ui/cyberTheme.test.mjs @@ -854,7 +854,7 @@ test('Cyber Cockpit keeps Data Layers inside its bounded rail', () => { ); assert.match( cyberStyles, - /body\.cockpit-mode #left-panel-stack > #data-panel \{[\s\S]*?max-height: min\(64vh, 610px\);/, + /body\.cockpit-mode\s+#left-panel-stack\s+> :is\(#data-panel, #utilities-panel\) \{[\s\S]*?max-height: min\(64vh, 610px\);/, ); }); diff --git a/src/ui/panelChrome.js b/src/ui/panelChrome.js index f2adef3bf..e83083865 100644 --- a/src/ui/panelChrome.js +++ b/src/ui/panelChrome.js @@ -25,6 +25,11 @@ const SHARE_PANEL_STATE_SPECS = Object.freeze([ { id: 'pp-toggles' }, { id: 'param-slider-panel' }, ]); +/** + * The left panels Cockpit keeps (Data Layers, and Utilities for Directions): + * opening one there folds the Cockpit context rail away until it closes. + */ +const COCKPIT_LEFT_PANEL_IDS = new Set(['data-panel', 'utilities-panel']); /** The command dock trays with a pin button. */ const PINNABLE_PANEL_IDS = new Set( SHARE_PANEL_STATE_SPECS.filter((spec) => spec.pinnable).map( @@ -497,7 +502,7 @@ export class PanelChrome { if ( !nextCollapsed && this.cockpitView?.active && - panelId === 'data-panel' + COCKPIT_LEFT_PANEL_IDS.has(panelId) ) { this._cockpitContextCollapsedForDataPanel = !this.cockpitView.contextCollapsed; @@ -528,7 +533,7 @@ export class PanelChrome { if ( nextCollapsed && this.cockpitView?.active && - panelId === 'data-panel' && + COCKPIT_LEFT_PANEL_IDS.has(panelId) && this._cockpitContextCollapsedForDataPanel ) { this._cockpitContextCollapsedForDataPanel = false; diff --git a/src/ui/radioBindings.js b/src/ui/radioBindings.js index 05a211537..ac3c7c151 100644 --- a/src/ui/radioBindings.js +++ b/src/ui/radioBindings.js @@ -380,7 +380,9 @@ export function bindRadioControls() { window, 'gev:cockpit-context-expanded', () => { + // Contact takes the room the open left panel had. this.actions.setPanelCollapsed('data-panel', true); + this.actions.setPanelCollapsed('utilities-panel', true); }, tunerListenerOptions, ); diff --git a/src/ui/styles/cyber.css b/src/ui/styles/cyber.css index dbaf12146..4957cd769 100644 --- a/src/ui/styles/cyber.css +++ b/src/ui/styles/cyber.css @@ -2771,7 +2771,10 @@ border-inline-start: 0; } -:root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack > #data-panel { +:root[data-ui-theme='cyber'] + body.cockpit-mode + #left-panel-stack + > :is(#data-panel, #utilities-panel) { --panel-expanded-width: var(--cyber-panel-width); width: var(--cyber-panel-width); max-height: min(64vh, 610px); @@ -2780,7 +2783,7 @@ :root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack - > #data-panel.collapsed { + > :is(#data-panel, #utilities-panel).collapsed { width: var(--cyber-panel-collapsed-width); } @@ -2813,7 +2816,7 @@ :root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack - > #data-panel:not(.collapsed) { + > :is(#data-panel, #utilities-panel):not(.collapsed) { flex: 0 0 var(--cyber-cockpit-panel-height, 40vh); height: var(--cyber-cockpit-panel-height, 40vh); max-height: var(--cyber-cockpit-panel-height, 40vh);