mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 19:34:01 +00:00
feat(app): voice on the Navara engine
createViewerlessTools creates the voice commands with viewer: null on the engine (tools.js passes it the voice options and place search), so window.__gevVoiceCommands exists in ?engine=navara: the W1 tools drive the engine and the viewer-only tools answer UNAVAILABLE_ON_ENGINE. The Cesium path hands its engine to initGevVoiceCommands explicitly. viewerlessTools.test.mjs: the Navara tools now return voice commands (created without DOM through injected control and session stand-ins), whose runner answers get_current_view_state on the engine and refuses control_scene; the debug handle carries them and teardown removes them.
This commit is contained in:
1 parent
bc281ab246
commit
18b5475b8a
3 files changed
+74
-12
No files matched your search
+28
-3
@@ -12,6 +12,7 @@ import {
|
||||
installRenderGovernor,
|
||||
uninstallRenderGovernor,
|
||||
} from '../renderGovernor.js';
|
||||
import { initGevVoiceCommands } from '../voice/gevRealtime.js';
|
||||
|
||||
/** The Navara globe module, loaded on demand. */
|
||||
const loadNavaraGlobeModule = async () =>
|
||||
@@ -109,14 +110,17 @@ export async function createNavaraScene({
|
||||
|
||||
/**
|
||||
* The tools without a viewer (the experimental Navara engine): the startup
|
||||
* chrome, the render governor on the engine, hidden-tab handling and the
|
||||
* debug handle. The shell shows which features are unavailable.
|
||||
* chrome, the render governor on the engine, hidden-tab handling, voice on
|
||||
* the engine and the debug handle. The shell shows which features are
|
||||
* unavailable.
|
||||
*/
|
||||
export function createViewerlessTools({
|
||||
scene: { engine, mapStackController, operations },
|
||||
controls: { styleManager },
|
||||
data,
|
||||
loadingScreen,
|
||||
placeSearch,
|
||||
voice = {},
|
||||
startChrome,
|
||||
signal,
|
||||
defer,
|
||||
@@ -170,5 +174,26 @@ export function createViewerlessTools({
|
||||
defer(() => {
|
||||
if (window.__godsEyeView === debug) delete window.__godsEyeView;
|
||||
});
|
||||
return { sceneDirector: null, annotations: null, voiceCommands: null };
|
||||
// Voice on the engine (phase 3, Task 9): the W1 tools drive the engine;
|
||||
// the viewer-only ones (director, annotations, cockpit, radio…) refuse.
|
||||
const voiceCommands = initGevVoiceCommands({
|
||||
...voice,
|
||||
annotationResolver: operations.annotationResolver,
|
||||
searchNavigation: operations.searchAndFlyTo,
|
||||
signal,
|
||||
placeSearch,
|
||||
viewer: null,
|
||||
engine,
|
||||
styleManager,
|
||||
dataManager,
|
||||
sceneDirector: null,
|
||||
annotations: null,
|
||||
});
|
||||
defer(() => {
|
||||
voiceCommands.stop({ removeUi: true });
|
||||
if (window.__gevVoiceCommands === voiceCommands)
|
||||
delete window.__gevVoiceCommands;
|
||||
});
|
||||
debug.voiceCommands = voiceCommands;
|
||||
return { sceneDirector: null, annotations: null, voiceCommands };
|
||||
}
|
||||
+6
-2
@@ -17,8 +17,9 @@ import {
|
||||
/**
|
||||
* Attach scene tools, rendering listeners and the application debug handle.
|
||||
* Without a viewer (the experimental Navara engine) the viewer-bound tools
|
||||
* (director, annotations, drawing, imagery box, scope mask, tracking, voice,
|
||||
* embedded views) are not created; their handles are null.
|
||||
* (director, annotations, drawing, imagery box, scope mask, tracking,
|
||||
* embedded views) are not created; their handles are null. Voice runs on
|
||||
* the engine there (src/app/navaraMode.js).
|
||||
*/
|
||||
export function createApplicationTools({
|
||||
scene,
|
||||
@@ -44,6 +45,8 @@ export function createApplicationTools({
|
||||
controls,
|
||||
data,
|
||||
loadingScreen,
|
||||
placeSearch,
|
||||
voice,
|
||||
startChrome,
|
||||
signal,
|
||||
defer,
|
||||
@@ -189,6 +192,7 @@ export function createApplicationTools({
|
||||
signal,
|
||||
placeSearch,
|
||||
viewer,
|
||||
engine,
|
||||
styleManager,
|
||||
dataManager,
|
||||
sceneDirector,
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
|
||||
// The tools without a viewer (the experimental Navara engine); they load
|
||||
// from src/app/navaraMode.js.
|
||||
test('without a viewer the tools run the governor on the engine, show the notice and create no viewer tools', async (t) => {
|
||||
test('without a viewer the tools run the governor on the engine, show the notice, create no viewer tools and run voice on the engine', async (t) => {
|
||||
const saved = { window: globalThis.window, document: globalThis.document };
|
||||
const listeners = new Map();
|
||||
globalThis.window = {};
|
||||
@@ -34,7 +34,9 @@ test('without a viewer the tools run the governor on the engine, show the notice
|
||||
else globalThis[name] = value;
|
||||
}
|
||||
});
|
||||
const engine = createFakeEngine({ kind: 'navara' });
|
||||
// The voice pointer listens on the engine's canvas.
|
||||
const canvas = new EventTarget();
|
||||
const engine = createFakeEngine({ kind: 'navara', canvas });
|
||||
const notices = [];
|
||||
const chrome = [];
|
||||
const cleanups = [];
|
||||
@@ -43,7 +45,27 @@ test('without a viewer the tools run the governor on the engine, show the notice
|
||||
_showGlobalStatusNotice: (label, options) =>
|
||||
notices.push({ label, options }),
|
||||
};
|
||||
const dataManager = { layers: new Map() };
|
||||
const dataManager = { layers: new Map(), getAll: () => [] };
|
||||
// Voice without DOM or provider: a control and session stand-in.
|
||||
const button = new EventTarget();
|
||||
button.setAttribute = () => {};
|
||||
let uiRemoved = 0;
|
||||
const voice = {
|
||||
createControl: () => ({
|
||||
button,
|
||||
root: { dataset: {}, remove: () => uiRemoved++ },
|
||||
status: {},
|
||||
detail: {},
|
||||
}),
|
||||
createCard: () => null,
|
||||
createSession: (hooks) => ({
|
||||
runner: (voice.runner = hooks.runner),
|
||||
async start() {},
|
||||
stop() {},
|
||||
sendText() {},
|
||||
sendMapEvent() {},
|
||||
}),
|
||||
};
|
||||
const tools = await createApplicationTools({
|
||||
scene: {
|
||||
viewer: null,
|
||||
@@ -52,6 +74,7 @@ test('without a viewer the tools run the governor on the engine, show the notice
|
||||
mapStackController: { kind: 'maps' },
|
||||
operations: { surface: { kind: 'surface' } },
|
||||
},
|
||||
voice,
|
||||
controls: { styleManager, weatherEffects: null, cockpitCloudEffects: null },
|
||||
data: { dataManager, presentation: { flushVisible: () => flushed++ } },
|
||||
loadingScreen: { kind: 'loading' },
|
||||
@@ -62,11 +85,18 @@ test('without a viewer the tools run the governor on the engine, show the notice
|
||||
signal: new AbortController().signal,
|
||||
defer: (dispose) => cleanups.push(dispose),
|
||||
});
|
||||
assert.deepEqual(tools, {
|
||||
sceneDirector: null,
|
||||
annotations: null,
|
||||
voiceCommands: null,
|
||||
assert.equal(tools.sceneDirector, null);
|
||||
assert.equal(tools.annotations, null);
|
||||
assert.ok(tools.voiceCommands, 'voice runs on the engine');
|
||||
assert.equal(window.__gevVoiceCommands, tools.voiceCommands);
|
||||
// The W1 tools drive the engine; the viewer-only ones refuse.
|
||||
const view = await voice.runner('get_current_view_state');
|
||||
assert.equal(view.ok, true);
|
||||
const scene = await voice.runner('control_scene', {
|
||||
action: 'list',
|
||||
});
|
||||
assert.equal(scene.ok, false);
|
||||
assert.equal(scene.code, 'UNAVAILABLE_ON_ENGINE');
|
||||
assert.equal(chrome.length, 1);
|
||||
assert.equal(chrome[0].initializeWelcome, null, 'no first-run welcome');
|
||||
assert.equal(chrome[0].styleManager, styleManager);
|
||||
@@ -103,9 +133,12 @@ test('without a viewer the tools run the governor on the engine, show the notice
|
||||
assert.equal(debug.engine, engine);
|
||||
assert.equal(debug.sceneDirector, null);
|
||||
assert.equal(debug.dataManager, dataManager);
|
||||
assert.equal(debug.voiceCommands, tools.voiceCommands);
|
||||
listeners.get('visibilitychange')();
|
||||
assert.equal(flushed, 1);
|
||||
while (cleanups.length) await cleanups.pop()();
|
||||
assert.equal(window.__godsEyeView, undefined);
|
||||
assert.equal(window.__gevVoiceCommands, undefined);
|
||||
assert.equal(uiRemoved, 1, 'the voice control is removed');
|
||||
assert.equal(listeners.size, 0);
|
||||
});
|
||||
Reference in new issue
Block a user