merge main 43a67f9 into feat/voice-outlines

This commit is contained in:
manjunath committed 2026-11-03 00:26:40 +05:30
commit 3ac3499e4a
175 files changed
+4153 -522

No files matched your search

+5
View File
@@ -1996,6 +1996,11 @@ body.cockpit-mode #left-panel-stack {
body.cockpit-mode #left-panel-stack > #scene-panel {
display: none !important;
}
/* Directions stays reachable in Cockpit, as in Data Layers; Draw and Import
were never there (DISPLAY sits in the hidden right rail). */
body.cockpit-mode #utilities-panel .utilities-tools {
display: none !important;
}
body.cockpit-mode #right-context-rail {
display: none !important;
}
+30 -7
View File
@@ -495,6 +495,36 @@ body:not(.ui-clean-view):not(.recording-mode):has(
#command-dock .gev-voice-error-tray {
width: min(24rem, calc(100vw - 1.5rem));
}
/* The wings shrink to --dock-side-compact at this width, open or closed (an
open tray pops out above the dock), which is too narrow for the text
labels; they would spill over the voice control and take its taps. The
wings show only their icon; the toggles keep their aria-label. Cyber sets
a wider --dock-side-compact that fits its labels, so it keeps them. */
:root:not([data-ui-theme='cyber'])
#command-dock
:is(#location-bar, #control-panel)
.panel-title,
:root:not([data-ui-theme='cyber'])
#command-dock
:is(#location-bar, #control-panel)
.location-toolbar-label {
font-size: 0;
letter-spacing: 0;
/* The empty text is still a flex item; drop the gap it would claim. */
gap: 0;
}
:root:not([data-ui-theme='cyber'])
#command-dock
:is(#location-bar, #control-panel)
.panel-title::after,
:root:not([data-ui-theme='cyber'])
#command-dock
:is(#location-bar, #control-panel)
.location-toolbar-label::after {
content: none;
}
}
@media (max-width: 620px) {
@@ -503,13 +533,6 @@ body:not(.ui-clean-view):not(.recording-mode):has(
--dock-voice-width: 8.75rem;
}
#command-dock .panel-title,
#command-dock .location-toolbar-label {
max-width: 2.3rem;
overflow: hidden;
text-overflow: ellipsis;
}
#left-panel-stack {
bottom: calc(50vh + 8px);
}
+111 -24
View File
@@ -220,6 +220,7 @@ html:not([lang='en']):not(:lang(zh)):not(:lang(ja))
:is(#left-panel-stack, #right-context-rail)
:where(
.data-panel-inner,
.utilities-panel-inner,
.scene-panel-inner,
.cctv-panel-inner,
.weather-panel-inner,
@@ -236,6 +237,7 @@ html:not([lang='en']):not(:lang(zh)):not(:lang(ja))
:is(#left-panel-stack, #right-context-rail)
:where(
.data-panel-inner,
.utilities-panel-inner,
.scene-panel-inner,
.cctv-panel-inner,
.weather-panel-inner,
@@ -892,38 +894,104 @@ html:not([lang='en']) .pp-slider-row > .pp-slider-mini-label {
min-width: 0;
}
/* DISPLAY ▸ Draw: shape segment, label field, colour, and the one-line hint */
/* UTILITIES ▸ Draw: shape icons and colour swatches for the next shape, the
hint with Clear, then one row per drawn shape (named and recoloured there) */
#draw-mode-row {
min-width: 184px;
}
#draw-label-row {
flex-wrap: wrap;
row-gap: 6px;
column-gap: 10px;
}
.pp-text-input {
flex: 1 1 110px;
min-width: 0;
height: 24px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 6px;
color: var(--text-primary);
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.6px;
padding: 2px 6px;
#draw-mode-row .draw-shape-seg {
flex: 0 0 auto;
}
#draw-mode-row .draw-shape-seg .gev-segment {
min-width: 30px;
padding: 3px 6px;
}
.draw-shape-seg .material-symbols-outlined {
display: block;
font-size: 16px;
line-height: 1;
}
.draw-colors {
display: flex;
align-items: center;
gap: 6px;
}
#utilities-panel .draw-color {
flex: 0 0 auto;
width: 16px;
height: 16px;
padding: 0;
border: 1px solid rgba(0, 0, 0, 0.45);
border-radius: 50%;
cursor: pointer;
}
/* The swatch keeps its own colour in every theme; only a ring marks it. */
#utilities-panel .draw-color:is(.active, :hover, :focus-visible) {
border-color: rgba(0, 0, 0, 0.45);
box-shadow: none;
}
#utilities-panel .draw-color.active,
#utilities-panel .draw-color:focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: 2px;
}
#draw-label-row {
justify-content: space-between;
gap: 8px;
}
#draw-label-row .draw-hint {
flex: 1 1 auto;
}
.draw-mark-row {
flex-wrap: wrap;
}
.draw-mark-swatch {
flex: 0 0 10px;
width: 10px;
height: 10px;
padding: 0;
border: 0;
border-radius: 3px;
cursor: pointer;
}
.draw-mark-swatch:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
/* The name reads as text until it is clicked into, and gives way so the
row's buttons stay on its line. */
.draw-mark-row .draw-mark-name {
flex: 1 1 0;
width: 0;
height: 22px;
padding: 0 4px;
background: transparent;
border: 1px solid transparent;
border-radius: 5px;
outline: none;
}
.pp-text-input:focus {
.draw-mark-name::placeholder {
color: var(--text-primary);
opacity: 0.85;
}
.draw-mark-name:hover {
border-color: rgba(255, 255, 255, 0.14);
}
.draw-mark-name:focus {
border-color: var(--accent);
background: rgba(255, 255, 255, 0.06);
}
.draw-color-select {
flex: 0 0 74px;
.draw-mark-row.hidden-mark .draw-mark-name,
.draw-mark-row.hidden-mark .draw-mark-swatch {
opacity: 0.45;
}
/* Sized to its longest colour name so a translated one is never clipped. */
html:not([lang='en']) .draw-color-select {
flex: 0 0 auto;
min-width: 74px;
.draw-mark-colors {
/* Right after its swatch for keyboard and screen readers, shown last. */
order: 1;
flex-basis: 100%;
padding: 2px 0 4px 16px;
}
.draw-clear-btn {
flex: 0 0 auto;
@@ -943,7 +1011,7 @@ body.gev-drawing #cesiumContainer canvas {
cursor: crosshair;
}
/* DISPLAY ▸ Import: one row per imported file, and the drop-target outline */
/* UTILITIES ▸ Import: one row per imported file, and the drop-target outline */
.geo-import-list {
display: none;
flex-direction: column;
@@ -1102,6 +1170,7 @@ body.ui-clean-view #data-panel,
body.ui-clean-view #cctv-panel,
body.ui-clean-view #radio-panel,
body.ui-clean-view #global-context-panel,
body.ui-clean-view #utilities-panel,
body.ui-clean-view #scene-panel {
opacity: 0 !important;
visibility: hidden !important;
@@ -1269,3 +1338,21 @@ html:not([lang='en']) .param-slider-row {
flex: 1;
min-width: 0;
}
/* Touch: Draw's swatches keep their small dots but answer a 24px target. */
@media (pointer: coarse) {
#utilities-panel .draw-color,
#utilities-panel .draw-mark-swatch {
position: relative;
}
#utilities-panel .draw-color::before,
#utilities-panel .draw-mark-swatch::before {
position: absolute;
inset: 50% auto auto 50%;
width: 24px;
height: 24px;
transform: translate(-50%, -50%);
content: '';
}
}
+25 -9
View File
@@ -201,6 +201,7 @@
.recent-imagery-panel-inner,
.street-level-panel-inner,
.scene-panel-inner,
.utilities-panel-inner,
.global-context-panel-inner,
.radio-panel-inner,
#pp-toggles:not(.collapsed),
@@ -222,6 +223,7 @@
.recent-imagery-panel-inner,
.street-level-panel-inner,
.scene-panel-inner,
.utilities-panel-inner,
.global-context-panel-inner,
.radio-panel-inner
)
@@ -304,6 +306,7 @@
.recent-imagery-panel-inner,
.street-level-panel-inner,
.scene-panel-inner,
.utilities-panel-inner,
.global-context-panel-inner,
.radio-panel-inner
)
@@ -320,6 +323,7 @@
.recent-imagery-panel-inner,
.street-level-panel-inner,
.scene-panel-inner,
.utilities-panel-inner,
.global-context-panel-inner,
.radio-panel-inner
)
@@ -344,6 +348,7 @@
.recent-imagery-panel-inner,
.street-level-panel-inner,
.scene-panel-inner,
.utilities-panel-inner,
.global-context-panel-inner,
.radio-panel-inner
)
@@ -517,6 +522,14 @@
color: #f7f8f7;
}
:root[data-ui-theme='cyber']
#utilities-panel
.utilities-tools
.pp-toggle-btn:is([aria-pressed='true'], .active) {
background: rgba(64, 45, 46, 0.94);
box-shadow: 3px 0 0 var(--cyber-red) inset;
}
:root[data-ui-theme='cyber'] .pp-toggle-btn.active .pp-label {
color: #f5f7f6;
}
@@ -1680,7 +1693,7 @@
:root[data-ui-theme='cyber']
body:not(.cockpit-mode)
#left-panel-stack:has(> [data-panel-id]:not(.collapsed))
> :is(#data-panel, #cctv-panel, #scene-panel):not(.collapsed) {
> :is(#data-panel, #cctv-panel, #utilities-panel, #scene-panel):not(.collapsed) {
flex: 1 1 auto;
height: auto;
max-height: none;
@@ -1702,21 +1715,21 @@
:root[data-ui-theme='cyber']
#left-panel-stack
> :is(#data-panel, #scene-panel) {
> :is(#data-panel, #utilities-panel, #scene-panel) {
--panel-expanded-width: var(--cyber-panel-width);
width: var(--panel-expanded-width);
}
:root[data-ui-theme='cyber']
#left-panel-stack
> :is(#data-panel, #scene-panel).collapsed {
> :is(#data-panel, #utilities-panel, #scene-panel).collapsed {
width: var(--cyber-panel-collapsed-width);
}
:root[data-ui-theme='cyber']
#left-panel-stack
> :is(#data-panel, #scene-panel).collapsed
:is(.data-panel-inner, .scene-panel-inner) {
> :is(#data-panel, #utilities-panel, #scene-panel).collapsed
:is(.data-panel-inner, .utilities-panel-inner, .scene-panel-inner) {
box-sizing: border-box;
height: 50px;
min-height: 50px;
@@ -1743,7 +1756,7 @@
:root[data-ui-theme='cyber']
body:not(.cockpit-mode)
:is(#data-panel, #scene-panel)
:is(#data-panel, #utilities-panel, #scene-panel)
.panel-collapse-btn {
width: var(--cyber-panel-control-size);
min-width: var(--cyber-panel-control-size);
@@ -2758,7 +2771,10 @@
border-inline-start: 0;
}
:root[data-ui-theme='cyber'] body.cockpit-mode #left-panel-stack > #data-panel {
:root[data-ui-theme='cyber']
body.cockpit-mode
#left-panel-stack
> :is(#data-panel, #utilities-panel) {
--panel-expanded-width: var(--cyber-panel-width);
width: var(--cyber-panel-width);
max-height: min(64vh, 610px);
@@ -2767,7 +2783,7 @@
:root[data-ui-theme='cyber']
body.cockpit-mode
#left-panel-stack
> #data-panel.collapsed {
> :is(#data-panel, #utilities-panel).collapsed {
width: var(--cyber-panel-collapsed-width);
}
@@ -2800,7 +2816,7 @@
:root[data-ui-theme='cyber']
body.cockpit-mode
#left-panel-stack
> #data-panel:not(.collapsed) {
> :is(#data-panel, #utilities-panel):not(.collapsed) {
flex: 0 0 var(--cyber-cockpit-panel-height, 40vh);
height: var(--cyber-cockpit-panel-height, 40vh);
max-height: var(--cyber-cockpit-panel-height, 40vh);
@@ -105,8 +105,6 @@ test('Display and parameter rows keep their English layout', () => {
assert.equal(unguardedRules(source, '#param-sliders').length, 0);
const [row] = unguardedRules(source, '.param-slider-row');
assert.equal(declaration(row, 'display'), 'flex');
const [color] = unguardedRules(source, '.draw-color-select');
assert.equal(declaration(color, 'flex'), '0 0 74px');
});
test('weather setting rows keep the 56px English label column', () => {
+45
View File
@@ -116,6 +116,51 @@
opacity: 0.35;
cursor: default;
}
/* A collapsible rail shows each item as an icon and its label while no panel
is open, and folds the labels away once one is. The collapsed item keeps
the plain rail's 32px square. */
.ui-rail--collapsible {
transition: border-radius var(--transition-fast);
}
.ui-rail--collapsible .ui-rail__item {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
min-width: 32px;
padding: 0 6px;
transition:
color var(--transition-fast),
background var(--transition-fast),
border-color var(--transition-fast),
border-radius var(--transition-fast);
}
.ui-rail__label {
max-width: 0;
overflow: hidden;
opacity: 0;
white-space: nowrap;
text-overflow: ellipsis;
font: var(--dock-label-font-weight) var(--dock-label-font-size) / 1
var(--font-sans);
letter-spacing: var(--dock-label-letter-spacing);
transition:
max-width var(--transition-fast),
margin var(--transition-fast),
opacity var(--transition-fast);
}
.ui-rail--expanded {
border-radius: 21px;
}
.ui-rail--expanded .ui-rail__item {
border-radius: 16px;
}
.ui-rail--expanded .ui-rail__label {
max-width: 220px;
margin-inline: 8px 8px;
opacity: 1;
}
.ui-rail__badge {
position: absolute;
top: 1px;
+102 -4
View File
@@ -40,7 +40,8 @@
transform: translateY(0);
}
#data-panel .panel-glow {
#data-panel .panel-glow,
#utilities-panel .panel-glow {
position: absolute;
inset: -20px;
background: radial-gradient(
@@ -53,7 +54,8 @@
pointer-events: none;
}
.data-panel-inner {
.data-panel-inner,
.utilities-panel-inner {
position: relative;
background: var(--glass-bg);
border: 1px solid var(--glass-border);
@@ -255,11 +257,14 @@
pointer-events: auto;
transition: top var(--transition-fast);
}
/* Each rail keeps its outer edge at the screen-side clearance, so a rail that
widens (labelled pills while its side has no open panel) grows toward the
middle of the screen. Collapsed, it sits 6px outside its stack. */
.gev-panel-rail--left {
inset-inline-end: calc(100% + 6px);
inset-inline-start: calc(-1 * var(--panel-rail-clearance));
}
.gev-panel-rail--right {
inset-inline-start: calc(100% + 6px);
inset-inline-end: calc(-1 * var(--panel-rail-clearance));
}
body.mobile-shell #left-panel-stack > .gev-panel-rail,
body.mobile-shell #right-context-rail > .gev-panel-rail,
@@ -1255,3 +1260,96 @@ body.recording-mode #right-context-rail > [data-panel-id] {
width: 100%;
}
}
/* ── Utilities Panel ─────────────────────────── */
/* Directions (from the layer list), Draw and Import. Draw and Import read as
rows like Directions above them (.data-toggle-row): icon and name, their
options indented under the name. */
#utilities-panel {
--panel-expanded-width: 280px;
width: var(--panel-expanded-width);
}
/* The layer list takes its natural height; the panel scrolls as a whole. */
.utilities-list {
flex: 0 0 auto;
max-height: none;
overflow: visible;
}
.utilities-tools {
display: flex;
flex-direction: column;
gap: 2px;
margin-top: 2px;
}
.utilities-tools .pp-toggle-group {
width: 100%;
}
#utilities-panel .utilities-tools .pp-toggle-btn {
width: 100%;
min-height: 0;
box-sizing: border-box;
gap: 8px;
padding: 8px 10px 6px;
color: var(--text-primary);
text-align: left;
background: transparent;
border: 0;
border-radius: 8px;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
transition: background var(--transition-fast);
}
#utilities-panel .utilities-tools .pp-toggle-btn:hover {
background: rgba(255, 255, 255, 0.03);
}
#utilities-panel .utilities-tools .pp-icon {
flex: none;
width: 16px;
font-size: 16px;
line-height: 1;
color: var(--text-primary);
text-align: center;
}
#utilities-panel .utilities-tools .pp-label {
font-family: var(--font-sans);
font-size: 13px;
font-weight: 500;
letter-spacing: 0.3px;
text-transform: none;
color: var(--text-primary);
}
/* Draw marks its on state with an accent edge, like an active DISPLAY
button; Cyber swaps in its red edge (cyber.css). */
#utilities-panel
.utilities-tools
.pp-toggle-btn:is([aria-pressed='true'], .active) {
background: var(--accent-dim);
box-shadow: inset 2px 0 0 var(--accent);
}
#utilities-panel
.utilities-tools
:is(.pp-slider-row, .geo-import-list, .geo-import-hint) {
margin-left: 34px;
margin-right: 10px;
}
/* Draw's options sit flat under its name, like a layer row's controls, not
as the card that hangs under a DISPLAY button. */
#utilities-panel .utilities-tools .pp-slider-row {
padding: 4px 0;
background: none;
border: 0;
border-radius: 0;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
+3
View File
@@ -166,6 +166,9 @@ body.mobile-shell #right-context-rail > #recent-imagery-panel {
/* Rail-scoped ids so a rail's own hiding rules (for example the Tactical HUD
hiding collapsed siblings) never take a panel out of an open sheet. */
body.mobile-shell[data-mobile-sheet='layers'] #left-panel-stack > #data-panel,
body.mobile-shell[data-mobile-sheet='layers']
#left-panel-stack
> #utilities-panel,
body.mobile-shell[data-mobile-sheet='layers'] #left-panel-stack > #scene-panel,
body.mobile-shell[data-mobile-sheet='cameras']
#right-context-rail
+1
View File
@@ -15,6 +15,7 @@ body.recording-mode #cctv-panel,
body.recording-mode #radio-panel,
body.recording-mode #global-context-panel,
body.recording-mode #gev-voice-control,
body.recording-mode #utilities-panel,
body.recording-mode #scene-panel {
opacity: 0 !important;
visibility: hidden !important;
+2
View File
@@ -220,11 +220,13 @@
#left-panel-stack > #cctv-panel,
#left-panel-stack > #street-level-panel,
#left-panel-stack > #global-context-panel,
#left-panel-stack > #utilities-panel,
#left-panel-stack > #scene-panel,
#left-panel-stack > #data-panel.collapsed,
#left-panel-stack > #cctv-panel.collapsed,
#left-panel-stack > #street-level-panel.collapsed,
#left-panel-stack > #global-context-panel.collapsed,
#left-panel-stack > #utilities-panel.collapsed,
#left-panel-stack > #scene-panel.collapsed {
position: relative;
top: auto;
+16 -1
View File
@@ -16,6 +16,7 @@
#left-panel-stack > #data-panel,
#left-panel-stack > #cctv-panel,
#left-panel-stack > #global-context-panel,
#left-panel-stack > #utilities-panel,
#left-panel-stack > #scene-panel {
position: relative;
top: auto;
@@ -37,6 +38,10 @@
order: 2;
}
#left-panel-stack > #utilities-panel {
order: 3;
}
#left-panel-stack > #scene-panel {
order: 4;
}
@@ -52,6 +57,7 @@
#left-panel-stack > #data-panel:not(.collapsed),
#left-panel-stack > #cctv-panel:not(.collapsed),
#left-panel-stack > #utilities-panel:not(.collapsed),
#left-panel-stack > #scene-panel:not(.collapsed) {
box-sizing: border-box;
flex: 0 1 var(--left-panel-allocated-height, auto);
@@ -64,7 +70,9 @@
* the canonical control order and center their combined stack at 50vh.
*/
#left-panel-stack.layout-tail
> :is(#data-panel, #cctv-panel, #scene-panel):not(.collapsed) {
> :is(#data-panel, #cctv-panel, #utilities-panel, #scene-panel):not(
.collapsed
) {
box-sizing: border-box;
flex: 0 0 var(--left-panel-allocated-height);
height: var(--left-panel-allocated-height);
@@ -73,6 +81,7 @@
#left-panel-stack > #data-panel:not(.collapsed) .data-panel-inner,
#left-panel-stack > #cctv-panel:not(.collapsed) .cctv-panel-inner,
#left-panel-stack > #utilities-panel:not(.collapsed) .utilities-panel-inner,
#left-panel-stack > #scene-panel:not(.collapsed) .scene-panel-inner {
height: 100%;
max-height: 100%;
@@ -83,6 +92,7 @@
}
#left-panel-stack > #cctv-panel:not(.collapsed) .cctv-panel-inner,
#left-panel-stack > #utilities-panel:not(.collapsed) .utilities-panel-inner,
#left-panel-stack > #scene-panel:not(.collapsed) .scene-panel-inner {
overflow-y: auto;
scrollbar-gutter: stable;
@@ -449,6 +459,8 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
#scene-panel.collapsed .scene-shot-list,
#scene-panel.collapsed .scene-progress,
#scene-panel.collapsed #scene-status,
#utilities-panel.collapsed .utilities-list,
#utilities-panel.collapsed .utilities-tools,
#param-slider-panel.collapsed #param-sliders,
#location-bar.collapsed .poi-row-container,
#location-bar.collapsed .location-bar-divider,
@@ -459,6 +471,7 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
#data-panel.collapsed,
#cctv-panel.collapsed,
#utilities-panel.collapsed,
#scene-panel.collapsed {
width: var(--left-collapsed-width);
}
@@ -472,6 +485,7 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
#cctv-panel.collapsed .cctv-panel-inner,
#radio-panel.collapsed .radio-panel-inner,
#scene-panel.collapsed .scene-panel-inner,
#utilities-panel.collapsed .utilities-panel-inner,
#param-slider-panel.collapsed,
#location-bar.collapsed .location-inner {
padding-bottom: 10px;
@@ -483,6 +497,7 @@ html:not([lang='en']) #scene-panel .scene-controls .scene-btn {
#radio-panel.collapsed .panel-header,
#global-context-panel.collapsed .panel-header,
#scene-panel.collapsed .panel-header,
#utilities-panel.collapsed .panel-header,
#param-slider-panel.collapsed .param-panel-header {
margin-bottom: 2px;
}