Merge upstream/main (v0.2.1) into pr/mapillary-layer

Brings in 123 commits from main, including the v0.2.0 and v0.2.1 releases
(MCP server and tool catalog, server security hardening, build hygiene).

Conflicts:
- package.json: kept main's script order and added this PR's
  qa:panel-resize and qa:street-level; exports keep both sides (main's
  tools/view entries and this PR's street-level and Mapillary entries).
- src/tooling/viteBuild.test.mjs: kept this PR's Mapillary-token define
  assertion and main's new wildcard-host allowedHosts assertions.
- CHANGELOG.md: main now has release sections; this PR's entries go under
  [Unreleased].

Checks after the merge: doctor, format:check, check:boundaries,
layer-token:check (28 published, 1 new: street-level 0), npm test (5726
pass), build, the Windows job's focused tests (70 pass), qa:panel-resize,
and the keyless street-level gate. The new CSP allows Mapillary (https:
connect/img, blob workers). The keyed gate stops at coverage locally
because the dev .env Mapillary token is invalid (upstream 403), not because
of the merge.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-03 13:29:32 +01:00
commit 5a7ce5785e
176 files changed
+19654 -1431

No files matched your search

+123 -11
View File
@@ -185,6 +185,11 @@
0 8px 20px rgba(0, 0, 0, 0.28);
}
/* Compact Cyber rails must wrap multi-word actions within their own button. */
:root[data-ui-theme='cyber'] #scene-panel .scene-controls .scene-btn {
white-space: normal;
}
/* Carry the raised voice-module silhouette across the primary panel system.
The six-pixel calibration bay is decorative and stays outside panel content. */
:root[data-ui-theme='cyber']
@@ -224,6 +229,65 @@
border-bottom: 1px solid rgba(206, 222, 226, 0.08);
}
/* The right-rail surfaces use different content gutters, but their disclosure
controls should still read as one vertical column. Eight pixels here plus
the shared two-pixel header inset matches Display's eleven-pixel framed
edge inset once the surface border is included. */
:root[data-ui-theme='cyber']
#right-context-rail
:is(
.cctv-panel-inner,
.weather-panel-inner,
.recent-imagery-panel-inner,
.global-context-panel-inner
) {
padding-right: 8px;
}
/* Collapsed right-rail panels otherwise inherit different legacy gutters:
CCTV lands five pixels low while Context lands four pixels high. Give every
compact launcher the same centered 48px content row inside its 1px frame. */
:root[data-ui-theme='cyber']
#right-context-rail
:is(
#cctv-panel,
#weather-panel,
#recent-imagery-panel,
#global-context-panel
).collapsed
> :is(
.cctv-panel-inner,
.weather-panel-inner,
.recent-imagery-panel-inner,
.global-context-panel-inner
) {
padding: 0;
}
:root[data-ui-theme='cyber']
#right-context-rail
:is(
#cctv-panel,
#weather-panel,
#recent-imagery-panel,
#global-context-panel
).collapsed
> :is(
.cctv-panel-inner,
.weather-panel-inner,
.recent-imagery-panel-inner,
.global-context-panel-inner
)
> .panel-header {
box-sizing: border-box;
width: 100%;
height: 48px;
min-height: 48px;
margin: 0;
padding: 0 10px 0 14px;
border-bottom: 0;
}
:root[data-ui-theme='cyber']
:is(
.data-panel-inner,
@@ -924,7 +988,24 @@
--cyber-voice-wave: 105, 203, 209;
--cyber-voice-peak: 226, 240, 240;
--cyber-voice-alert: 196, 84, 84;
border-color: rgba(196, 84, 84, 0.58);
/* Popups extend above this root. Only its decorative layer may be clipped. */
isolation: isolate;
overflow: visible;
clip-path: none;
border-color: transparent;
background: none;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
:root[data-ui-theme='cyber'] #command-dock > #gev-voice-control::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
pointer-events: none;
border: 1px solid rgba(196, 84, 84, 0.58);
border-radius: 0;
background:
linear-gradient(180deg, rgba(196, 84, 84, 0.14), transparent 34%),
@@ -1192,8 +1273,30 @@
:root[data-ui-theme='cyber'] #command-dock .dock-popover-content {
padding-top: 1.65rem;
border: 1px solid rgba(196, 84, 84, 0.62);
/* The pin straddles the tray edge; keep it outside the decorative mask. */
isolation: isolate;
clip-path: none;
border-color: transparent;
border-radius: 0;
background: none;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
:root[data-ui-theme='cyber']
#command-dock
:is(#location-bar, #control-panel)
.dock-popover-content::after {
content: '';
position: absolute;
inset: 0;
z-index: -1;
width: auto;
height: auto;
transform: none;
pointer-events: none;
border: 1px solid rgba(196, 84, 84, 0.62);
background:
linear-gradient(135deg, rgba(62, 86, 94, 0.22), transparent 46%),
linear-gradient(rgba(188, 211, 218, 0.045) 1px, transparent 1px),
@@ -1456,6 +1559,12 @@
bottom: calc(2vh + 6.5rem);
}
/* Attribution's logo row extends above its text baseline. Reserve the whole
row plus a gap above the dock-relative 2vh + 5rem credit position. */
#intel-hud[data-variant='cyber'] .hud-bottom-left {
bottom: calc(2vh + 7.75rem);
}
#intel-hud[data-variant='cyber'] .hud-corner {
position: fixed;
isolation: isolate;
@@ -1493,12 +1602,21 @@
/* Cyber also owns a distinct desktop composition. Existing components keep
their controls and semantics, but move onto asymmetric mission-deck rails. */
@media (min-width: 961px) and (max-height: 649px) {
/* The compact-height layout drops the desktop rail composition one pixel
below its 650px threshold. Keep the left rail inside the same telemetry
and attribution-safe floor while its panel bodies take over scrolling. */
:root[data-ui-theme='cyber'] #left-panel-stack {
bottom: calc(2vh + 13rem);
}
}
@media (min-width: 961px) and (min-height: 650px) {
:root[data-ui-theme='cyber'] {
/* Compose the upper mission card and both panel rails as one compact lane.
The shared rail anchor keeps expanded left/right panels aligned. */
--cyber-hud-card-top: max(100px, 10.25vh);
--cyber-left-rail-top: max(252px, calc(var(--cyber-hud-card-top) + 146px));
--cyber-left-rail-top: max(220px, calc(var(--cyber-hud-card-top) + 120px));
}
/* Keep the rotated COLL / ONA telemetry in the upper clearance lane. Its
@@ -1510,7 +1628,7 @@
:root[data-ui-theme='cyber'] #left-panel-stack {
top: var(--cyber-left-rail-top) !important;
bottom: calc(2vh + 11rem);
bottom: calc(2vh + 13rem);
left: 54px;
width: 300px;
padding-left: 10px;
@@ -1564,7 +1682,7 @@
.cockpit-utility-launcher {
height: 50px;
min-height: 50px;
padding: 8px 18px 10px 24px;
padding: 9px 18px 9px 24px;
}
:root[data-ui-theme='cyber']
@@ -1709,12 +1827,6 @@
right: 28px;
bottom: 28px;
}
/* Match the lower telemetry cards by their top edge while leaving the
shorter coordinate card clear of Cesium attribution. */
#intel-hud[data-variant='cyber'] .hud-bottom-left {
bottom: 60px;
}
}
@media (max-width: 720px) {
+14
View File
@@ -0,0 +1,14 @@
/* Embed mode (?embed=1): the globe alone, on top of clean view. Provider
attribution stays visible. */
body.ui-embed #clean-view-exit,
body.ui-embed #intel-hud,
body.ui-embed #key-setup-chip,
body.ui-embed #key-setup,
body.ui-embed #first-run-launcher,
body.ui-embed #command-dock,
body.ui-embed #left-panel-stack,
body.ui-embed #right-context-rail,
body.ui-embed #world-overlay-actions,
body.ui-embed #view-switcher {
display: none !important;
}
+230
View File
@@ -0,0 +1,230 @@
/* Shared structural hooks for panels admitted by the existing rail owners.
Specific legacy component rules retain priority; no panel IDs are registered
here. The rail itself remains pointer-transparent over the globe. */
:is(#left-panel-stack, #right-context-rail)
> [data-panel-id]:has(> [data-panel-surface]) {
position: relative;
inset: auto;
flex: 0 0 auto;
min-height: 0;
pointer-events: auto;
}
#left-panel-stack
> [data-panel-id]:has(> [data-panel-surface]):not(.collapsed) {
flex: 0 1 var(--left-panel-allocated-height, auto);
height: var(--left-panel-allocated-height, auto);
max-height: var(--left-panel-allocated-height, 100%);
}
#left-panel-stack.layout-tail
> [data-panel-id]:has(> [data-panel-surface]):not(.collapsed) {
flex: 0 0 var(--left-panel-allocated-height);
}
#right-context-rail.layout-focus
> [data-panel-id]:has(> [data-panel-surface]):not(.collapsed) {
flex: 0 1 var(--right-panel-allocated-height);
height: var(--right-panel-allocated-height);
max-height: var(--right-panel-allocated-height);
}
/* Opt-in skin: the positioned surface can contain an outward popup. Only the
non-interactive decoration is clipped; only data-panel-body scrolls. */
:root {
--panel-surface-font: var(--font-sans);
--panel-surface-detail: none;
--panel-surface-frame: none;
--panel-surface-shadow: 0 8px 24px rgba(0, 0, 0, 0.38);
--panel-surface-title: var(--text-secondary);
--panel-surface-error: #ff9b8e;
--panel-surface-width: 300px;
--panel-surface-collapsed-width: var(--left-collapsed-width);
}
:root[data-ui-theme='cyber'] {
--panel-surface-font: var(--font-mono);
--panel-surface-detail: var(--cyber-panel-surface-detail);
--panel-surface-frame: var(--cyber-panel-frame);
--panel-surface-shadow: var(--cyber-panel-shadow);
--panel-surface-title: var(--cyber-red-bright);
--panel-surface-error: var(--cyber-red-bright);
--panel-surface-width: var(--cyber-panel-width);
--panel-surface-collapsed-width: var(--cyber-panel-collapsed-width);
}
:root[data-ui-theme='cyber']
#right-context-rail
> [data-panel-id]:has(> [data-panel-surface]) {
--panel-surface-width: var(--cyber-right-panel-width);
}
:is([data-panel-surface], [data-panel-popup]) {
isolation: isolate;
min-width: 0;
min-height: 0;
padding: 12px;
border: 1px solid transparent;
border-radius: var(--panel-radius);
background: none;
color: var(--text-primary);
font: 12px/1.5 var(--panel-surface-font);
overflow: visible;
}
[data-panel-surface]:not(dialog) {
position: relative;
}
:is([data-panel-surface], [data-panel-popup])::before {
content: '';
position: absolute;
inset: -1px;
z-index: -1;
border: 1px solid var(--glass-border);
border-radius: inherit;
background-color: var(--glass-bg);
background-image: var(--panel-surface-detail);
box-shadow: var(--panel-surface-shadow);
clip-path: var(--panel-surface-frame);
pointer-events: none;
}
/* A closed native dialog must retain the browser's display:none. */
[data-panel-surface]:not([hidden]):not(dialog:not([open])) {
display: flex;
flex-direction: column;
gap: 10px;
}
:is(#left-panel-stack, #right-context-rail)
> [data-panel-id]:has(> [data-panel-surface]) {
width: min(100%, var(--panel-expanded-width, var(--panel-surface-width)));
background: none;
box-shadow: none;
}
:is(#left-panel-stack, #right-context-rail)
> [data-panel-id]
> [data-panel-surface] {
height: 100%;
max-height: 100%;
}
:is(#left-panel-stack, #right-context-rail)
> [data-panel-id].collapsed:has(> [data-panel-surface]) {
width: min(
100%,
var(--panel-collapsed-width, var(--panel-surface-collapsed-width))
);
}
#right-context-rail[data-rail-measuring]
> [data-panel-id]
> [data-panel-surface] {
height: auto;
max-height: none;
}
[data-panel-header] {
display: flex;
align-items: center;
flex: 0 0 auto;
gap: 8px;
min-height: 30px;
padding-bottom: 8px;
border-bottom: 1px solid var(--glass-border);
}
.panel-surface-title {
flex: 1;
min-width: 0;
color: var(--panel-surface-title);
font: 600 11px/1.4 var(--font-mono);
letter-spacing: 0.1em;
text-transform: uppercase;
overflow-wrap: anywhere;
}
[data-panel-body] {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: var(--glass-border-hover) transparent;
/* Keep an inset focus ring inside the intentional scrolling boundary. */
padding: 3px;
}
.panel-surface-control {
min-width: 0;
min-height: 30px;
max-width: 100%;
padding: 5px 8px;
border: 1px solid var(--glass-border);
border-radius: var(--btn-radius);
background: var(--bg-dark);
color: var(--text-primary);
font: inherit;
accent-color: var(--accent);
}
button.panel-surface-control {
cursor: pointer;
}
.panel-surface-control:hover:not(:disabled):not([aria-disabled='true']) {
border-color: var(--glass-border-hover);
background: var(--accent-dim);
}
.panel-surface-control:focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: -3px;
}
.panel-surface-control:is(:disabled, [aria-disabled='true']) {
opacity: 0.5;
cursor: not-allowed;
}
.panel-surface-error,
.panel-surface-control[aria-invalid='true'] {
color: var(--panel-surface-error);
border-color: var(--panel-surface-error);
}
[data-panel-popup] {
position: absolute;
z-index: 5;
/* The owner supplies placement, width, collision handling and disclosure. */
}
[data-panel-surface].collapsed > :is([data-panel-body], [data-panel-popup]),
[data-panel-id].collapsed
> [data-panel-surface]
> :is([data-panel-body], [data-panel-popup]),
:is(#left-panel-stack, #right-context-rail)
> [data-panel-id]:has(> [data-panel-surface])[hidden],
:is(
[data-panel-surface],
[data-panel-header],
[data-panel-body],
[data-panel-popup]
)[hidden],
dialog[data-panel-surface]:not([open]) {
display: none !important;
}
/* Conforming additions follow the same concealment policy as existing chrome.
Cockpit continues to own rail/dock placement and exclusions in cockpit.css. */
body:is(.ui-clean-view, .recording-mode)
:is([data-panel-surface], [data-panel-popup]),
body:is(.ui-clean-view, .recording-mode)
:is(#left-panel-stack, #right-context-rail)
> [data-panel-id]:has(> [data-panel-surface]) {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}