mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(voice): keep the voice preferences in reach from chat Settings
With Voice Preview off the voice panel never opens on its own, and chat Settings hid the voice preferences, so the Preview toggle, inactivity timeout and model tier could not be reached by pointer. Chat Settings now show them below the chat options, still in chat's frame and without the voice tabs.
This commit is contained in:
1 parent
4f4af9c4e0
commit
829db19f60
4 files changed
+71
-8
No files matched your search
@@ -764,8 +764,9 @@ again once the trays close and the pointer leaves the wings. Panel height animat
|
||||
other or the voice view changes. Minimized voice shows the last sentence from
|
||||
each side, and the empty Voice Preview suggests things to say until voice
|
||||
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
|
||||
provider, the quick-commands option and then the voice preferences (Voice
|
||||
Preview, inactivity timeout and model tier), so they stay in reach while
|
||||
Preview is off, 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. Asking the assistant from chat to open
|
||||
Voice Settings or Voice Preview puts chat away, minimized or not, and shows the
|
||||
|
||||
@@ -1242,14 +1242,20 @@ body[data-assistant-docked='true'] #right-context-rail {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Settings opened from chat: same frame as chat, chat options only. */
|
||||
/* Settings opened from chat: same frame as chat, chat options first, then
|
||||
the voice preferences, the only pointer route to them while Preview is off. */
|
||||
#command-dock
|
||||
#gev-voice-control
|
||||
#gev-voice-panel[data-context='chat']
|
||||
:is(.gev-voice-panel-tabs, .gev-panel-layout, #gev-settings-voice),
|
||||
:is(.gev-voice-panel-tabs, .gev-panel-layout),
|
||||
#gev-voice-panel:not([data-context='chat']) #gev-settings-chat {
|
||||
display: none;
|
||||
}
|
||||
#gev-voice-panel[data-context='chat'] #gev-settings-voice {
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--glass-border);
|
||||
}
|
||||
#gev-voice-control
|
||||
#gev-voice-panel[data-context='chat'][data-placement='compact']:not(
|
||||
[data-minimized]
|
||||
|
||||
@@ -125,6 +125,10 @@ const frameStyle = (panel) => ({
|
||||
<UiSelect id="gev-voice-provider" class="gev-field" :aria-label="t('AI Provider')" :title="view.provider.title" :model-value="view.provider.value" @update:model-value="emit('provider-change', $event)"><option v-for="option in view.provider.options" :key="option.value" :value="option.value">{{ option.label }}</option></UiSelect>
|
||||
</label>
|
||||
<p class="gev-voice-settings-note">{{ t('The selected AI provider is shared by voice and chat.') }}</p>
|
||||
<div id="gev-settings-chat">
|
||||
<label class="gev-voice-preference gev-voice-preference-toggle"><input id="gev-chat-quick" type="checkbox" v-checked="view.chat.quickChecked" @change="emit('chat-quick-change', $event.target.checked)" /><span>{{ t('Show quick commands') }}</span></label>
|
||||
<p>{{ t('Chat replies are silent. No microphone access is needed.') }}</p>
|
||||
</div>
|
||||
<div id="gev-settings-voice">
|
||||
<label class="gev-voice-preference gev-voice-preference-toggle"><input id="gev-voice-preview-enabled" type="checkbox" aria-describedby="gev-voice-preview-note" v-checked="view.preferences.previewChecked" @change="emit('preview-change', $event.target.checked)" /><span>{{ t('Show Voice Preview') }}</span></label>
|
||||
<p id="gev-voice-preview-note" class="gev-voice-settings-note">{{ view.preferences.previewNote }}</p>
|
||||
@@ -152,10 +156,6 @@ const frameStyle = (panel) => ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gev-settings-chat">
|
||||
<label class="gev-voice-preference gev-voice-preference-toggle"><input id="gev-chat-quick" type="checkbox" v-checked="view.chat.quickChecked" @change="emit('chat-quick-change', $event.target.checked)" /><span>{{ t('Show quick commands') }}</span></label>
|
||||
<p>{{ t('Chat replies are silent. No microphone access is needed.') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '../../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createPinia } from 'pinia';
|
||||
import { createVoiceControl } from '../control.js';
|
||||
import { useVoiceStore } from '../../ui/stores/voice.js';
|
||||
@@ -1007,6 +1008,61 @@ test('chat Settings keep chat context until the panel closes', async (t) => {
|
||||
assert.equal(f.ui.state.chat.backVisible, false);
|
||||
});
|
||||
|
||||
// Shown once neither the element nor an ancestor is hidden by the page's
|
||||
// chat stylesheet or a hidden attribute.
|
||||
function shown(selector) {
|
||||
for (let node = $(selector); node?.nodeType === 1; node = node.parentElement)
|
||||
if (node.hidden || globalThis.getComputedStyle(node).display === 'none')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
test('with Preview off, chat Settings reach the voice preferences', async (t) => {
|
||||
const style = globalThis.document.createElement('style');
|
||||
style.textContent = readFileSync(
|
||||
new URL('../../ui/styles/ai-chat.css', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
globalThis.document.head.append(style);
|
||||
t.after(() => style.remove());
|
||||
const f = mounted(t, { view: true });
|
||||
// The control renders into the command dock, as the page does.
|
||||
globalThis.document.body.firstElementChild.id = 'command-dock';
|
||||
f.ui.emit('preview-change', false);
|
||||
f.commands.voicePanel.close();
|
||||
await f.settle();
|
||||
$('#gev-chat-button').click();
|
||||
await f.settle();
|
||||
$('#gev-voice-settings-button').click();
|
||||
await f.settle();
|
||||
assert.equal(f.ui.state.voicePanel.context, 'chat');
|
||||
for (const selector of [
|
||||
'#gev-settings-chat',
|
||||
'#gev-settings-voice',
|
||||
'#gev-voice-preview-enabled',
|
||||
'#gev-voice-timeout-select',
|
||||
])
|
||||
assert.equal(shown(selector), true, selector);
|
||||
// Chat's frame keeps the voice tabs and dock layout buttons away.
|
||||
assert.equal(shown('.gev-voice-panel-tabs'), false);
|
||||
const preview = $('#gev-voice-preview-enabled');
|
||||
preview.checked = true;
|
||||
preview.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
assert.equal(f.commands.getVoicePreferences().previewEnabled, true);
|
||||
const timeout = $('#gev-voice-timeout-select');
|
||||
timeout.value = '10';
|
||||
timeout.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
assert.equal(f.commands.getVoicePreferences().minutes, 10);
|
||||
f.ui.update('tier', { hidden: false });
|
||||
await f.settle();
|
||||
assert.equal(shown('#gev-voice-tier'), true);
|
||||
let tiers = 0;
|
||||
t.after(f.ui.on('tier', () => tiers++));
|
||||
$('#gev-voice-tier').click();
|
||||
assert.equal(tiers, 1);
|
||||
assert.equal(f.ui.state.voicePanel.context, 'chat', 'still chat Settings');
|
||||
});
|
||||
|
||||
test('chat Settings keep only the quick commands preference', async (t) => {
|
||||
const f = mounted(t, { view: true });
|
||||
await f.settle();
|
||||
|
||||
Reference in new issue
Block a user