mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(voice): show the voice panel that typed chat asks for
set_panel_open for voice-settings or voice-preview reported success while chat's open state kept the voice panel hidden, minimized chat included. A successful explicit show now closes chat, which keeps the typed session, its running turn and its messages; reopening chat restores them.
This commit is contained in:
1 parent
b6967a3dd9
commit
4f4af9c4e0
3 files changed
+72
-4
No files matched your search
@@ -767,7 +767,10 @@ starts. Settings opened from chat keep chat's frame and show the shared AI
|
||||
provider and the quick-commands option, with Back to chat; voice Settings show
|
||||
Back to voice. Escape steps back from either Settings view, closes Voice
|
||||
Preview, and does nothing in chat. Closing chat ends a typed session, and the
|
||||
voice panel's close turns voice off.
|
||||
voice panel's close turns voice off. Asking the assistant from chat to open
|
||||
Voice Settings or Voice Preview puts chat away, minimized or not, and shows the
|
||||
voice panel; the typed conversation and its running reply carry on, and the
|
||||
chat button brings them back.
|
||||
|
||||
Contacts nearest-aircraft follow-ups retain the snapshot's distance basis even
|
||||
after the camera moves. An explicit new scope replaces that basis. Provider
|
||||
|
||||
@@ -511,7 +511,7 @@ test('the voice inactivity timeout never ends a typed chat session', async (t) =
|
||||
|
||||
async function directActions(
|
||||
t,
|
||||
{ storage = null, delayExit = false, exposePanel = true } = {},
|
||||
{ storage = null, delayExit = false, exposePanel = true, typed = false } = {},
|
||||
) {
|
||||
let f, releaseExit;
|
||||
const state = { active: true, exits: 0, visual: [] };
|
||||
@@ -544,7 +544,12 @@ async function directActions(
|
||||
exposePanel ? f?.commands.captureVoicePanelOperation() : undefined,
|
||||
});
|
||||
f = mounted(t, { native: true, runner, storage });
|
||||
await f.commands.session.start();
|
||||
// A typed chat session starts from chat's first send instead.
|
||||
if (typed) {
|
||||
const adapter = f.instances[0];
|
||||
adapter.getStartOptions = () => ({ textOnly: true });
|
||||
adapter.sendText = () => true;
|
||||
} else await f.commands.session.start();
|
||||
return {
|
||||
...f,
|
||||
runner,
|
||||
@@ -618,6 +623,53 @@ test('named voice settings exits Cockpit directly; Preview stays disabled and vi
|
||||
);
|
||||
});
|
||||
|
||||
for (const minimized of [false, true])
|
||||
test(`an explicit voice panel request from ${minimized ? 'minimized ' : ''}typed chat shows the panel and keeps the chat`, async (t) => {
|
||||
const f = await directActions(t, { typed: true });
|
||||
const adapter = f.instances[0];
|
||||
f.ui.emit('chat-button');
|
||||
f.ui.emit('chat-input', 'Fly to Tokyo');
|
||||
f.ui.emit('chat-submit');
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
assert.equal(f.ui.state.chat.busy, true, 'the typed turn is running');
|
||||
if (minimized) f.ui.emit('chat-minimize');
|
||||
assert.equal(f.ui.state.root.chatOpen, true);
|
||||
for (const [panelId, view] of [
|
||||
['voice-settings', 'settings'],
|
||||
['voice-preview', 'preview'],
|
||||
]) {
|
||||
const result = await f.run('set_panel_open', { panelId, open: true });
|
||||
assert.equal(result.ok, true);
|
||||
// `[data-chat-open='true'] .gev-voice-panel` is display:none.
|
||||
assert.equal(f.ui.state.root.chatOpen, false, panelId);
|
||||
assert.equal(f.ui.state.chat.open, false);
|
||||
assert.equal(f.ui.state.voicePanel.hidden, false);
|
||||
assert.equal(f.ui.state.voicePanel.view, view);
|
||||
}
|
||||
assert.equal(adapter.stops, 0, 'the typed session continues');
|
||||
assert.equal(f.commands.session.isActive(), true);
|
||||
assert.equal(f.ui.state.chat.busy, true, 'the turn is not cancelled');
|
||||
adapter.hooks.emit({
|
||||
type: 'transcript',
|
||||
role: 'assistant',
|
||||
text: 'Flying to Tokyo.',
|
||||
final: true,
|
||||
itemId: 'reply',
|
||||
});
|
||||
adapter.hooks.emit({ type: 'completion', status: 'completed' });
|
||||
assert.equal(f.ui.state.chat.busy, false);
|
||||
f.ui.emit('chat-button');
|
||||
assert.equal(f.ui.state.root.chatOpen, true);
|
||||
assert.equal(f.ui.state.voicePanel.hidden, true);
|
||||
assert.deepEqual(
|
||||
f.ui.chatMessages.map(({ role }) => role),
|
||||
['user', 'assistant'],
|
||||
'reopened chat shows the whole conversation',
|
||||
);
|
||||
assert.equal(f.ui.chatMessages[0].text, 'Fly to Tokyo');
|
||||
assert.equal(adapter.stops, 0);
|
||||
});
|
||||
|
||||
test('canonical exit reveals requested voice UI; newer manual choice or Stop defeats pending reveal', async (t) => {
|
||||
const f = await directActions(t, { delayExit: true });
|
||||
const pending = f.run('control_cockpit', {
|
||||
|
||||
@@ -157,7 +157,20 @@ export function createVoiceCommands({
|
||||
controls.voiceCard = card;
|
||||
if (panelMount) {
|
||||
controls.voicePanel = panelMount.panel;
|
||||
controls.captureVoicePanelOperation = panelMount.captureOperation;
|
||||
// An explicit, successful show hands the frame from chat to the voice
|
||||
// panel, which chat's open state hides. Closing chat keeps its typed
|
||||
// session, running turn and messages; reopening chat restores them.
|
||||
controls.captureVoicePanelOperation = () => {
|
||||
const operation = panelMount.captureOperation();
|
||||
return {
|
||||
...operation,
|
||||
show(view, open = true) {
|
||||
const result = operation.show(view, open);
|
||||
if (open && result.ok) chat?.close();
|
||||
return result;
|
||||
},
|
||||
};
|
||||
};
|
||||
controls.getVoicePanelState = panelMount.snapshot;
|
||||
controls.getVoicePreferences = panelHost.snapshot;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user