mirror of
https://github.com/open-webui/open-webui
synced 2024-11-07 17:19:53 +00:00
refac: voice call tap to interrupt
This commit is contained in:
parent
e183b0e5ff
commit
ad2ffc33d8
@ -31,6 +31,7 @@
|
|||||||
let loading = false;
|
let loading = false;
|
||||||
let confirmed = false;
|
let confirmed = false;
|
||||||
let interrupted = false;
|
let interrupted = false;
|
||||||
|
let assistantSpeaking = false;
|
||||||
|
|
||||||
let emoji = null;
|
let emoji = null;
|
||||||
|
|
||||||
@ -268,6 +269,14 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (assistantSpeaking) {
|
||||||
|
analyser.maxDecibels = 0;
|
||||||
|
analyser.minDecibels = -10;
|
||||||
|
} else {
|
||||||
|
analyser.minDecibels = MIN_DECIBELS;
|
||||||
|
analyser.maxDecibels = -30;
|
||||||
|
}
|
||||||
|
|
||||||
analyser.getByteTimeDomainData(timeDomainData);
|
analyser.getByteTimeDomainData(timeDomainData);
|
||||||
analyser.getByteFrequencyData(domainData);
|
analyser.getByteFrequencyData(domainData);
|
||||||
|
|
||||||
@ -379,6 +388,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const stopAllAudio = async () => {
|
const stopAllAudio = async () => {
|
||||||
|
assistantSpeaking = false;
|
||||||
interrupted = true;
|
interrupted = true;
|
||||||
|
|
||||||
if (chatStreaming) {
|
if (chatStreaming) {
|
||||||
@ -485,6 +495,7 @@
|
|||||||
}
|
}
|
||||||
} else if (finishedMessages[id] && messages[id] && messages[id].length === 0) {
|
} else if (finishedMessages[id] && messages[id] && messages[id].length === 0) {
|
||||||
// If the message is finished and there are no more messages to process, break the loop
|
// If the message is finished and there are no more messages to process, break the loop
|
||||||
|
assistantSpeaking = false;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// No messages to process, sleep for a bit
|
// No messages to process, sleep for a bit
|
||||||
@ -511,6 +522,7 @@
|
|||||||
}
|
}
|
||||||
audioAbortController = new AbortController();
|
audioAbortController = new AbortController();
|
||||||
|
|
||||||
|
assistantSpeaking = true;
|
||||||
// Start monitoring and playing audio for the message ID
|
// Start monitoring and playing audio for the message ID
|
||||||
monitorAndPlayAudio(id, audioAbortController.signal);
|
monitorAndPlayAudio(id, audioAbortController.signal);
|
||||||
}
|
}
|
||||||
@ -545,9 +557,9 @@
|
|||||||
const chatFinishHandler = async (e) => {
|
const chatFinishHandler = async (e) => {
|
||||||
const { id, content } = e.detail;
|
const { id, content } = e.detail;
|
||||||
// "content" here is the entire message from the assistant
|
// "content" here is the entire message from the assistant
|
||||||
|
finishedMessages[id] = true;
|
||||||
|
|
||||||
chatStreaming = false;
|
chatStreaming = false;
|
||||||
finishedMessages[id] = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
eventTarget.addEventListener('chat:start', chatStartHandler);
|
eventTarget.addEventListener('chat:start', chatStartHandler);
|
||||||
@ -577,7 +589,15 @@
|
|||||||
>
|
>
|
||||||
<div class="max-w-lg w-full h-screen max-h-[100dvh] flex flex-col justify-between p-3 md:p-6">
|
<div class="max-w-lg w-full h-screen max-h-[100dvh] flex flex-col justify-between p-3 md:p-6">
|
||||||
{#if camera}
|
{#if camera}
|
||||||
<div class="flex justify-center items-center w-full h-20 min-h-20">
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex justify-center items-center w-full h-20 min-h-20"
|
||||||
|
on:click={() => {
|
||||||
|
if (assistantSpeaking) {
|
||||||
|
stopAllAudio();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
{#if emoji}
|
{#if emoji}
|
||||||
<div
|
<div
|
||||||
class=" transition-all rounded-full"
|
class=" transition-all rounded-full"
|
||||||
@ -640,72 +660,81 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- navbar -->
|
<!-- navbar -->
|
||||||
</div>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex justify-center items-center flex-1 h-full w-full max-h-full">
|
<div class="flex justify-center items-center flex-1 h-full w-full max-h-full">
|
||||||
{#if !camera}
|
{#if !camera}
|
||||||
{#if emoji}
|
<button
|
||||||
<div
|
type="button"
|
||||||
class=" transition-all rounded-full"
|
on:click={() => {
|
||||||
style="font-size:{rmsLevel * 100 > 4
|
if (assistantSpeaking) {
|
||||||
? '13'
|
stopAllAudio();
|
||||||
: rmsLevel * 100 > 2
|
}
|
||||||
? '12'
|
}}
|
||||||
: rmsLevel * 100 > 1
|
>
|
||||||
? '11.5'
|
{#if emoji}
|
||||||
: '11'}rem;width:100%;text-align:center;"
|
<div
|
||||||
>
|
class=" transition-all rounded-full"
|
||||||
{emoji}
|
style="font-size:{rmsLevel * 100 > 4
|
||||||
</div>
|
? '13'
|
||||||
{:else if loading}
|
: rmsLevel * 100 > 2
|
||||||
<svg
|
? '12'
|
||||||
class="size-44 text-gray-900 dark:text-gray-400"
|
: rmsLevel * 100 > 1
|
||||||
viewBox="0 0 24 24"
|
? '11.5'
|
||||||
fill="currentColor"
|
: '11'}rem;width:100%;text-align:center;"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
><style>
|
{emoji}
|
||||||
.spinner_qM83 {
|
</div>
|
||||||
animation: spinner_8HQG 1.05s infinite;
|
{:else if loading}
|
||||||
}
|
<svg
|
||||||
.spinner_oXPr {
|
class="size-44 text-gray-900 dark:text-gray-400"
|
||||||
animation-delay: 0.1s;
|
viewBox="0 0 24 24"
|
||||||
}
|
fill="currentColor"
|
||||||
.spinner_ZTLf {
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
animation-delay: 0.2s;
|
><style>
|
||||||
}
|
.spinner_qM83 {
|
||||||
@keyframes spinner_8HQG {
|
animation: spinner_8HQG 1.05s infinite;
|
||||||
0%,
|
|
||||||
57.14% {
|
|
||||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
|
||||||
transform: translate(0);
|
|
||||||
}
|
}
|
||||||
28.57% {
|
.spinner_oXPr {
|
||||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
animation-delay: 0.1s;
|
||||||
transform: translateY(-6px);
|
|
||||||
}
|
}
|
||||||
100% {
|
.spinner_ZTLf {
|
||||||
transform: translate(0);
|
animation-delay: 0.2s;
|
||||||
}
|
}
|
||||||
}
|
@keyframes spinner_8HQG {
|
||||||
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
|
0%,
|
||||||
class="spinner_qM83 spinner_oXPr"
|
57.14% {
|
||||||
cx="12"
|
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||||
cy="12"
|
transform: translate(0);
|
||||||
r="3"
|
}
|
||||||
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
|
28.57% {
|
||||||
>
|
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||||
{:else}
|
transform: translateY(-6px);
|
||||||
<div
|
}
|
||||||
class=" {rmsLevel * 100 > 4
|
100% {
|
||||||
? ' size-52'
|
transform: translate(0);
|
||||||
: rmsLevel * 100 > 2
|
}
|
||||||
? 'size-48'
|
}
|
||||||
: rmsLevel * 100 > 1
|
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
|
||||||
? 'size-[11.5rem]'
|
class="spinner_qM83 spinner_oXPr"
|
||||||
: 'size-44'} transition-all bg-black dark:bg-white rounded-full"
|
cx="12"
|
||||||
/>
|
cy="12"
|
||||||
{/if}
|
r="3"
|
||||||
|
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class=" {rmsLevel * 100 > 4
|
||||||
|
? ' size-52'
|
||||||
|
: rmsLevel * 100 > 2
|
||||||
|
? 'size-48'
|
||||||
|
: rmsLevel * 100 > 1
|
||||||
|
? 'size-[11.5rem]'
|
||||||
|
: 'size-44'} transition-all bg-black dark:bg-white rounded-full"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full"
|
class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full"
|
||||||
@ -805,10 +834,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button type="button">
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
if (assistantSpeaking) {
|
||||||
|
stopAllAudio();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class=" line-clamp-1 text-sm font-medium">
|
<div class=" line-clamp-1 text-sm font-medium">
|
||||||
{#if loading}
|
{#if loading}
|
||||||
{$i18n.t('Thinking...')}
|
{$i18n.t('Thinking...')}
|
||||||
|
{:else if assistantSpeaking}
|
||||||
|
{$i18n.t('Tap to interrupt')}
|
||||||
{:else}
|
{:else}
|
||||||
{$i18n.t('Listening...')}
|
{$i18n.t('Listening...')}
|
||||||
{/if}
|
{/if}
|
||||||
|
Loading…
Reference in New Issue
Block a user