Let voice open the Utilities panel

Draw and Import used to be reachable by voice through DISPLAY. set_panel_open
now takes 'utilities-panel', and "utilities", "draw", "drawing" and "import"
reach it. This adds one value to the Realtime tool schema, so the pinned
payload digest, enum list and serialized length are re-derived with a note.

The show_data_layers_menu test now finds a row only inside the list that
really holds it, so dropping #utilities-toggles from the lookup fails it.
This commit is contained in:
Said Turksever committed 2026-11-02 16:43:49 +00:00
1 parent 38eb98b8f8
commit 46b23ccc06
6 files changed
+47 -5

No files matched your search

+3 -2
View File
@@ -1030,10 +1030,11 @@ test('the voice TOOL SCHEMA matches the pinned contract — the mission mapping
// preserves the mission mapping and globe-rotation arguments.
// S2 GhostMaps extends the generated layer enums; both feature contracts remain.
// The layer alias hint leaves out aliases the layer id already spells.
assert.equal(block.length, 30901, 'serialized tool schema length drifted');
// The Utilities panel adds one set_panel_open target.
assert.equal(block.length, 30919, 'serialized tool schema length drifted');
assert.equal(
crypto.createHash('sha256').update(block).digest('hex'),
'9e1bd8fde648953c47718ba248758415a95c1484b0da6369df6b89b0505c0de2',
'dbf9a45649179e626b2e3d938c43903f573c50ffa279b3245b18244dbe9a9284',
'the first-run missions must ride EXISTING tools: no schema edit, no cache bust',
);
const instructions = fs.readFileSync(
+1 -1
View File
@@ -152,7 +152,7 @@ test('the edited existing tools changed exactly as intended', () => {
const panel = byName.get('set_panel_open');
assert.deepEqual(
panel.parameters.properties.panelId.enum,
['data-panel', 'location-bar', 'control-panel', 'cctv-panel', 'radio-panel', 'scene-panel', 'pp-toggles', 'global-context-panel', 'weather-panel', 'recent-imagery-panel', 'voice-settings', 'voice-preview', 'provider-settings'],
['data-panel', 'location-bar', 'control-panel', 'cctv-panel', 'radio-panel', 'scene-panel', 'utilities-panel', 'pp-toggles', 'global-context-panel', 'weather-panel', 'recent-imagery-panel', 'voice-settings', 'voice-preview', 'provider-settings'],
);
assert.deepEqual(panel.parameters.required, ['panelId', 'open']);
+1
View File
@@ -174,6 +174,7 @@ const schemas = [
'cctv-panel',
'radio-panel',
'scene-panel',
'utilities-panel',
'pp-toggles',
'global-context-panel',
'weather-panel',
+3 -1
View File
@@ -43,12 +43,13 @@ test('the complete Realtime tool payload pins the manifest-generated layer relea
// Globe rotation adds one motion; visual keyhole fit and explicit
// reset guidance/defaults update only move_camera and mode descriptions.
// S2 GhostMaps extends the generated layer enums.
// The Utilities panel (Directions, Draw, Import) adds one panel target.
// Panel adds named voice targets and timeout; shared Panel/Cockpit guidance
// is consolidated without changing either feature's arguments.
// Snow leaves the set_visual_style enum.
// The Operator HUD layout leaves set_hud's layout enum.
// The layer alias hint leaves out aliases the layer id already spells.
'e65812db60a2702cebb496fca257cb04a28f1bbbf250cb9ab65a3db33005e0c8',
'dc2bac1be93a503dc88d637beea2f1bc457e1f9167422bd91548b44756a8aca5',
);
});
@@ -142,6 +143,7 @@ test('all legacy action arguments are byte-identical after removing the delibera
'voice-settings',
'voice-preview',
'provider-settings',
'utilities-panel',
].includes(id),
);
for (const tool of legacy) {
+7
View File
@@ -99,6 +99,12 @@ const PANEL_ALIASES = new Map([
['global context', 'global-context-panel'],
['right context', 'global-context-panel'],
['context right panel', 'global-context-panel'],
['utilities', 'utilities-panel'],
['utilities panel', 'utilities-panel'],
['draw', 'utilities-panel'],
['drawing', 'utilities-panel'],
['import', 'utilities-panel'],
['import files', 'utilities-panel'],
['authoring', 'scene-panel'],
['scenes', 'scene-panel'],
['scene', 'scene-panel'],
@@ -122,6 +128,7 @@ const PANEL_IDS = new Set([
'cctv-panel',
'radio-panel',
'global-context-panel',
'utilities-panel',
'scene-panel',
'pp-toggles',
'weather-panel',
+32 -1
View File
@@ -754,9 +754,17 @@ test('showing a layer opens the panel that holds its row: Directions lives in Ut
closest: (selector) => (selector === '[data-panel-id]' ? { id: panelId } : null),
});
const rows = { directions: rowIn('utilities-panel'), flights: rowIn('data-panel') };
// Each row is found only inside the list that really holds it.
const lists = { directions: '#utilities-toggles', flights: '#data-toggles' };
globalThis.CSS = { escape: (value) => value };
globalThis.document = {
querySelector: (selector) => rows[/data-layer-id="([^"]+)"/.exec(selector)?.[1]] || null,
querySelector: (selector) => {
for (const part of selector.split(',')) {
const [, list, id] = /^\s*(#[\w-]+) \[data-layer-id="([^"]+)"\]\s*$/.exec(part) || [];
if (id && lists[id] === list) return rows[id];
}
return null;
},
};
try {
const opened = [];
@@ -4001,3 +4009,26 @@ test('set_panel_open accepts the Authoring name for the scene panel', async () =
}
assert.deepEqual(calls.at(-1), ['scene-panel', false, { explicit: true }]);
});
test('set_panel_open reaches the Utilities panel by its name, Draw or Import', async () => {
const calls = [];
const runner = createGevActionRunner({
viewer: {
clock: { onTick: { addEventListener: () => () => {} } },
scene: { canvas: { addEventListener() {}, removeEventListener() {} } },
camera: {
moveEnd: { addEventListener() {} },
positionWC: Cesium.Cartesian3.fromDegrees(-97.7, 30.2, 1000),
},
},
styleManager: {
setPanelCollapsed: (...args) => calls.push(args),
},
dataManager: { layers: new Map(), isEnabled: () => false, getAll: () => [] },
});
for (const panel of ['utilities-panel', 'utilities', 'drawing', 'import']) {
const result = await runner('set_panel_open', { panelId: panel, open: true });
assert.equal(result.panelId, 'utilities-panel', panel);
}
assert.deepEqual(calls.at(-1), ['utilities-panel', false, { explicit: true }]);
});