From 733b227c01c509aa239dee0f1f14ac6e3d07c588 Mon Sep 17 00:00:00 2001 From: Manjunath Paparaju Date: Fri, 13 Nov 2026 10:31:42 +0530 Subject: [PATCH 01/13] Restyle the cockpit side panels The cockpit's Display and Radio panels get the compact dark styling of the rest of the shell, a header with the edge switch and collapse, and a rail. Co-authored-by: Sameh Khamis --- src/cockpitMarkup.test.mjs | 2 +- src/ui/cockpitBriefPages.js | 8 +- src/ui/styles/cockpit-panels.css | 668 ++++++++++++++++++ src/ui/views/panels/CockpitBriefing.vue | 3 +- .../views/panels/CockpitUtilityControls.vue | 28 + 5 files changed, 699 insertions(+), 10 deletions(-) create mode 100644 src/ui/styles/cockpit-panels.css diff --git a/src/cockpitMarkup.test.mjs b/src/cockpitMarkup.test.mjs index 3f461c392..d4167f043 100644 --- a/src/cockpitMarkup.test.mjs +++ b/src/cockpitMarkup.test.mjs @@ -1275,7 +1275,7 @@ test('cockpit briefing cycle control keeps its state as the accessible name', () assert.match(match[0], />CYCLE OFF<\/button>/); assert.match( match[0], - /title="Cycle briefing pages automatically every 9 seconds \(Signals → News → Local\)\./, + /title="Cycle briefing pages automatically every 9 seconds \(Signals → Local\)\./, ); // The presented label is the state; the help is only the tooltip. diff --git a/src/ui/cockpitBriefPages.js b/src/ui/cockpitBriefPages.js index 9f75c83d8..943255eae 100644 --- a/src/ui/cockpitBriefPages.js +++ b/src/ui/cockpitBriefPages.js @@ -31,12 +31,6 @@ export const COCKPIT_BRIEF_PAGES = [ subtitleKey: msg('OBSERVED / MAPPED PINGS'), sourceKey: msg('SOURCE-BACKED EVENTS · NO SYNTHETIC NEWS'), }, - { - id: 'news', - kickerKey: msg('REGIONAL NEWS'), - subtitleKey: msg('LATEST LOCATION-MATCHED REPORTING'), - // The news footer comes from the news state (`news.footer`), not here. - }, { id: 'local', kickerKey: msg('LOCAL INFO'), @@ -52,7 +46,7 @@ export const CYCLE_HELP_KEYS = Object.freeze({ 'Stop automatic page cycling. Previous, Next, and the SIG/NEWS/LOCAL tabs stay available.', ), off: msg( - 'Cycle briefing pages automatically every 9 seconds (Signals → News → Local). Pauses while you hover or focus the panel. Live signal data refreshes continuously either way.', + 'Cycle briefing pages automatically every 9 seconds (Signals → Local). Pauses while you hover or focus the panel. Live signal data refreshes continuously either way.', ), }); diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css new file mode 100644 index 000000000..8dd9e2c9c --- /dev/null +++ b/src/ui/styles/cockpit-panels.css @@ -0,0 +1,668 @@ +/* ── Cockpit side windows in the app's panel look ─────────────────────── + Contact (left), the briefing carousel (right) and the Display / Radio + controls get the same smoked glass, 16px corners and card rows as the + main panels. They keep cockpit.css's slight 3D slant toward the centre, + and stand the same height. The text glow goes; the state colours on the + top edge (uncertain, lost) stay. */ + +.cockpit-context-window, +.cockpit-signal-window { + border: 1px solid rgba(255, 255, 255, 0.08); + border-top-color: rgba(255, 255, 255, 0.14); + border-radius: 16px; + background: rgba(12, 12, 20, 0.72); + box-shadow: + 0 8px 32px rgba(0, 0, 0, 0.42), + inset 0 1px 0 rgba(255, 255, 255, 0.04); + backdrop-filter: blur(24px) saturate(1.4); + -webkit-backdrop-filter: blur(24px) saturate(1.4); + text-shadow: none; + scrollbar-color: rgba(255, 255, 255, 0.18) transparent; +} + +/* Open, both windows share one compact size; their lists scroll. */ +.cockpit-context-window:not([data-collapsed='true']), +.cockpit-signal-window:not([data-collapsed='true']) { + width: min(280px, 23vw); + height: min(34vh, 330px); +} + +/* Their foot sits just above the bottom HUD readouts (MGRS / GSD), which + stand at clamp(84px, 11vh, 118px) and are about 38px tall. */ +.cockpit-context-window, +.cockpit-signal-window { + bottom: calc(clamp(84px, 11vh, 118px) + 54px); +} + +.cockpit-context-window[data-state='uncertain'] { + border-top-color: rgba(116, 219, 255, 0.72); +} + +.cockpit-context-window[data-state='lost'] { + border-top-color: rgba(213, 168, 99, 0.72); +} + +.cockpit-context-window[data-collapsed='true'], +.cockpit-signal-window[data-collapsed='true'] { + border-radius: 12px; + background: rgba(12, 12, 20, 0.66); +} + +/* Headers: a quiet hairline like the panel headers. */ +.cockpit-card-header { + border-bottom-color: rgba(255, 255, 255, 0.08); +} + +.cockpit-card-actions button { + border-color: rgba(255, 255, 255, 0.1); + border-radius: 6px; + background: rgba(255, 255, 255, 0.04); + color: rgba(224, 236, 242, 0.82); +} + +.cockpit-card-actions button:hover, +.cockpit-card-actions button:focus-visible { + border-color: rgba(0, 212, 255, 0.45); + background: rgba(0, 212, 255, 0.1); + color: #fff; +} + +/* Rows and tiles inside: the card style used in Context and Visual. */ +.cockpit-context-cohorts > div, +.cockpit-signal-list li, +.cockpit-news-list li, +.cockpit-local-grid > div { + border: 1px solid rgba(255, 255, 255, 0.06); + border-radius: 10px; + background: rgba(255, 255, 255, 0.035); +} + +.cockpit-route-card { + border-bottom-color: rgba(255, 255, 255, 0.08); +} + +/* Display / Radio: the closed launchers and the open panels match too. */ +body.cockpit-mode .cockpit-utility-control.is-expanded { + border-color: rgba(255, 255, 255, 0.08); + border-radius: 16px; + background: rgba(12, 12, 20, 0.72); + box-shadow: + 0 8px 32px rgba(0, 0, 0, 0.42), + inset 0 1px 0 rgba(255, 255, 255, 0.04); +} + +/* ── Cockpit uses the main page's rails ─────────────────────────────── + The left rail shows (Data Layers, Utilities; Authoring has no place in + first person, and the cockpit has its own Display control on the right in + place of Visual), one panel opens beside it, and the collapsed header + strips the cockpit used instead are hidden. */ +body.cockpit-mode #left-panel-stack > .gev-panel-rail--left { + display: block; +} + +body.cockpit-mode #left-panel-stack > .gev-panel-rail--left :is([data-rail-item='scene-panel'], [data-rail-item='pp-toggles']) { + display: none; +} + +body.cockpit-mode #left-panel-stack > [data-panel-id].collapsed { + display: none; +} + +/* Display and Radio as the main page's right rail: a column of labels and a + column of icon buttons, each reading as one glass strip. */ +body.cockpit-mode .cockpit-utility-controls { + gap: 0; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher { + display: flex; + justify-content: flex-end; + align-items: stretch; + gap: 6px; + width: auto; + min-height: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child, +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { + display: flex; + align-items: center; + box-sizing: border-box; + height: 40px; + border: 1px solid rgba(255, 255, 255, 0.08); + border-block-width: 0; + background: rgba(12, 12, 20, 0.72); + backdrop-filter: blur(24px) saturate(1.4); + -webkit-backdrop-filter: blur(24px) saturate(1.4); +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child { + width: 120px; + padding: 0 14px; + color: rgba(224, 236, 242, 0.72); + font: 9px/1 var(--font-mono); + letter-spacing: 0.16em; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-divider { + display: none; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { + justify-content: center; + width: 42px; + padding: 0; + border-radius: 0; + color: rgba(224, 236, 242, 0.82); + font-size: 0; + cursor: pointer; +} + +body.cockpit-mode .cockpit-utility-glyph::before { + font-family: 'Material Symbols Outlined'; + font-size: 18px; + font-weight: 400; + line-height: 1; + letter-spacing: normal; + -webkit-font-feature-settings: 'liga'; + font-feature-settings: 'liga'; +} + +body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-glyph { + font-size: 0; +} + +body.cockpit-mode #cockpit-display-toggle-btn::before { + content: 'tune'; +} + +body.cockpit-mode #cockpit-radio-toggle-btn::before { + content: 'radio'; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph:hover { + color: #fff; + background: rgba(0, 212, 255, 0.12); +} + +/* Round the ends of each strip and draw its top / bottom edge. */ +body.cockpit-mode .cockpit-utility-control:first-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { + padding-top: 4px; + border-top-width: 1px; + border-radius: 12px 12px 0 0; +} + +body.cockpit-mode .cockpit-utility-control:last-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { + padding-bottom: 4px; + border-bottom-width: 1px; + border-radius: 0 0 12px 12px; +} + +/* A strip left alone beside an open panel is rounded all round. */ +body.cockpit-mode .cockpit-utility-control.is-expanded + .cockpit-utility-control:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), +body.cockpit-mode .cockpit-utility-control:not(.is-expanded):has(+ .cockpit-utility-control.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { + padding-block: 4px; + border-block-width: 1px; + border-radius: 12px; +} + +body.cockpit-mode .cockpit-utility-control.is-expanded + .cockpit-utility-control:not(.is-expanded) { + margin-top: 7px; +} + +/* Map credits: one quiet centred line at the very bottom, under Reset / + Exit Cockpit, clear of the HUD readouts in the corners. */ +body.cockpit-mode #cesium-credits { + top: auto !important; + right: auto !important; + bottom: 8px !important; + left: 50% !important; + max-width: calc(100vw - 2rem) !important; + padding: 0.2rem 0.75rem !important; + border-radius: 999px !important; + transform: translateX(-50%); + white-space: nowrap; +} + +body.cockpit-mode #cesium-credits :is(.cesium-widget-credits, .cesium-credit-textContainer) { + display: inline-flex !important; + flex-wrap: nowrap !important; + align-items: center; + gap: 0.4rem; + white-space: nowrap; +} + +body.cockpit-mode #cesium-credits img { + height: 13px; +} + +/* Both rails start at one height, just under the top HUD text, so their top + edges line up. */ +body.cockpit-mode #left-panel-stack, +body.cockpit-mode #cockpit-utility-controls { + top: max(206px, 22vh) !important; +} + +/* Bottom HUD readouts: scale from their foot, and drop the three-line right + block (GSD / ALT / AIS) by one scaled line so both start level, as on the + main page. */ +body.cockpit-mode #intel-hud .hud-bottom-left { + transform-origin: left bottom; +} + +body.cockpit-mode #intel-hud .hud-bottom-right { + bottom: calc(clamp(84px, 11vh, 118px) - 20px); + transform-origin: right bottom; +} + +/* ── Live signals rows ────────────────────────────────────────────────── + Callsign on the left, time small at the top right, the detail under it. + The status colour is a slim inset bar (cyan for the aircraft you ride, + white for nearby, amber for military / warnings), not a bent border. */ +.cockpit-signal-list { + gap: 5px; +} + +.cockpit-signal-list li { + position: relative; + display: block; + padding: 8px 12px 8px 14px; + border: 1px solid rgba(255, 255, 255, 0.06); + box-shadow: inset 2px 0 0 rgba(244, 251, 255, 0.22); +} + +.cockpit-signal-list li:is(.track, .current) { + border-color: rgba(0, 212, 255, 0.22); + background: rgba(0, 212, 255, 0.06); + box-shadow: inset 2px 0 0 var(--cockpit-accent, #00d4ff); +} + +.cockpit-signal-list li:is(.nearby) { + border-color: rgba(255, 255, 255, 0.06); + box-shadow: inset 2px 0 0 rgba(244, 251, 255, 0.55); +} + +.cockpit-signal-list li:is(.military, .warning) { + border-color: rgba(255, 255, 255, 0.06); + box-shadow: inset 2px 0 0 #d5a863; +} + +.cockpit-signal-list li.actionable:is(:hover, :focus-within) { + border-color: rgba(0, 212, 255, 0.3); + background: rgba(0, 212, 255, 0.08); +} + +.cockpit-signal-list time { + position: absolute; + top: 9px; + right: 30px; + color: rgba(224, 236, 242, 0.4); + font: 7px/1 var(--font-mono); + letter-spacing: 0.06em; + font-variant-numeric: tabular-nums; +} + +.cockpit-signal-list li > div { + gap: 3px; +} + +.cockpit-signal-target { + grid-template-columns: minmax(0, 1fr) 14px; + gap: 8px; + font-size: 10.5px; + letter-spacing: 0.1em; +} + +.cockpit-signal-target-rule { + display: none; +} + +.cockpit-signal-target-chevron { + justify-self: end; + color: rgba(224, 236, 242, 0.5) !important; +} + +.cockpit-signal-list li.actionable:is(:hover, :focus-within) .cockpit-signal-target-chevron { + color: #fff !important; +} + +.cockpit-signal-list span:not([class]) { + color: rgba(224, 236, 242, 0.62); + font-size: 8px; + letter-spacing: 0.08em; +} + +/* Keep a long callsign clear of the time at the top right. */ +.cockpit-signal-list .cockpit-signal-target-label { + margin-right: 50px; +} + +/* ── Briefing content fits the narrower window ────────────────────────── */ +.cockpit-signal-window { + grid-template-columns: minmax(0, 1fr); +} + +.cockpit-signal-window > *, +.cockpit-brief-stage, +.cockpit-brief-stage > section, +.cockpit-signal-list, +.cockpit-signal-list li { + min-width: 0; + max-width: 100%; + box-sizing: border-box; +} + +/* Route: a quiet heading, endpoints that truncate. */ +.cockpit-route-card { + padding-inline: 4px; +} + +.cockpit-route-heading { + display: flex; + justify-content: space-between; + gap: 8px; + min-width: 0; +} + +#cockpit-route-status { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.cockpit-route-airport strong { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Header: the title on one line, the controls on the row under it. */ +.cockpit-signal-window:not([data-collapsed='true']) .cockpit-signal-header { + grid-template-columns: minmax(0, 1fr); + gap: 6px; + padding-inline: 4px; +} + +.cockpit-signal-window:not([data-collapsed='true']) .cockpit-brief-heading { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: 2px 8px; +} + +.cockpit-signal-window:not([data-collapsed='true']) .cockpit-signal-kicker { + white-space: nowrap; +} + +.cockpit-signal-window:not([data-collapsed='true']) .cockpit-brief-actions { + justify-content: flex-start; +} + +.cockpit-signal-window:not([data-collapsed='true']) #cockpit-signal-toggle { + margin-inline-start: auto; +} + +/* The Display / Radio strips carry the left rail's exact frame: border, + glass, corners and drop shadow. */ +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child, +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { + border-color: rgba(255, 255, 255, 0.14); + background: rgba(16, 20, 28, 0.82); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +body.cockpit-mode .cockpit-utility-control:first-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { + border-radius: 14px 14px 0 0; + box-shadow: + 0 6px 20px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.05); +} + +body.cockpit-mode .cockpit-utility-control:last-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { + border-radius: 0 0 14px 14px; +} + +body.cockpit-mode .cockpit-utility-control.is-expanded + .cockpit-utility-control:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), +body.cockpit-mode .cockpit-utility-control:not(.is-expanded):has(+ .cockpit-utility-control.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { + border-radius: 14px; +} + +/* ── Open Display / Radio panels: the main panels' header and size ───── */ +body.cockpit-mode .cockpit-utility-control.is-expanded { + min-width: 295px; + box-sizing: border-box; +} + +body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-launcher { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 4px 10px 8px; + color: rgba(224, 236, 242, 0.78); + font: 9px/1 var(--font-mono); + letter-spacing: 0.22em; +} + +body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-divider { + flex: 0 1 22px; + height: 1px; + margin-right: auto; + background: rgba(255, 255, 255, 0.18); + box-shadow: none; +} + +/* The launcher's own toggle steps aside; "−" collapses instead. */ +body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-glyph { + display: none; +} + +body.cockpit-mode .cockpit-utility-collapse { + display: grid; + flex: none; + place-items: center; + width: 22px; + height: 22px; + padding: 0; + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 6px; + background: rgba(255, 255, 255, 0.03); + color: rgba(206, 232, 239, 0.75); + font: 13px/1 var(--font-mono); + cursor: pointer; +} + +body.cockpit-mode .cockpit-utility-collapse:hover, +body.cockpit-mode .cockpit-utility-collapse:focus-visible { + border-color: rgba(0, 212, 255, 0.45); + color: #fff; + outline: none; +} + +/* Align-to-edge keeps the rail's inset (mirroring the left rail); docked, the + open panel takes the rail's place and the icons go above it (below). */ + +/* Same measurements as the left rail: a 170px label strip, 4px gap, 42px + icon strip, 38px rows. */ +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher { + gap: 4px; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child { + width: 170px; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child, +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { + height: 38px; +} + +/* Hover as on the left rail: a 32px rounded square lights behind the icon + and the icon and its label brighten; the strip itself stays put. */ +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph::before { + display: grid; + place-items: center; + width: 32px; + height: 32px; + border: 1px solid transparent; + border-radius: var(--btn-radius, 8px); + box-sizing: border-box; + transition: + color var(--transition-fast, 120ms), + background var(--transition-fast, 120ms); +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph:hover { + background: rgba(16, 20, 28, 0.82); + color: var(--text-primary, #fff); +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph:hover::before { + background: rgba(255, 255, 255, 0.05); +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher:has(.cockpit-utility-glyph:hover) > span:first-child { + color: var(--text-primary, #fff); +} + +/* ── Right rail as on the left: an icon column that stays, labels beside it + while nothing is open, and the open panel to its left. The launchers' + own strips give way to it. ─────────────────────────────────────────── */ +body.cockpit-mode .cockpit-utility-controls { + display: grid; + grid-template-columns: auto auto; + align-items: start; + justify-content: end; + column-gap: 6px; +} + +body.cockpit-mode .cockpit-utility-control:not(.is-expanded) { + display: none; +} + +body.cockpit-mode .cockpit-utility-control.is-expanded { + grid-column: 1; + grid-row: 1; + margin-top: 0 !important; +} + +.cockpit-utility-rail { + display: none; +} + +body.cockpit-mode .cockpit-utility-rail { + grid-column: 2; + grid-row: 1; + display: flex; + gap: 4px; + align-items: stretch; + pointer-events: auto; +} + +body.cockpit-mode :is(.cockpit-utility-rail__labels, .cockpit-utility-rail__icons) { + display: flex; + flex-direction: column; + gap: 2px; + padding: 4px; + border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: 14px; + background: rgba(16, 20, 28, 0.82); + box-shadow: + 0 6px 20px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.05); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +body.cockpit-mode .cockpit-utility-rail__labels { + box-sizing: border-box; + width: 170px; + padding-inline: 14px; +} + +body.cockpit-mode .cockpit-utility-rail__labels > span { + display: flex; + align-items: center; + height: 32px; + color: rgba(225, 236, 242, 0.82); + font: 500 9.28px/1 Inter, -apple-system, system-ui, sans-serif; + letter-spacing: 2.08px; +} + +body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__labels { + display: none; +} + +body.cockpit-mode .cockpit-utility-rail__item { + display: grid; + place-items: center; + width: 32px; + height: 32px; + padding: 0; + border: 1px solid transparent; + border-radius: 10px; + background: transparent; + color: var(--text-secondary, rgba(206, 232, 239, 0.75)); + cursor: pointer; + transition: + color var(--transition-fast, 120ms), + background var(--transition-fast, 120ms), + border-color var(--transition-fast, 120ms); +} + +body.cockpit-mode .cockpit-utility-rail__item .material-symbols-outlined { + font-size: 18px; +} + +body.cockpit-mode .cockpit-utility-rail__item:hover { + background: rgba(255, 255, 255, 0.05); + color: var(--text-primary, #fff); +} + +body.cockpit-mode .cockpit-utility-rail__item.is-on, +body.cockpit-mode .cockpit-utility-rail__item.is-on:hover { + border-color: rgba(0, 212, 255, 0.15); + background: rgba(0, 212, 255, 0.15); + color: #00d4ff; +} + +/* Hovering an icon lights its label too, as on the left. */ +body.cockpit-mode .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(1):hover) .cockpit-utility-rail__labels > span:nth-child(1), +body.cockpit-mode .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(2):hover) .cockpit-utility-rail__labels > span:nth-child(2) { + color: var(--text-primary, #fff); +} + +/* Docked (align-to-edge) with a panel open: as on the main page, the panel + takes the edge and the rail's icons sit in a row above it. */ +body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) { + grid-template-columns: auto; + justify-items: end; + row-gap: 8px; +} + +body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) .cockpit-utility-rail { + grid-column: 1; + grid-row: 1; +} + +body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) .cockpit-utility-control.is-expanded { + grid-column: 1; + grid-row: 2; +} + +body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) .cockpit-utility-rail__icons { + flex-direction: row; +} + +/* Mirror the left rail's 52px inset, clear of the HUD's vertical readouts. */ +body.cockpit-mode .cockpit-utility-controls { + right: 52px; +} diff --git a/src/ui/views/panels/CockpitBriefing.vue b/src/ui/views/panels/CockpitBriefing.vue index cbb37b935..f0a86b1cc 100644 --- a/src/ui/views/panels/CockpitBriefing.vue +++ b/src/ui/views/panels/CockpitBriefing.vue @@ -84,8 +84,7 @@ function leaveSignals() { {{ b.source }} diff --git a/src/ui/views/panels/CockpitUtilityControls.vue b/src/ui/views/panels/CockpitUtilityControls.vue index 1b20a531e..e2e85cf3c 100644 --- a/src/ui/views/panels/CockpitUtilityControls.vue +++ b/src/ui/views/panels/CockpitUtilityControls.vue @@ -8,6 +8,20 @@ import { useRadioStore } from '../../stores/radio.js'; import { useCockpitStore } from '../../stores/cockpit.js'; import { useDisplayStore } from '../../stores/display.js'; import UiButton from '../../kit/UiButton.vue'; +import UiPanelEdge from '../../kit/UiPanelEdge.vue'; + +// The open panel's header carries the same edge switch and collapse button as +// the main panels; collapsing goes through each launcher's own toggle. +function collapse(which) { + if (which === 'radio') act('toggleCockpitRadio'); + else document.getElementById('cockpit-display-toggle-btn')?.click(); +} +// The rail opens one panel at a time, as the main page's rails do. +function railToggle(which) { + const other = which === 'radio' ? 'display' : 'radio'; + if (open.value[other]) collapse(other); + collapse(which); +} // The Cockpit's Display and Radio launchers and their popovers. RadioControls // presents which popover is open and the compact Radio controls; until it @@ -52,6 +66,8 @@ const radioLabel = computed(() =>
{{ t('DISPLAY') }} + +
@@ -65,6 +81,8 @@ const radioLabel = computed(() =>
{{ t('RADIO') }} + +
@@ -82,5 +100,15 @@ const radioLabel = computed(() =>
+ From 839409a3fc65b120f48f97a0ceb8bc98ba70b54e Mon Sep 17 00:00:00 2001 From: Manjunath Paparaju Date: Fri, 13 Nov 2026 11:14:14 +0530 Subject: [PATCH 02/13] Fit the side rails and panels to cockpit view - The left rail shows Data Layers and Visual in cockpit view; Authoring and Utilities are hidden along with their slots and panels, so no empty pieces are left behind. - With a panel open there, each icon left is its own island; a hidden panel still open underneath leaves the rail in one piece. - The cockpit's Display and Radio rail uses the main rails' 12 px spacing and draws each icon as its own island while a panel is open. - Contact and Live Signals stand shorter (min(27vh, 260px)); Contact's buttons share the kicker's row and its subject runs full width on at most two lines. - Edge scrolling skips rail items that are not shown. Co-authored-by: Sameh Khamis --- src/ui/styles/cockpit-panels.css | 111 ++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 8 deletions(-) diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index 8dd9e2c9c..970b12952 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -24,7 +24,33 @@ .cockpit-context-window:not([data-collapsed='true']), .cockpit-signal-window:not([data-collapsed='true']) { width: min(280px, 23vw); - height: min(34vh, 330px); + height: min(27vh, 260px); +} + +/* Contact's header: the buttons share the kicker's row, so the subject runs + the full width on at most two lines instead of wrapping beside them. */ +.cockpit-context-window:not([data-collapsed='true']) .cockpit-context-header > div { + display: contents; +} + +.cockpit-context-window:not([data-collapsed='true']) .cockpit-context-kicker { + grid-column: 1; + grid-row: 1; +} + +.cockpit-context-window:not([data-collapsed='true']) .cockpit-context-actions { + grid-column: 2; + grid-row: 1; +} + +.cockpit-context-window:not([data-collapsed='true']) #cockpit-context-subject { + display: -webkit-box; + grid-column: 1 / -1; + grid-row: 2; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; } /* Their foot sits just above the bottom HUD readouts (MGRS / GSD), which @@ -92,19 +118,57 @@ body.cockpit-mode .cockpit-utility-control.is-expanded { } /* ── Cockpit uses the main page's rails ─────────────────────────────── - The left rail shows (Data Layers, Utilities; Authoring has no place in - first person, and the cockpit has its own Display control on the right in - place of Visual), one panel opens beside it, and the collapsed header - strips the cockpit used instead are hidden. */ + The left rail shows Data Layers and Visual (Authoring has no place in + first person, and Utilities has nothing to do there), one panel opens + beside it, and the collapsed header strips the cockpit used instead are + hidden. */ body.cockpit-mode #left-panel-stack > .gev-panel-rail--left { display: block; } -body.cockpit-mode #left-panel-stack > .gev-panel-rail--left :is([data-rail-item='scene-panel'], [data-rail-item='pp-toggles']) { +body.cockpit-mode #left-panel-stack > .gev-panel-rail--left :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel']), +body.cockpit-mode + #left-panel-stack + > .gev-panel-rail--left + .ui-tooltip:has(> :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'])) { display: none; } -body.cockpit-mode #left-panel-stack > [data-panel-id].collapsed { +/* With some icons hidden, the runs between them break up: with a panel + open, each icon left is its own island. */ +body.cockpit-mode + #left-panel-stack + > .gev-panel-rail--left + .ui-rail--collapsible:not(.ui-rail--expanded):has(.ui-rail__item--on) + > .ui-tooltip::before { + inset: -4px -5px; + border-width: 1px; + border-radius: 14px; +} + +/* Authoring or Utilities may still be open underneath; its hidden icon must + not split the rail into islands. */ +body.cockpit-mode + #left-panel-stack + > .gev-panel-rail--left + .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'])[aria-pressed='true']) + > .ui-tooltip::before { + display: none; +} + +body.cockpit-mode + #left-panel-stack + > .gev-panel-rail--left + .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'])[aria-pressed='true']) { + background: rgba(16, 20, 28, 0.82) !important; + border-color: rgba(255, 255, 255, 0.14) !important; + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45) !important; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); +} + +body.cockpit-mode #left-panel-stack > [data-panel-id].collapsed, +body.cockpit-mode #left-panel-stack > :is([data-panel-id='scene-panel'], [data-panel-id='utilities-panel']) { display: none; } @@ -570,7 +634,7 @@ body.cockpit-mode .cockpit-utility-rail { body.cockpit-mode :is(.cockpit-utility-rail__labels, .cockpit-utility-rail__icons) { display: flex; flex-direction: column; - gap: 2px; + gap: 12px; padding: 4px; border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 14px; @@ -634,6 +698,37 @@ body.cockpit-mode .cockpit-utility-rail__item.is-on:hover { color: #00d4ff; } +/* With a panel open, each icon is its own island, as on the main rails. */ +body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__icons { + border-color: transparent; + background: none; + box-shadow: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; +} + +body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__item { + position: relative; +} + +body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__item::before { + position: absolute; + inset: -5px; + z-index: -1; + border: 1px solid rgba(255, 255, 255, 0.14); + border-radius: 14px; + background: rgba(16, 20, 28, 0.82); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + content: ''; + pointer-events: none; +} + +body.cockpit-mode.panels-edge-right .cockpit-utility-rail.is-open .cockpit-utility-rail__icons { + gap: 12px; +} + /* Hovering an icon lights its label too, as on the left. */ body.cockpit-mode .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(1):hover) .cockpit-utility-rail__labels > span:nth-child(1), body.cockpit-mode .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(2):hover) .cockpit-utility-rail__labels > span:nth-child(2) { From fd1f5161925881ab38555047fc2a05789886a038 Mon Sep 17 00:00:00 2001 From: Manjunath Paparaju Date: Fri, 13 Nov 2026 11:16:07 +0530 Subject: [PATCH 03/13] Tighten the cockpit briefing's rows and weather tiles Live Signals rows drop from 43.5 px to 36 px with less padding. The local weather tiles lose the lit grid that showed between them, stand 5 px apart as cards and shrink from 60 px to 53 px, so all four fit the shorter window. Co-authored-by: Sameh Khamis --- src/ui/styles/cockpit-panels.css | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index 970b12952..30a9e2b39 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -103,6 +103,19 @@ background: rgba(255, 255, 255, 0.035); } +/* The weather tiles stand apart as cards, with no lit grid behind them, + and are short enough to fit the shorter window. */ +.cockpit-local-grid { + gap: 5px; + background: none; +} + +.cockpit-local-grid > div { + min-height: 0; + gap: 2px; + padding: 6px 9px; +} + .cockpit-route-card { border-bottom-color: rgba(255, 255, 255, 0.08); } @@ -331,13 +344,13 @@ body.cockpit-mode #intel-hud .hud-bottom-right { The status colour is a slim inset bar (cyan for the aircraft you ride, white for nearby, amber for military / warnings), not a bent border. */ .cockpit-signal-list { - gap: 5px; + gap: 4px; } .cockpit-signal-list li { position: relative; display: block; - padding: 8px 12px 8px 14px; + padding: 5px 10px 5px 12px; border: 1px solid rgba(255, 255, 255, 0.06); box-shadow: inset 2px 0 0 rgba(244, 251, 255, 0.22); } @@ -365,8 +378,8 @@ body.cockpit-mode #intel-hud .hud-bottom-right { .cockpit-signal-list time { position: absolute; - top: 9px; - right: 30px; + top: 6px; + right: 28px; color: rgba(224, 236, 242, 0.4); font: 7px/1 var(--font-mono); letter-spacing: 0.06em; @@ -374,7 +387,7 @@ body.cockpit-mode #intel-hud .hud-bottom-right { } .cockpit-signal-list li > div { - gap: 3px; + gap: 1px; } .cockpit-signal-target { From 5b3be915c8bec0975caccb2d51de573f85047098 Mon Sep 17 00:00:00 2001 From: Manjunath Paparaju Date: Fri, 13 Nov 2026 15:42:31 +0530 Subject: [PATCH 04/13] Keep only what cockpit Display lacks in the Visual panel In cockpit, the Visual panel hides the HUD, detection, parameters and 3D-model groups, which the cockpit's Display panel already offers, and the celestial ring toggle. It keeps Look, Map source, Scope, Bloom and Sharpen. Co-authored-by: Sameh Khamis --- src/ui/styles/cockpit-panels.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index 30a9e2b39..ff457edb5 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -159,6 +159,22 @@ body.cockpit-mode border-radius: 14px; } +/* In cockpit, Visual keeps only what the cockpit's own Display panel does + not already offer: the HUD, detection, parameters and 3D-model groups live + there (its portal moves them while it shows; hidden here otherwise, so they + never appear twice), and the celestial ring has no place in first person. */ +body.cockpit-mode + #pp-toggles + :is( + .pp-toggle-group:has(#hud-toggle), + .pp-toggle-group:has(#detection-toggle), + .pp-toggle-group:has(#models3d-toggle), + .pp-toggle-group:has(#celestial-toggle), + #param-slider-panel + ) { + display: none !important; +} + /* Authoring or Utilities may still be open underneath; its hidden icon must not split the rail into islands. */ body.cockpit-mode From 50086f56b62c799be193d85958873b1696356c13 Mon Sep 17 00:00:00 2001 From: Manjunath Paparaju Date: Fri, 13 Nov 2026 15:51:23 +0530 Subject: [PATCH 05/13] Hide Clean UI from the Visual panel in cockpit Cockpit's Visual panel now also leaves out the Clean UI toggle, as it does the celestial ring. Co-authored-by: Sameh Khamis --- src/ui/styles/cockpit-panels.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index ff457edb5..85ef6fa8f 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -162,7 +162,8 @@ body.cockpit-mode /* In cockpit, Visual keeps only what the cockpit's own Display panel does not already offer: the HUD, detection, parameters and 3D-model groups live there (its portal moves them while it shows; hidden here otherwise, so they - never appear twice), and the celestial ring has no place in first person. */ + never appear twice), and the celestial ring and Clean UI have no place in + first person. */ body.cockpit-mode #pp-toggles :is( @@ -170,6 +171,7 @@ body.cockpit-mode .pp-toggle-group:has(#detection-toggle), .pp-toggle-group:has(#models3d-toggle), .pp-toggle-group:has(#celestial-toggle), + #clean-view-toggle, #param-slider-panel ) { display: none !important; From 52b256ce946c6def92cb36fd36c599fbacaef3f8 Mon Sep 17 00:00:00 2001 From: Manjunath Paparaju Date: Fri, 13 Nov 2026 15:55:36 +0530 Subject: [PATCH 06/13] Hide Visual from the rail in cockpit In cockpit the left rail now shows Data Layers only: Visual's rail item and panel are hidden there, as Authoring and Utilities are, and the cockpit's own Display panel stands in for it. The rules that trimmed Visual's groups in cockpit are removed as no longer reachable. If Visual was open on entering cockpit, the rail's sliding highlight no longer stays where its hidden icon was. Co-authored-by: Sameh Khamis --- src/ui/styles/cockpit-panels.css | 45 ++++++++++++-------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index 85ef6fa8f..04e13144f 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -131,19 +131,19 @@ body.cockpit-mode .cockpit-utility-control.is-expanded { } /* ── Cockpit uses the main page's rails ─────────────────────────────── - The left rail shows Data Layers and Visual (Authoring has no place in - first person, and Utilities has nothing to do there), one panel opens - beside it, and the collapsed header strips the cockpit used instead are - hidden. */ + The left rail shows Data Layers only (Authoring has no place in first + person, Utilities has nothing to do there, and the cockpit's own Display + panel stands in for Visual), one panel opens beside it, and the collapsed + header strips the cockpit used instead are hidden. */ body.cockpit-mode #left-panel-stack > .gev-panel-rail--left { display: block; } -body.cockpit-mode #left-panel-stack > .gev-panel-rail--left :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel']), +body.cockpit-mode #left-panel-stack > .gev-panel-rail--left :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles']), body.cockpit-mode #left-panel-stack > .gev-panel-rail--left - .ui-tooltip:has(> :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'])) { + .ui-tooltip:has(> :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])) { display: none; } @@ -159,38 +159,25 @@ body.cockpit-mode border-radius: 14px; } -/* In cockpit, Visual keeps only what the cockpit's own Display panel does - not already offer: the HUD, detection, parameters and 3D-model groups live - there (its portal moves them while it shows; hidden here otherwise, so they - never appear twice), and the celestial ring and Clean UI have no place in - first person. */ -body.cockpit-mode - #pp-toggles - :is( - .pp-toggle-group:has(#hud-toggle), - .pp-toggle-group:has(#detection-toggle), - .pp-toggle-group:has(#models3d-toggle), - .pp-toggle-group:has(#celestial-toggle), - #clean-view-toggle, - #param-slider-panel - ) { - display: none !important; -} - -/* Authoring or Utilities may still be open underneath; its hidden icon must +/* Authoring, Utilities or Visual may still be open underneath; its hidden icon must not split the rail into islands. */ body.cockpit-mode #left-panel-stack > .gev-panel-rail--left - .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'])[aria-pressed='true']) - > .ui-tooltip::before { + .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])[aria-pressed='true']) + > .ui-tooltip::before, +body.cockpit-mode + #left-panel-stack + > .gev-panel-rail--left + .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])[aria-pressed='true'])::before { + /* Nor may the sliding highlight stay where the hidden icon was. */ display: none; } body.cockpit-mode #left-panel-stack > .gev-panel-rail--left - .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'])[aria-pressed='true']) { + .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])[aria-pressed='true']) { background: rgba(16, 20, 28, 0.82) !important; border-color: rgba(255, 255, 255, 0.14) !important; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45) !important; @@ -199,7 +186,7 @@ body.cockpit-mode } body.cockpit-mode #left-panel-stack > [data-panel-id].collapsed, -body.cockpit-mode #left-panel-stack > :is([data-panel-id='scene-panel'], [data-panel-id='utilities-panel']) { +body.cockpit-mode #left-panel-stack > :is([data-panel-id='scene-panel'], [data-panel-id='utilities-panel'], [data-panel-id='pp-toggles']) { display: none; } From af7f659f0d277a256920df562de61a749704218d Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 14 Nov 2026 15:41:14 -0800 Subject: [PATCH 07/13] Keep the cockpit briefing's Regional News page The restyle had dropped the page and its tab while the news section, its fetch and the briefing tests still use it. Co-authored-by: Manjunath Paparaju --- src/cockpitMarkup.test.mjs | 2 +- src/ui/cockpitBriefPages.js | 8 +++++++- src/ui/views/panels/CockpitBriefing.vue | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/cockpitMarkup.test.mjs b/src/cockpitMarkup.test.mjs index d4167f043..3f461c392 100644 --- a/src/cockpitMarkup.test.mjs +++ b/src/cockpitMarkup.test.mjs @@ -1275,7 +1275,7 @@ test('cockpit briefing cycle control keeps its state as the accessible name', () assert.match(match[0], />CYCLE OFF<\/button>/); assert.match( match[0], - /title="Cycle briefing pages automatically every 9 seconds \(Signals → Local\)\./, + /title="Cycle briefing pages automatically every 9 seconds \(Signals → News → Local\)\./, ); // The presented label is the state; the help is only the tooltip. diff --git a/src/ui/cockpitBriefPages.js b/src/ui/cockpitBriefPages.js index 943255eae..9f75c83d8 100644 --- a/src/ui/cockpitBriefPages.js +++ b/src/ui/cockpitBriefPages.js @@ -31,6 +31,12 @@ export const COCKPIT_BRIEF_PAGES = [ subtitleKey: msg('OBSERVED / MAPPED PINGS'), sourceKey: msg('SOURCE-BACKED EVENTS · NO SYNTHETIC NEWS'), }, + { + id: 'news', + kickerKey: msg('REGIONAL NEWS'), + subtitleKey: msg('LATEST LOCATION-MATCHED REPORTING'), + // The news footer comes from the news state (`news.footer`), not here. + }, { id: 'local', kickerKey: msg('LOCAL INFO'), @@ -46,7 +52,7 @@ export const CYCLE_HELP_KEYS = Object.freeze({ 'Stop automatic page cycling. Previous, Next, and the SIG/NEWS/LOCAL tabs stay available.', ), off: msg( - 'Cycle briefing pages automatically every 9 seconds (Signals → Local). Pauses while you hover or focus the panel. Live signal data refreshes continuously either way.', + 'Cycle briefing pages automatically every 9 seconds (Signals → News → Local). Pauses while you hover or focus the panel. Live signal data refreshes continuously either way.', ), }); diff --git a/src/ui/views/panels/CockpitBriefing.vue b/src/ui/views/panels/CockpitBriefing.vue index f0a86b1cc..cbb37b935 100644 --- a/src/ui/views/panels/CockpitBriefing.vue +++ b/src/ui/views/panels/CockpitBriefing.vue @@ -84,7 +84,8 @@ function leaveSignals() { {{ b.source }} From d157198ecba23ee0b82557e89c32bfd8ffe017a4 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 14 Nov 2026 15:43:14 -0800 Subject: [PATCH 08/13] Load the cockpit panel styles with the shell, formatted Co-authored-by: Manjunath Paparaju --- scripts/format-scope.json | 3 +- src/ui/styles/cockpit-panels.css | 227 ++++++++++++++++++++++++------- style.css | 1 + 3 files changed, 183 insertions(+), 48 deletions(-) diff --git a/scripts/format-scope.json b/scripts/format-scope.json index 49f71deb8..8026811ea 100644 --- a/scripts/format-scope.json +++ b/scripts/format-scope.json @@ -1461,5 +1461,6 @@ "src/data/placementProvider.test.mjs", "src/layers/vessels/placement.test.mjs", "src/ui/styles/visual-panel.css", - "src/ui/styles/scope-off-shadows.css" + "src/ui/styles/scope-off-shadows.css", + "src/ui/styles/cockpit-panels.css" ] diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index 04e13144f..dbf89e96e 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -29,7 +29,9 @@ /* Contact's header: the buttons share the kicker's row, so the subject runs the full width on at most two lines instead of wrapping beside them. */ -.cockpit-context-window:not([data-collapsed='true']) .cockpit-context-header > div { +.cockpit-context-window:not([data-collapsed='true']) + .cockpit-context-header + > div { display: contents; } @@ -139,11 +141,24 @@ body.cockpit-mode #left-panel-stack > .gev-panel-rail--left { display: block; } -body.cockpit-mode #left-panel-stack > .gev-panel-rail--left :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles']), body.cockpit-mode #left-panel-stack > .gev-panel-rail--left - .ui-tooltip:has(> :is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])) { + :is( + [data-rail-item='scene-panel'], + [data-rail-item='utilities-panel'], + [data-rail-item='pp-toggles'] + ), +body.cockpit-mode + #left-panel-stack + > .gev-panel-rail--left + .ui-tooltip:has( + > :is( + [data-rail-item='scene-panel'], + [data-rail-item='utilities-panel'], + [data-rail-item='pp-toggles'] + ) + ) { display: none; } @@ -164,12 +179,24 @@ body.cockpit-mode body.cockpit-mode #left-panel-stack > .gev-panel-rail--left - .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])[aria-pressed='true']) + .ui-rail:has( + :is( + [data-rail-item='scene-panel'], + [data-rail-item='utilities-panel'], + [data-rail-item='pp-toggles'] + )[aria-pressed='true'] + ) > .ui-tooltip::before, body.cockpit-mode #left-panel-stack > .gev-panel-rail--left - .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])[aria-pressed='true'])::before { + .ui-rail:has( + :is( + [data-rail-item='scene-panel'], + [data-rail-item='utilities-panel'], + [data-rail-item='pp-toggles'] + )[aria-pressed='true'] + )::before { /* Nor may the sliding highlight stay where the hidden icon was. */ display: none; } @@ -177,7 +204,13 @@ body.cockpit-mode body.cockpit-mode #left-panel-stack > .gev-panel-rail--left - .ui-rail:has(:is([data-rail-item='scene-panel'], [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'])[aria-pressed='true']) { + .ui-rail:has( + :is( + [data-rail-item='scene-panel'], + [data-rail-item='utilities-panel'], + [data-rail-item='pp-toggles'] + )[aria-pressed='true'] + ) { background: rgba(16, 20, 28, 0.82) !important; border-color: rgba(255, 255, 255, 0.14) !important; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45) !important; @@ -186,7 +219,13 @@ body.cockpit-mode } body.cockpit-mode #left-panel-stack > [data-panel-id].collapsed, -body.cockpit-mode #left-panel-stack > :is([data-panel-id='scene-panel'], [data-panel-id='utilities-panel'], [data-panel-id='pp-toggles']) { +body.cockpit-mode + #left-panel-stack + > :is( + [data-panel-id='scene-panel'], + [data-panel-id='utilities-panel'], + [data-panel-id='pp-toggles'] + ) { display: none; } @@ -196,7 +235,9 @@ body.cockpit-mode .cockpit-utility-controls { gap: 0; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher { display: flex; justify-content: flex-end; align-items: stretch; @@ -212,8 +253,13 @@ body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-la -webkit-backdrop-filter: none; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child, -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher + > span:first-child, +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph { display: flex; align-items: center; box-sizing: border-box; @@ -225,7 +271,10 @@ body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-gl -webkit-backdrop-filter: blur(24px) saturate(1.4); } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher + > span:first-child { width: 120px; padding: 0 14px; color: rgba(224, 236, 242, 0.72); @@ -233,11 +282,15 @@ body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-la letter-spacing: 0.16em; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-divider { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-divider { display: none; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph { justify-content: center; width: 42px; padding: 0; @@ -269,33 +322,48 @@ body.cockpit-mode #cockpit-radio-toggle-btn::before { content: 'radio'; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph:hover { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph:hover { color: #fff; background: rgba(0, 212, 255, 0.12); } /* Round the ends of each strip and draw its top / bottom edge. */ -body.cockpit-mode .cockpit-utility-control:first-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { +body.cockpit-mode + .cockpit-utility-control:first-child:not(.is-expanded) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { padding-top: 4px; border-top-width: 1px; border-radius: 12px 12px 0 0; } -body.cockpit-mode .cockpit-utility-control:last-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { +body.cockpit-mode + .cockpit-utility-control:last-child:not(.is-expanded) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { padding-bottom: 4px; border-bottom-width: 1px; border-radius: 0 0 12px 12px; } /* A strip left alone beside an open panel is rounded all round. */ -body.cockpit-mode .cockpit-utility-control.is-expanded + .cockpit-utility-control:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), -body.cockpit-mode .cockpit-utility-control:not(.is-expanded):has(+ .cockpit-utility-control.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { +body.cockpit-mode + .cockpit-utility-control.is-expanded + + .cockpit-utility-control:not(.is-expanded) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded):has( + + .cockpit-utility-control.is-expanded + ) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { padding-block: 4px; border-block-width: 1px; border-radius: 12px; } -body.cockpit-mode .cockpit-utility-control.is-expanded + .cockpit-utility-control:not(.is-expanded) { +body.cockpit-mode + .cockpit-utility-control.is-expanded + + .cockpit-utility-control:not(.is-expanded) { margin-top: 7px; } @@ -313,7 +381,9 @@ body.cockpit-mode #cesium-credits { white-space: nowrap; } -body.cockpit-mode #cesium-credits :is(.cesium-widget-credits, .cesium-credit-textContainer) { +body.cockpit-mode + #cesium-credits + :is(.cesium-widget-credits, .cesium-credit-textContainer) { display: inline-flex !important; flex-wrap: nowrap !important; align-items: center; @@ -411,7 +481,9 @@ body.cockpit-mode #intel-hud .hud-bottom-right { color: rgba(224, 236, 242, 0.5) !important; } -.cockpit-signal-list li.actionable:is(:hover, :focus-within) .cockpit-signal-target-chevron { +.cockpit-signal-list + li.actionable:is(:hover, :focus-within) + .cockpit-signal-target-chevron { color: #fff !important; } @@ -493,8 +565,13 @@ body.cockpit-mode #intel-hud .hud-bottom-right { /* The Display / Radio strips carry the left rail's exact frame: border, glass, corners and drop shadow. */ -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child, -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher + > span:first-child, +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph { border-color: rgba(255, 255, 255, 0.14); background: rgba(16, 20, 28, 0.82); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45); @@ -502,19 +579,30 @@ body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-gl -webkit-backdrop-filter: blur(20px); } -body.cockpit-mode .cockpit-utility-control:first-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { +body.cockpit-mode + .cockpit-utility-control:first-child:not(.is-expanded) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { border-radius: 14px 14px 0 0; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.05); } -body.cockpit-mode .cockpit-utility-control:last-child:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { +body.cockpit-mode + .cockpit-utility-control:last-child:not(.is-expanded) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { border-radius: 0 0 14px 14px; } -body.cockpit-mode .cockpit-utility-control.is-expanded + .cockpit-utility-control:not(.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), -body.cockpit-mode .cockpit-utility-control:not(.is-expanded):has(+ .cockpit-utility-control.is-expanded) :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { +body.cockpit-mode + .cockpit-utility-control.is-expanded + + .cockpit-utility-control:not(.is-expanded) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded):has( + + .cockpit-utility-control.is-expanded + ) + :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { border-radius: 14px; } @@ -524,7 +612,9 @@ body.cockpit-mode .cockpit-utility-control.is-expanded { box-sizing: border-box; } -body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-launcher { +body.cockpit-mode + .cockpit-utility-control.is-expanded + .cockpit-utility-launcher { display: flex; align-items: center; gap: 8px; @@ -534,7 +624,9 @@ body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-launcher letter-spacing: 0.22em; } -body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-divider { +body.cockpit-mode + .cockpit-utility-control.is-expanded + .cockpit-utility-divider { flex: 0 1 22px; height: 1px; margin-right: auto; @@ -574,22 +666,34 @@ body.cockpit-mode .cockpit-utility-collapse:focus-visible { /* Same measurements as the left rail: a 170px label strip, 4px gap, 42px icon strip, 38px rows. */ -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher { gap: 4px; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher + > span:first-child { width: 170px; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher > span:first-child, -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher + > span:first-child, +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph { height: 38px; } /* Hover as on the left rail: a 32px rounded square lights behind the icon and the icon and its label brighten; the strip itself stays put. */ -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph::before { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph::before { display: grid; place-items: center; width: 32px; @@ -602,16 +706,23 @@ body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-gl background var(--transition-fast, 120ms); } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph:hover { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph:hover { background: rgba(16, 20, 28, 0.82); color: var(--text-primary, #fff); } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-glyph:hover::before { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-glyph:hover::before { background: rgba(255, 255, 255, 0.05); } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) .cockpit-utility-launcher:has(.cockpit-utility-glyph:hover) > span:first-child { +body.cockpit-mode + .cockpit-utility-control:not(.is-expanded) + .cockpit-utility-launcher:has(.cockpit-utility-glyph:hover) + > span:first-child { color: var(--text-primary, #fff); } @@ -649,7 +760,8 @@ body.cockpit-mode .cockpit-utility-rail { pointer-events: auto; } -body.cockpit-mode :is(.cockpit-utility-rail__labels, .cockpit-utility-rail__icons) { +body.cockpit-mode + :is(.cockpit-utility-rail__labels, .cockpit-utility-rail__icons) { display: flex; flex-direction: column; gap: 12px; @@ -675,7 +787,11 @@ body.cockpit-mode .cockpit-utility-rail__labels > span { align-items: center; height: 32px; color: rgba(225, 236, 242, 0.82); - font: 500 9.28px/1 Inter, -apple-system, system-ui, sans-serif; + font: + 500 9.28px/1 Inter, + -apple-system, + system-ui, + sans-serif; letter-spacing: 2.08px; } @@ -729,7 +845,9 @@ body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__item { position: relative; } -body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__item::before { +body.cockpit-mode + .cockpit-utility-rail.is-open + .cockpit-utility-rail__item::before { position: absolute; inset: -5px; z-index: -1; @@ -743,35 +861,50 @@ body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__item::bef pointer-events: none; } -body.cockpit-mode.panels-edge-right .cockpit-utility-rail.is-open .cockpit-utility-rail__icons { +body.cockpit-mode.panels-edge-right + .cockpit-utility-rail.is-open + .cockpit-utility-rail__icons { gap: 12px; } /* Hovering an icon lights its label too, as on the left. */ -body.cockpit-mode .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(1):hover) .cockpit-utility-rail__labels > span:nth-child(1), -body.cockpit-mode .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(2):hover) .cockpit-utility-rail__labels > span:nth-child(2) { +body.cockpit-mode + .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(1):hover) + .cockpit-utility-rail__labels + > span:nth-child(1), +body.cockpit-mode + .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(2):hover) + .cockpit-utility-rail__labels + > span:nth-child(2) { color: var(--text-primary, #fff); } /* Docked (align-to-edge) with a panel open: as on the main page, the panel takes the edge and the rail's icons sit in a row above it. */ -body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) { +body.cockpit-mode.panels-edge-right + .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) { grid-template-columns: auto; justify-items: end; row-gap: 8px; } -body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) .cockpit-utility-rail { +body.cockpit-mode.panels-edge-right + .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) + .cockpit-utility-rail { grid-column: 1; grid-row: 1; } -body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) .cockpit-utility-control.is-expanded { +body.cockpit-mode.panels-edge-right + .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) + .cockpit-utility-control.is-expanded { grid-column: 1; grid-row: 2; } -body.cockpit-mode.panels-edge-right .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) .cockpit-utility-rail__icons { +body.cockpit-mode.panels-edge-right + .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) + .cockpit-utility-rail__icons { flex-direction: row; } diff --git a/style.css b/style.css index 2d79212fb..6f623d8ea 100644 --- a/style.css +++ b/style.css @@ -50,6 +50,7 @@ @import './src/ui/styles/left-tools.css'; @import './src/ui/styles/panel-edge.css'; @import './src/ui/styles/visual-panel.css'; +@import './src/ui/styles/cockpit-panels.css'; @import './src/ui/styles/ai-chat.css'; @import './src/ui/styles/scope-off-shadows.css'; @import './src/ui/styles/cyber.css'; From 0c295ea770e05a3fcdfdac2b7961f2d132fc5759 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 14 Nov 2026 15:51:13 -0800 Subject: [PATCH 09/13] Give only the desktop cockpit the rail look, placed by the layout pass Cyber and the phone keep their own cockpit layouts. Display and Radio take their top from the layout pass, whose floor now matches the left rail, so the panel's height still stops short of the briefing. The rail keeps its physical side in a right-to-left language, the Live Signals rows use logical sides, and rules for the launchers' old strips, which the rail hides, are gone. Co-authored-by: Manjunath Paparaju --- src/ui/cockpitPresentation.js | 6 +- src/ui/styles/cockpit-panels.css | 457 +++++++++---------------------- 2 files changed, 136 insertions(+), 327 deletions(-) diff --git a/src/ui/cockpitPresentation.js b/src/ui/cockpitPresentation.js index 99727c7be..39811db0d 100644 --- a/src/ui/cockpitPresentation.js +++ b/src/ui/cockpitPresentation.js @@ -27,9 +27,11 @@ export const COCKPIT_UTILITY_REC_GAP_PX = 12; export const COCKPIT_UTILITY_SIGNAL_GAP_PX = 8; -export const COCKPIT_UTILITY_MIN_TOP_PX = 96; +// Display and Radio start where the left rail does (max(206px, 22vh) in +// cockpit-panels.css), so the two rails' tops line up. +export const COCKPIT_UTILITY_MIN_TOP_PX = 206; -export const COCKPIT_UTILITY_MIN_TOP_RATIO = 0.12; +export const COCKPIT_UTILITY_MIN_TOP_RATIO = 0.22; export const COCKPIT_UTILITY_LAUNCHER_MIN_HEIGHT_PX = 50; diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index dbf89e96e..960c4407b 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -123,7 +123,9 @@ } /* Display / Radio: the closed launchers and the open panels match too. */ -body.cockpit-mode .cockpit-utility-control.is-expanded { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-control.is-expanded { border-color: rgba(255, 255, 255, 0.08); border-radius: 16px; background: rgba(12, 12, 20, 0.72); @@ -137,11 +139,15 @@ body.cockpit-mode .cockpit-utility-control.is-expanded { person, Utilities has nothing to do there, and the cockpit's own Display panel stands in for Visual), one panel opens beside it, and the collapsed header strips the cockpit used instead are hidden. */ -body.cockpit-mode #left-panel-stack > .gev-panel-rail--left { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + #left-panel-stack + > .gev-panel-rail--left { display: block; } -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) #left-panel-stack > .gev-panel-rail--left :is( @@ -149,7 +155,8 @@ body.cockpit-mode [data-rail-item='utilities-panel'], [data-rail-item='pp-toggles'] ), -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) #left-panel-stack > .gev-panel-rail--left .ui-tooltip:has( @@ -164,7 +171,8 @@ body.cockpit-mode /* With some icons hidden, the runs between them break up: with a panel open, each icon left is its own island. */ -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) #left-panel-stack > .gev-panel-rail--left .ui-rail--collapsible:not(.ui-rail--expanded):has(.ui-rail__item--on) @@ -176,7 +184,8 @@ body.cockpit-mode /* Authoring, Utilities or Visual may still be open underneath; its hidden icon must not split the rail into islands. */ -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) #left-panel-stack > .gev-panel-rail--left .ui-rail:has( @@ -187,7 +196,8 @@ body.cockpit-mode )[aria-pressed='true'] ) > .ui-tooltip::before, -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) #left-panel-stack > .gev-panel-rail--left .ui-rail:has( @@ -201,7 +211,8 @@ body.cockpit-mode display: none; } -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) #left-panel-stack > .gev-panel-rail--left .ui-rail:has( @@ -218,8 +229,12 @@ body.cockpit-mode -webkit-backdrop-filter: blur(20px); } -body.cockpit-mode #left-panel-stack > [data-panel-id].collapsed, -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + #left-panel-stack + > [data-panel-id].collapsed, +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) #left-panel-stack > :is( [data-panel-id='scene-panel'], @@ -229,144 +244,6 @@ body.cockpit-mode display: none; } -/* Display and Radio as the main page's right rail: a column of labels and a - column of icon buttons, each reading as one glass strip. */ -body.cockpit-mode .cockpit-utility-controls { - gap: 0; -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher { - display: flex; - justify-content: flex-end; - align-items: stretch; - gap: 6px; - width: auto; - min-height: 0; - padding: 0; - border: 0; - border-radius: 0; - background: transparent; - box-shadow: none; - backdrop-filter: none; - -webkit-backdrop-filter: none; -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher - > span:first-child, -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph { - display: flex; - align-items: center; - box-sizing: border-box; - height: 40px; - border: 1px solid rgba(255, 255, 255, 0.08); - border-block-width: 0; - background: rgba(12, 12, 20, 0.72); - backdrop-filter: blur(24px) saturate(1.4); - -webkit-backdrop-filter: blur(24px) saturate(1.4); -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher - > span:first-child { - width: 120px; - padding: 0 14px; - color: rgba(224, 236, 242, 0.72); - font: 9px/1 var(--font-mono); - letter-spacing: 0.16em; -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-divider { - display: none; -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph { - justify-content: center; - width: 42px; - padding: 0; - border-radius: 0; - color: rgba(224, 236, 242, 0.82); - font-size: 0; - cursor: pointer; -} - -body.cockpit-mode .cockpit-utility-glyph::before { - font-family: 'Material Symbols Outlined'; - font-size: 18px; - font-weight: 400; - line-height: 1; - letter-spacing: normal; - -webkit-font-feature-settings: 'liga'; - font-feature-settings: 'liga'; -} - -body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-glyph { - font-size: 0; -} - -body.cockpit-mode #cockpit-display-toggle-btn::before { - content: 'tune'; -} - -body.cockpit-mode #cockpit-radio-toggle-btn::before { - content: 'radio'; -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph:hover { - color: #fff; - background: rgba(0, 212, 255, 0.12); -} - -/* Round the ends of each strip and draw its top / bottom edge. */ -body.cockpit-mode - .cockpit-utility-control:first-child:not(.is-expanded) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { - padding-top: 4px; - border-top-width: 1px; - border-radius: 12px 12px 0 0; -} - -body.cockpit-mode - .cockpit-utility-control:last-child:not(.is-expanded) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { - padding-bottom: 4px; - border-bottom-width: 1px; - border-radius: 0 0 12px 12px; -} - -/* A strip left alone beside an open panel is rounded all round. */ -body.cockpit-mode - .cockpit-utility-control.is-expanded - + .cockpit-utility-control:not(.is-expanded) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded):has( - + .cockpit-utility-control.is-expanded - ) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { - padding-block: 4px; - border-block-width: 1px; - border-radius: 12px; -} - -body.cockpit-mode - .cockpit-utility-control.is-expanded - + .cockpit-utility-control:not(.is-expanded) { - margin-top: 7px; -} - /* Map credits: one quiet centred line at the very bottom, under Reset / Exit Cockpit, clear of the HUD readouts in the corners. */ body.cockpit-mode #cesium-credits { @@ -396,9 +273,11 @@ body.cockpit-mode #cesium-credits img { } /* Both rails start at one height, just under the top HUD text, so their top - edges line up. */ -body.cockpit-mode #left-panel-stack, -body.cockpit-mode #cockpit-utility-controls { + edges line up; the cockpit layout pass places Display and Radio at the + same height (COCKPIT_UTILITY_MIN_TOP_PX / _RATIO in cockpitPresentation.js). */ +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + #left-panel-stack { top: max(206px, 22vh) !important; } @@ -454,7 +333,7 @@ body.cockpit-mode #intel-hud .hud-bottom-right { .cockpit-signal-list time { position: absolute; top: 6px; - right: 28px; + inset-inline-end: 28px; color: rgba(224, 236, 242, 0.4); font: 7px/1 var(--font-mono); letter-spacing: 0.06em; @@ -493,9 +372,9 @@ body.cockpit-mode #intel-hud .hud-bottom-right { letter-spacing: 0.08em; } -/* Keep a long callsign clear of the time at the top right. */ +/* Keep a long callsign clear of the time at the row's end. */ .cockpit-signal-list .cockpit-signal-target-label { - margin-right: 50px; + margin-inline-end: 50px; } /* ── Briefing content fits the narrower window ────────────────────────── */ @@ -563,56 +442,16 @@ body.cockpit-mode #intel-hud .hud-bottom-right { margin-inline-start: auto; } -/* The Display / Radio strips carry the left rail's exact frame: border, - glass, corners and drop shadow. */ -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher - > span:first-child, -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph { - border-color: rgba(255, 255, 255, 0.14); - background: rgba(16, 20, 28, 0.82); - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); -} - -body.cockpit-mode - .cockpit-utility-control:first-child:not(.is-expanded) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { - border-radius: 14px 14px 0 0; - box-shadow: - 0 6px 20px rgba(0, 0, 0, 0.45), - inset 0 1px 0 rgba(255, 255, 255, 0.05); -} - -body.cockpit-mode - .cockpit-utility-control:last-child:not(.is-expanded) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { - border-radius: 0 0 14px 14px; -} - -body.cockpit-mode - .cockpit-utility-control.is-expanded - + .cockpit-utility-control:not(.is-expanded) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph), -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded):has( - + .cockpit-utility-control.is-expanded - ) - :is(.cockpit-utility-launcher > span:first-child, .cockpit-utility-glyph) { - border-radius: 14px; -} - /* ── Open Display / Radio panels: the main panels' header and size ───── */ -body.cockpit-mode .cockpit-utility-control.is-expanded { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-control.is-expanded { min-width: 295px; box-sizing: border-box; } -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) .cockpit-utility-control.is-expanded .cockpit-utility-launcher { display: flex; @@ -624,7 +463,8 @@ body.cockpit-mode letter-spacing: 0.22em; } -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) .cockpit-utility-control.is-expanded .cockpit-utility-divider { flex: 0 1 22px; @@ -634,12 +474,18 @@ body.cockpit-mode box-shadow: none; } -/* The launcher's own toggle steps aside; "−" collapses instead. */ -body.cockpit-mode .cockpit-utility-control.is-expanded .cockpit-utility-glyph { +/* The launcher's own toggle steps aside: the rail and "−" open and close + the panel instead. */ +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-control.is-expanded + .cockpit-utility-glyph { display: none; } -body.cockpit-mode .cockpit-utility-collapse { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-collapse { display: grid; flex: none; place-items: center; @@ -654,82 +500,23 @@ body.cockpit-mode .cockpit-utility-collapse { cursor: pointer; } -body.cockpit-mode .cockpit-utility-collapse:hover, -body.cockpit-mode .cockpit-utility-collapse:focus-visible { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-collapse:hover, +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-collapse:focus-visible { border-color: rgba(0, 212, 255, 0.45); color: #fff; outline: none; } -/* Align-to-edge keeps the rail's inset (mirroring the left rail); docked, the - open panel takes the rail's place and the icons go above it (below). */ - -/* Same measurements as the left rail: a 170px label strip, 4px gap, 42px - icon strip, 38px rows. */ -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher { - gap: 4px; -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher - > span:first-child { - width: 170px; -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher - > span:first-child, -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph { - height: 38px; -} - -/* Hover as on the left rail: a 32px rounded square lights behind the icon - and the icon and its label brighten; the strip itself stays put. */ -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph::before { - display: grid; - place-items: center; - width: 32px; - height: 32px; - border: 1px solid transparent; - border-radius: var(--btn-radius, 8px); - box-sizing: border-box; - transition: - color var(--transition-fast, 120ms), - background var(--transition-fast, 120ms); -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph:hover { - background: rgba(16, 20, 28, 0.82); - color: var(--text-primary, #fff); -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-glyph:hover::before { - background: rgba(255, 255, 255, 0.05); -} - -body.cockpit-mode - .cockpit-utility-control:not(.is-expanded) - .cockpit-utility-launcher:has(.cockpit-utility-glyph:hover) - > span:first-child { - color: var(--text-primary, #fff); -} - /* ── Right rail as on the left: an icon column that stays, labels beside it while nothing is open, and the open panel to its left. The launchers' own strips give way to it. ─────────────────────────────────────────── */ -body.cockpit-mode .cockpit-utility-controls { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-controls { display: grid; grid-template-columns: auto auto; align-items: start; @@ -737,21 +524,30 @@ body.cockpit-mode .cockpit-utility-controls { column-gap: 6px; } -body.cockpit-mode .cockpit-utility-control:not(.is-expanded) { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-control:not(.is-expanded) { display: none; } -body.cockpit-mode .cockpit-utility-control.is-expanded { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-control.is-expanded { grid-column: 1; grid-row: 1; margin-top: 0 !important; } -.cockpit-utility-rail { +/* Cyber and the phone keep the launchers' own toggles, so the rail and "−" + are for the desktop look only. */ +.cockpit-utility-rail, +.cockpit-utility-collapse { display: none; } -body.cockpit-mode .cockpit-utility-rail { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail { grid-column: 2; grid-row: 1; display: flex; @@ -760,7 +556,8 @@ body.cockpit-mode .cockpit-utility-rail { pointer-events: auto; } -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) :is(.cockpit-utility-rail__labels, .cockpit-utility-rail__icons) { display: flex; flex-direction: column; @@ -776,13 +573,18 @@ body.cockpit-mode -webkit-backdrop-filter: blur(20px); } -body.cockpit-mode .cockpit-utility-rail__labels { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail__labels { box-sizing: border-box; width: 170px; padding-inline: 14px; } -body.cockpit-mode .cockpit-utility-rail__labels > span { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail__labels + > span { display: flex; align-items: center; height: 32px; @@ -795,11 +597,16 @@ body.cockpit-mode .cockpit-utility-rail__labels > span { letter-spacing: 2.08px; } -body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__labels { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail.is-open + .cockpit-utility-rail__labels { display: none; } -body.cockpit-mode .cockpit-utility-rail__item { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail__item { display: grid; place-items: center; width: 32px; @@ -816,24 +623,36 @@ body.cockpit-mode .cockpit-utility-rail__item { border-color var(--transition-fast, 120ms); } -body.cockpit-mode .cockpit-utility-rail__item .material-symbols-outlined { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail__item + .material-symbols-outlined { font-size: 18px; } -body.cockpit-mode .cockpit-utility-rail__item:hover { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail__item:hover { background: rgba(255, 255, 255, 0.05); color: var(--text-primary, #fff); } -body.cockpit-mode .cockpit-utility-rail__item.is-on, -body.cockpit-mode .cockpit-utility-rail__item.is-on:hover { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail__item.is-on, +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail__item.is-on:hover { border-color: rgba(0, 212, 255, 0.15); background: rgba(0, 212, 255, 0.15); color: #00d4ff; } /* With a panel open, each icon is its own island, as on the main rails. */ -body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__icons { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail.is-open + .cockpit-utility-rail__icons { border-color: transparent; background: none; box-shadow: none; @@ -841,11 +660,15 @@ body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__icons { -webkit-backdrop-filter: none; } -body.cockpit-mode .cockpit-utility-rail.is-open .cockpit-utility-rail__item { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-rail.is-open + .cockpit-utility-rail__item { position: relative; } -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) .cockpit-utility-rail.is-open .cockpit-utility-rail__item::before { position: absolute; @@ -861,54 +684,38 @@ body.cockpit-mode pointer-events: none; } -body.cockpit-mode.panels-edge-right - .cockpit-utility-rail.is-open - .cockpit-utility-rail__icons { - gap: 12px; -} - /* Hovering an icon lights its label too, as on the left. */ -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(1):hover) .cockpit-utility-rail__labels > span:nth-child(1), -body.cockpit-mode +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) .cockpit-utility-rail:has(.cockpit-utility-rail__item:nth-child(2):hover) .cockpit-utility-rail__labels > span:nth-child(2) { color: var(--text-primary, #fff); } -/* Docked (align-to-edge) with a panel open: as on the main page, the panel - takes the edge and the rail's icons sit in a row above it. */ -body.cockpit-mode.panels-edge-right - .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) { - grid-template-columns: auto; - justify-items: end; - row-gap: 8px; -} - -body.cockpit-mode.panels-edge-right - .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) - .cockpit-utility-rail { - grid-column: 1; - grid-row: 1; -} - -body.cockpit-mode.panels-edge-right - .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) - .cockpit-utility-control.is-expanded { - grid-column: 1; - grid-row: 2; -} - -body.cockpit-mode.panels-edge-right - .cockpit-utility-controls:has(.cockpit-utility-control.is-expanded) - .cockpit-utility-rail__icons { - flex-direction: row; -} - /* Mirror the left rail's 52px inset, clear of the HUD's vertical readouts. */ -body.cockpit-mode .cockpit-utility-controls { +:root:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-controls { right: 52px; } + +/* Like the cockpit's other windows (rtl.css), the rail keeps its physical + side in a right-to-left language: icons at the screen edge, the open panel + inside them. Only the text reads right to left. */ +html[dir='rtl']:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + .cockpit-utility-controls { + direction: ltr; +} + +html[dir='rtl']:not([data-ui-theme='cyber']) + body.cockpit-mode:not(.mobile-shell) + :is(.cockpit-utility-control.is-expanded, .cockpit-utility-rail__labels) { + direction: rtl; +} From 95a0be13b6678fdae5dca2928928ef9630035f77 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 14 Nov 2026 15:51:13 -0800 Subject: [PATCH 10/13] Open the cockpit panels from the rail in one step and give focus back to it The rail's buttons are disclosures (aria-expanded, aria-controls) and go through one toggleCockpitUtility action, which already keeps one panel open. Collapsing returns focus to the rail when the launcher's toggle is hidden. The edge switch is gone: edge mode does not apply in the cockpit. Co-authored-by: Manjunath Paparaju --- src/cockpitMarkup.test.mjs | 57 +++++++++++++------ src/ui/cockpitUtilityControls.test.mjs | 39 +++++++++++++ src/ui/radioBindings.js | 26 +++++++-- .../views/panels/CockpitUtilityControls.vue | 38 ++++--------- 4 files changed, 112 insertions(+), 48 deletions(-) diff --git a/src/cockpitMarkup.test.mjs b/src/cockpitMarkup.test.mjs index 3f461c392..791fdbdb2 100644 --- a/src/cockpitMarkup.test.mjs +++ b/src/cockpitMarkup.test.mjs @@ -26,10 +26,7 @@ import { setSignalCollapsed, } from './ui/cockpitLayout.js'; import { createBriefState } from './ui/cockpitBriefPages.js'; -import { - publishBriefView, - setBriefAutoRotate, -} from './ui/cockpitBriefing.js'; +import { publishBriefView, setBriefAutoRotate } from './ui/cockpitBriefing.js'; import { CockpitDisplayPortal } from './ui/cockpitDisplayPortal.js'; import { _handleContextLayerChange } from './ui/contextLayerChanges.js'; import { clearSelectedLayers } from './ui/contextActions.js'; @@ -438,13 +435,10 @@ test('Cockpit owns a focused shared Display portal and compact Radio controls', /data-cockpit-launcher="radio"[\s\S]*?id="cockpit-radio-toggle-btn"/, ); const cockpitRadio = html.match( - /id="cockpit-radio-panel"[\s\S]*?<\/div>\s*<\/div>\s*<\/aside>/, + /id="cockpit-radio-panel"[\s\S]*?<\/div>\s*<\/div>\s*
]*@click="act\('reset'\)"/); + assert.match( + globeActions, + /id="reset-globe-view"[^>]*@click="act\('reset'\)"/, + ); const cockpitView = readRadioSource( new URL('./ui/views/CockpitView.vue', import.meta.url), 'utf8', ); - assert.match(cockpitView, /id="cockpit-reset-globe"[^>]*@click="globe\.act\('reset'\)"/); + assert.match( + cockpitView, + /id="cockpit-reset-globe"[^>]*@click="globe\.act\('reset'\)"/, + ); assert.match( cockpitEnter.toString(), /Object\.assign\(this\.presence, \{\s*active: true,/, @@ -1193,7 +1196,10 @@ test('cockpit summary presents the focused item as Contact', () => { const match = html.match(/
From d9cc397ca94fea328a6f85d282b5ffef6e9d8998 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 14 Nov 2026 15:52:16 -0800 Subject: [PATCH 11/13] Describe the cockpit side panels Co-authored-by: Manjunath Paparaju --- docs/CURRENT-STATE.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 83529861b..65386ab41 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -661,9 +661,9 @@ cards keep Cyber's own red skin. The presets and chips call the same Display store actions as the dock's Visual Presets tray, which stays on desktop and phone as before, along with Location; voice `Visual Presets` still opens that tray. On a phone the More sheet shows that tray right above -Visual, so Visual leaves out its own presets and chips there. In Cockpit the -groups Cockpit's Display popover takes -leave the panel as before and keep their own styling there. +Visual, so Visual leaves out its own presets and chips there. In Cockpit, +the groups that Cockpit's Display popover takes leave the panel as before and +keep their own styling there. Scope has an Opacity slider (`#scope-opacity-slider`, 50–100%, default 100%; `SCOPE_OPACITY_MIN_PCT` in `src/ui/visualSettings.js`, since a fainter mask @@ -723,6 +723,28 @@ and touch screens, while the rails are hidden (clean view, recording), and it follows the rails' sides in a right-to-left language. On a phone, Visual is in the More sheet with the dock's Visual Presets, as before. +## Cockpit side panels — November 14, 2026 + +On the desktop, the cockpit's side panels match the main page's +(`src/ui/styles/cockpit-panels.css`). The left rail shows Data Layers only: +Authoring, Utilities and Visual, with their panels, are hidden, since the +cockpit's own Display panel stands in for Visual. Display and Radio +(`CockpitUtilityControls.vue`) become a right rail of two icons with their +names beside them, and each opens one panel at a time beside the rail, with a +header and a "−" to close it. The rail's buttons are disclosures +(`aria-expanded`, `aria-controls`) and go through the `toggleCockpitUtility` +action (`src/ui/radioBindings.js`); closing a panel gives focus back to the +rail. Both rails start at the same height: the left one in CSS, and Display +and Radio through the cockpit layout pass, whose floor +(`COCKPIT_UTILITY_MIN_TOP_PX` / `_RATIO` in `src/ui/cockpitPresentation.js`) +matches it, so an open panel still stops short of the briefing. Contact and +Live Signals are shorter, in the same glass, with the briefing's rows and +weather tiles tightened; Regional News stays a briefing page. The map credits +sit on one line at the bottom. In a right-to-left language, the rail keeps its +physical side as the cockpit's other windows do. The Cyber HUD and the phone +keep their own cockpit layouts, with the Display and Radio launchers as +before, and edge mode does not apply in the cockpit. + ## God's Eye View in conversations — October 2, 2026 Tool answers that can be shown in God's Eye View include a view: camera, layers, From 394bfe71c4952ee4ed918ee5f9f2ff03cc52ec28 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 14 Nov 2026 15:54:32 -0800 Subject: [PATCH 12/13] Leave the map credits' place in the cockpit to the credits change Restyling them here moved the credits where the attribution layout model cannot follow. Co-authored-by: Manjunath Paparaju --- docs/CURRENT-STATE.md | 3 +-- src/ui/styles/cockpit-panels.css | 28 ---------------------------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 65386ab41..6b814f4d6 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -739,8 +739,7 @@ and Radio through the cockpit layout pass, whose floor (`COCKPIT_UTILITY_MIN_TOP_PX` / `_RATIO` in `src/ui/cockpitPresentation.js`) matches it, so an open panel still stops short of the briefing. Contact and Live Signals are shorter, in the same glass, with the briefing's rows and -weather tiles tightened; Regional News stays a briefing page. The map credits -sit on one line at the bottom. In a right-to-left language, the rail keeps its +weather tiles tightened; Regional News stays a briefing page. In a right-to-left language, the rail keeps its physical side as the cockpit's other windows do. The Cyber HUD and the phone keep their own cockpit layouts, with the Display and Radio launchers as before, and edge mode does not apply in the cockpit. diff --git a/src/ui/styles/cockpit-panels.css b/src/ui/styles/cockpit-panels.css index 960c4407b..3c2d81f97 100644 --- a/src/ui/styles/cockpit-panels.css +++ b/src/ui/styles/cockpit-panels.css @@ -244,34 +244,6 @@ display: none; } -/* Map credits: one quiet centred line at the very bottom, under Reset / - Exit Cockpit, clear of the HUD readouts in the corners. */ -body.cockpit-mode #cesium-credits { - top: auto !important; - right: auto !important; - bottom: 8px !important; - left: 50% !important; - max-width: calc(100vw - 2rem) !important; - padding: 0.2rem 0.75rem !important; - border-radius: 999px !important; - transform: translateX(-50%); - white-space: nowrap; -} - -body.cockpit-mode - #cesium-credits - :is(.cesium-widget-credits, .cesium-credit-textContainer) { - display: inline-flex !important; - flex-wrap: nowrap !important; - align-items: center; - gap: 0.4rem; - white-space: nowrap; -} - -body.cockpit-mode #cesium-credits img { - height: 13px; -} - /* Both rails start at one height, just under the top HUD text, so their top edges line up; the cockpit layout pass places Display and Radio at the same height (COCKPIT_UTILITY_MIN_TOP_PX / _RATIO in cockpitPresentation.js). */ From fbf135163c94ac00de3dc317c7553bbf7db32953 Mon Sep 17 00:00:00 2001 From: Sameh Khamis Date: Sat, 14 Nov 2026 16:01:28 -0800 Subject: [PATCH 13/13] Note the cockpit side panels in the changelog Co-authored-by: Manjunath Paparaju --- changelog.d/1739-cockpit-side-panels.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog.d/1739-cockpit-side-panels.md diff --git a/changelog.d/1739-cockpit-side-panels.md b/changelog.d/1739-cockpit-side-panels.md new file mode 100644 index 000000000..a15167d32 --- /dev/null +++ b/changelog.d/1739-cockpit-side-panels.md @@ -0,0 +1,4 @@ +- Give the desktop cockpit the main page's side rails: Data Layers alone on the + left, and Display and Radio as a right rail that opens one panel at a time + beside it, at the same height, with Contact and Live Signals restyled to + match (manjunath22466, samehkhamis, #1739).