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(() =>
+