mirror of
https://github.com/open-webui/open-webui
synced 2025-05-23 14:24:22 +00:00
refac: call overlay styling
This commit is contained in:
parent
4519ddd0e9
commit
48503573c1
@ -1634,6 +1634,25 @@
|
|||||||
|
|
||||||
<audio id="audioElement" src="" style="display: none;" />
|
<audio id="audioElement" src="" style="display: none;" />
|
||||||
|
|
||||||
|
<ChatControls
|
||||||
|
models={selectedModelIds.reduce((a, e, i, arr) => {
|
||||||
|
const model = $models.find((m) => m.id === e);
|
||||||
|
if (model) {
|
||||||
|
return [...a, model];
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}, [])}
|
||||||
|
bind:show={showControls}
|
||||||
|
bind:chatFiles
|
||||||
|
bind:params
|
||||||
|
bind:files
|
||||||
|
{submitPrompt}
|
||||||
|
{stopResponse}
|
||||||
|
modelId={selectedModelIds?.at(0) ?? null}
|
||||||
|
chatId={$chatId}
|
||||||
|
{eventTarget}
|
||||||
|
/>
|
||||||
|
|
||||||
<EventConfirmDialog
|
<EventConfirmDialog
|
||||||
bind:show={showEventConfirmation}
|
bind:show={showEventConfirmation}
|
||||||
title={eventConfirmationTitle}
|
title={eventConfirmationTitle}
|
||||||
@ -1653,17 +1672,6 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if $showCallOverlay}
|
|
||||||
<CallOverlay
|
|
||||||
{submitPrompt}
|
|
||||||
{stopResponse}
|
|
||||||
bind:files
|
|
||||||
modelId={selectedModelIds?.at(0) ?? null}
|
|
||||||
chatId={$chatId}
|
|
||||||
{eventTarget}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if !chatIdProp || (loaded && chatIdProp)}
|
{#if !chatIdProp || (loaded && chatIdProp)}
|
||||||
<div
|
<div
|
||||||
class="h-screen max-h-[100dvh] {$showSidebar
|
class="h-screen max-h-[100dvh] {$showSidebar
|
||||||
@ -1774,21 +1782,11 @@
|
|||||||
{messages}
|
{messages}
|
||||||
{submitPrompt}
|
{submitPrompt}
|
||||||
{stopResponse}
|
{stopResponse}
|
||||||
|
on:call={() => {
|
||||||
|
showControls = true;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ChatControls
|
|
||||||
models={selectedModelIds.reduce((a, e, i, arr) => {
|
|
||||||
const model = $models.find((m) => m.id === e);
|
|
||||||
if (model) {
|
|
||||||
return [...a, model];
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}, [])}
|
|
||||||
bind:show={showControls}
|
|
||||||
bind:chatFiles
|
|
||||||
bind:params
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
import Modal from '../common/Modal.svelte';
|
import Modal from '../common/Modal.svelte';
|
||||||
import Controls from './Controls/Controls.svelte';
|
import Controls from './Controls/Controls.svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { mobile, showCallOverlay } from '$lib/stores';
|
||||||
|
import CallOverlay from './MessageInput/CallOverlay.svelte';
|
||||||
|
|
||||||
export let show = false;
|
export let show = false;
|
||||||
|
|
||||||
@ -12,6 +14,12 @@
|
|||||||
export let chatFiles = [];
|
export let chatFiles = [];
|
||||||
export let params = {};
|
export let params = {};
|
||||||
|
|
||||||
|
export let eventTarget: EventTarget;
|
||||||
|
export let submitPrompt: Function;
|
||||||
|
export let stopResponse: Function;
|
||||||
|
export let files;
|
||||||
|
export let modelId;
|
||||||
|
|
||||||
let largeScreen = false;
|
let largeScreen = false;
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// listen to resize 1024px
|
// listen to resize 1024px
|
||||||
@ -42,18 +50,37 @@
|
|||||||
<div
|
<div
|
||||||
class="w-full h-full px-5 py-4 bg-white dark:shadow-lg dark:bg-gray-850 border border-gray-50 dark:border-gray-800 rounded-xl z-50 pointer-events-auto overflow-y-auto scrollbar-hidden"
|
class="w-full h-full px-5 py-4 bg-white dark:shadow-lg dark:bg-gray-850 border border-gray-50 dark:border-gray-800 rounded-xl z-50 pointer-events-auto overflow-y-auto scrollbar-hidden"
|
||||||
>
|
>
|
||||||
<Controls
|
{#if $showCallOverlay}
|
||||||
on:close={() => {
|
<CallOverlay
|
||||||
show = false;
|
bind:files
|
||||||
}}
|
{submitPrompt}
|
||||||
{models}
|
{stopResponse}
|
||||||
bind:chatFiles
|
{modelId}
|
||||||
bind:params
|
{chatId}
|
||||||
/>
|
{eventTarget}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<Controls
|
||||||
|
on:close={() => {
|
||||||
|
show = false;
|
||||||
|
}}
|
||||||
|
{models}
|
||||||
|
bind:chatFiles
|
||||||
|
bind:params
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else if $showCallOverlay}
|
||||||
|
<div class=" absolute w-full h-screen max-h-[100dvh] flex z-[999] overflow-hidden">
|
||||||
|
<div
|
||||||
|
class="absolute w-full h-screen max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center"
|
||||||
|
>
|
||||||
|
<CallOverlay bind:files {submitPrompt} {stopResponse} {modelId} {chatId} {eventTarget} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<Modal bind:show>
|
<Modal bind:show>
|
||||||
<div class=" px-6 py-4 h-full">
|
<div class=" px-6 py-4 h-full">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import { createEventDispatcher, getContext } from 'svelte';
|
import { createEventDispatcher, getContext } from 'svelte';
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
@ -10,9 +10,7 @@
|
|||||||
import Collapsible from '$lib/components/common/Collapsible.svelte';
|
import Collapsible from '$lib/components/common/Collapsible.svelte';
|
||||||
|
|
||||||
import { user } from '$lib/stores';
|
import { user } from '$lib/stores';
|
||||||
|
|
||||||
export let models = [];
|
export let models = [];
|
||||||
|
|
||||||
export let chatFiles = [];
|
export let chatFiles = [];
|
||||||
export let params = {};
|
export let params = {};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { onMount, tick, getContext } from 'svelte';
|
import { onMount, tick, getContext, createEventDispatcher } from 'svelte';
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type Model,
|
type Model,
|
||||||
@ -762,10 +763,14 @@
|
|||||||
}
|
}
|
||||||
// check if user has access to getUserMedia
|
// check if user has access to getUserMedia
|
||||||
try {
|
try {
|
||||||
await navigator.mediaDevices.getUserMedia({ audio: true });
|
let stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||||
// If the user grants the permission, proceed to show the call overlay
|
// If the user grants the permission, proceed to show the call overlay
|
||||||
|
|
||||||
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
|
stream = null;
|
||||||
|
|
||||||
showCallOverlay.set(true);
|
showCallOverlay.set(true);
|
||||||
|
dispatch('call');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// If the user denies the permission or an error occurs, show an error message
|
// If the user denies the permission or an error occurs, show an error message
|
||||||
toast.error($i18n.t('Permission denied when accessing media devices'));
|
toast.error($i18n.t('Permission denied when accessing media devices'));
|
||||||
|
@ -19,12 +19,9 @@
|
|||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
export let eventTarget: EventTarget;
|
export let eventTarget: EventTarget;
|
||||||
|
|
||||||
export let submitPrompt: Function;
|
export let submitPrompt: Function;
|
||||||
export let stopResponse: Function;
|
export let stopResponse: Function;
|
||||||
|
|
||||||
export let files;
|
export let files;
|
||||||
|
|
||||||
export let chatId;
|
export let chatId;
|
||||||
export let modelId;
|
export let modelId;
|
||||||
|
|
||||||
@ -631,299 +628,216 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $showCallOverlay}
|
{#if $showCallOverlay}
|
||||||
<div class=" absolute w-full h-screen max-h-[100dvh] flex z-[999] overflow-hidden">
|
<div class="max-w-lg w-full h-full max-h-[100dvh] flex flex-col justify-between p-3 md:p-6">
|
||||||
<div
|
{#if camera}
|
||||||
class="absolute w-full h-screen max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center"
|
<button
|
||||||
>
|
type="button"
|
||||||
<div class="max-w-lg w-full h-screen max-h-[100dvh] flex flex-col justify-between p-3 md:p-6">
|
class="flex justify-center items-center w-full h-20 min-h-20"
|
||||||
{#if camera}
|
on:click={() => {
|
||||||
<button
|
if (assistantSpeaking) {
|
||||||
type="button"
|
stopAllAudio();
|
||||||
class="flex justify-center items-center w-full h-20 min-h-20"
|
}
|
||||||
on:click={() => {
|
}}
|
||||||
if (assistantSpeaking) {
|
>
|
||||||
stopAllAudio();
|
{#if emoji}
|
||||||
}
|
<div
|
||||||
}}
|
class=" transition-all rounded-full"
|
||||||
|
style="font-size:{rmsLevel * 100 > 4
|
||||||
|
? '4.5'
|
||||||
|
: rmsLevel * 100 > 2
|
||||||
|
? '4.25'
|
||||||
|
: rmsLevel * 100 > 1
|
||||||
|
? '3.75'
|
||||||
|
: '3.5'}rem;width: 100%; text-align:center;"
|
||||||
>
|
>
|
||||||
{#if emoji}
|
{emoji}
|
||||||
<div
|
</div>
|
||||||
class=" transition-all rounded-full"
|
{:else if loading || assistantSpeaking}
|
||||||
style="font-size:{rmsLevel * 100 > 4
|
<svg
|
||||||
? '4.5'
|
class="size-12 text-gray-900 dark:text-gray-400"
|
||||||
: rmsLevel * 100 > 2
|
viewBox="0 0 24 24"
|
||||||
? '4.25'
|
fill="currentColor"
|
||||||
: rmsLevel * 100 > 1
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
? '3.75'
|
><style>
|
||||||
: '3.5'}rem;width: 100%; text-align:center;"
|
.spinner_qM83 {
|
||||||
>
|
animation: spinner_8HQG 1.05s infinite;
|
||||||
{emoji}
|
}
|
||||||
</div>
|
.spinner_oXPr {
|
||||||
{:else if loading || assistantSpeaking}
|
animation-delay: 0.1s;
|
||||||
<svg
|
}
|
||||||
class="size-12 text-gray-900 dark:text-gray-400"
|
.spinner_ZTLf {
|
||||||
viewBox="0 0 24 24"
|
animation-delay: 0.2s;
|
||||||
fill="currentColor"
|
}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
@keyframes spinner_8HQG {
|
||||||
><style>
|
0%,
|
||||||
.spinner_qM83 {
|
57.14% {
|
||||||
animation: spinner_8HQG 1.05s infinite;
|
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||||
}
|
transform: translate(0);
|
||||||
.spinner_oXPr {
|
|
||||||
animation-delay: 0.1s;
|
|
||||||
}
|
|
||||||
.spinner_ZTLf {
|
|
||||||
animation-delay: 0.2s;
|
|
||||||
}
|
|
||||||
@keyframes spinner_8HQG {
|
|
||||||
0%,
|
|
||||||
57.14% {
|
|
||||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
|
||||||
transform: translate(0);
|
|
||||||
}
|
|
||||||
28.57% {
|
|
||||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
|
||||||
transform: translateY(-6px);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
|
|
||||||
class="spinner_qM83 spinner_oXPr"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="3"
|
|
||||||
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
|
|
||||||
>
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class=" {rmsLevel * 100 > 4
|
|
||||||
? ' size-[4.5rem]'
|
|
||||||
: rmsLevel * 100 > 2
|
|
||||||
? ' size-16'
|
|
||||||
: rmsLevel * 100 > 1
|
|
||||||
? 'size-14'
|
|
||||||
: 'size-12'} transition-all rounded-full {(model?.info?.meta
|
|
||||||
?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
|
|
||||||
? ' bg-cover bg-center bg-no-repeat'
|
|
||||||
: 'bg-black dark:bg-white'} bg-black dark:bg-white"
|
|
||||||
style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
|
|
||||||
'/static/favicon.png'
|
|
||||||
? `background-image: url('${model?.info?.meta?.profile_image_url}');`
|
|
||||||
: ''}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<!-- navbar -->
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="flex justify-center items-center flex-1 h-full w-full max-h-full">
|
|
||||||
{#if !camera}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
on:click={() => {
|
|
||||||
if (assistantSpeaking) {
|
|
||||||
stopAllAudio();
|
|
||||||
}
|
}
|
||||||
}}
|
28.57% {
|
||||||
|
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||||
|
transform: translateY(-6px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
|
||||||
|
class="spinner_qM83 spinner_oXPr"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="3"
|
||||||
|
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<div
|
||||||
|
class=" {rmsLevel * 100 > 4
|
||||||
|
? ' size-[4.5rem]'
|
||||||
|
: rmsLevel * 100 > 2
|
||||||
|
? ' size-16'
|
||||||
|
: rmsLevel * 100 > 1
|
||||||
|
? 'size-14'
|
||||||
|
: 'size-12'} transition-all rounded-full {(model?.info?.meta
|
||||||
|
?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
|
||||||
|
? ' bg-cover bg-center bg-no-repeat'
|
||||||
|
: 'bg-black dark:bg-white'} bg-black dark:bg-white"
|
||||||
|
style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
|
||||||
|
'/static/favicon.png'
|
||||||
|
? `background-image: url('${model?.info?.meta?.profile_image_url}');`
|
||||||
|
: ''}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
<!-- navbar -->
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="flex justify-center items-center flex-1 h-full w-full max-h-full">
|
||||||
|
{#if !camera}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
if (assistantSpeaking) {
|
||||||
|
stopAllAudio();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if emoji}
|
||||||
|
<div
|
||||||
|
class=" transition-all rounded-full"
|
||||||
|
style="font-size:{rmsLevel * 100 > 4
|
||||||
|
? '13'
|
||||||
|
: rmsLevel * 100 > 2
|
||||||
|
? '12'
|
||||||
|
: rmsLevel * 100 > 1
|
||||||
|
? '11.5'
|
||||||
|
: '11'}rem;width:100%;text-align:center;"
|
||||||
|
>
|
||||||
|
{emoji}
|
||||||
|
</div>
|
||||||
|
{:else if loading || assistantSpeaking}
|
||||||
|
<svg
|
||||||
|
class="size-44 text-gray-900 dark:text-gray-400"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
><style>
|
||||||
|
.spinner_qM83 {
|
||||||
|
animation: spinner_8HQG 1.05s infinite;
|
||||||
|
}
|
||||||
|
.spinner_oXPr {
|
||||||
|
animation-delay: 0.1s;
|
||||||
|
}
|
||||||
|
.spinner_ZTLf {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
@keyframes spinner_8HQG {
|
||||||
|
0%,
|
||||||
|
57.14% {
|
||||||
|
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
||||||
|
transform: translate(0);
|
||||||
|
}
|
||||||
|
28.57% {
|
||||||
|
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
||||||
|
transform: translateY(-6px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
|
||||||
|
class="spinner_qM83 spinner_oXPr"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="3"
|
||||||
|
/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
|
||||||
>
|
>
|
||||||
{#if emoji}
|
|
||||||
<div
|
|
||||||
class=" transition-all rounded-full"
|
|
||||||
style="font-size:{rmsLevel * 100 > 4
|
|
||||||
? '13'
|
|
||||||
: rmsLevel * 100 > 2
|
|
||||||
? '12'
|
|
||||||
: rmsLevel * 100 > 1
|
|
||||||
? '11.5'
|
|
||||||
: '11'}rem;width:100%;text-align:center;"
|
|
||||||
>
|
|
||||||
{emoji}
|
|
||||||
</div>
|
|
||||||
{:else if loading || assistantSpeaking}
|
|
||||||
<svg
|
|
||||||
class="size-44 text-gray-900 dark:text-gray-400"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
><style>
|
|
||||||
.spinner_qM83 {
|
|
||||||
animation: spinner_8HQG 1.05s infinite;
|
|
||||||
}
|
|
||||||
.spinner_oXPr {
|
|
||||||
animation-delay: 0.1s;
|
|
||||||
}
|
|
||||||
.spinner_ZTLf {
|
|
||||||
animation-delay: 0.2s;
|
|
||||||
}
|
|
||||||
@keyframes spinner_8HQG {
|
|
||||||
0%,
|
|
||||||
57.14% {
|
|
||||||
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
|
|
||||||
transform: translate(0);
|
|
||||||
}
|
|
||||||
28.57% {
|
|
||||||
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
|
|
||||||
transform: translateY(-6px);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
|
|
||||||
class="spinner_qM83 spinner_oXPr"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
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 rounded-full {(model?.info?.meta
|
|
||||||
?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
|
|
||||||
? ' bg-cover bg-center bg-no-repeat'
|
|
||||||
: 'bg-black dark:bg-white'} "
|
|
||||||
style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
|
|
||||||
'/static/favicon.png'
|
|
||||||
? `background-image: url('${model?.info?.meta?.profile_image_url}');`
|
|
||||||
: ''}
|
|
||||||
/>
|
|
||||||
{/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=" {rmsLevel * 100 > 4
|
||||||
>
|
? ' size-52'
|
||||||
<video
|
: rmsLevel * 100 > 2
|
||||||
id="camera-feed"
|
? 'size-48'
|
||||||
autoplay
|
: rmsLevel * 100 > 1
|
||||||
class="rounded-2xl h-full min-w-full object-cover object-center"
|
? 'size-[11.5rem]'
|
||||||
playsinline
|
: 'size-44'} transition-all rounded-full {(model?.info?.meta
|
||||||
/>
|
?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
|
||||||
|
? ' bg-cover bg-center bg-no-repeat'
|
||||||
<canvas id="camera-canvas" style="display:none;" />
|
: 'bg-black dark:bg-white'} "
|
||||||
|
style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
|
||||||
<div class=" absolute top-4 md:top-8 left-4">
|
'/static/favicon.png'
|
||||||
<button
|
? `background-image: url('${model?.info?.meta?.profile_image_url}');`
|
||||||
type="button"
|
: ''}
|
||||||
class="p-1.5 text-white cursor-pointer backdrop-blur-xl bg-black/10 rounded-full"
|
/>
|
||||||
on:click={() => {
|
|
||||||
stopCamera();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="currentColor"
|
|
||||||
class="size-6"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</button>
|
||||||
|
{:else}
|
||||||
|
<div class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full">
|
||||||
|
<video
|
||||||
|
id="camera-feed"
|
||||||
|
autoplay
|
||||||
|
class="rounded-2xl h-full min-w-full object-cover object-center"
|
||||||
|
playsinline
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="flex justify-between items-center pb-2 w-full">
|
<canvas id="camera-canvas" style="display:none;" />
|
||||||
<div>
|
|
||||||
{#if camera}
|
|
||||||
<VideoInputMenu
|
|
||||||
devices={videoInputDevices}
|
|
||||||
on:change={async (e) => {
|
|
||||||
console.log(e.detail);
|
|
||||||
selectedVideoInputDeviceId = e.detail;
|
|
||||||
await stopVideoStream();
|
|
||||||
await startVideoStream();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900" type="button">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
class="size-5"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0V5.36l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389A5.5 5.5 0 0 1 13.89 6.11l.311.31h-2.432a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.219Z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</VideoInputMenu>
|
|
||||||
{:else}
|
|
||||||
<Tooltip content={$i18n.t('Camera')}>
|
|
||||||
<button
|
|
||||||
class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900"
|
|
||||||
type="button"
|
|
||||||
on:click={async () => {
|
|
||||||
await navigator.mediaDevices.getUserMedia({ video: true });
|
|
||||||
startCamera();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke="currentColor"
|
|
||||||
class="size-5"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div class=" absolute top-4 md:top-8 left-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
class="p-1.5 text-white cursor-pointer backdrop-blur-xl bg-black/10 rounded-full"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (assistantSpeaking) {
|
stopCamera();
|
||||||
stopAllAudio();
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" line-clamp-1 text-sm font-medium">
|
<svg
|
||||||
{#if loading}
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
{$i18n.t('Thinking...')}
|
viewBox="0 0 16 16"
|
||||||
{:else if assistantSpeaking}
|
fill="currentColor"
|
||||||
{$i18n.t('Tap to interrupt')}
|
class="size-6"
|
||||||
{:else}
|
>
|
||||||
{$i18n.t('Listening...')}
|
<path
|
||||||
{/if}
|
d="M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z"
|
||||||
</div>
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="flex justify-between items-center pb-2 w-full">
|
||||||
<button
|
<div>
|
||||||
class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900"
|
{#if camera}
|
||||||
on:click={async () => {
|
<VideoInputMenu
|
||||||
showCallOverlay.set(false);
|
devices={videoInputDevices}
|
||||||
}}
|
on:change={async (e) => {
|
||||||
type="button"
|
console.log(e.detail);
|
||||||
>
|
selectedVideoInputDeviceId = e.detail;
|
||||||
|
await stopVideoStream();
|
||||||
|
await startVideoStream();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900" type="button">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -931,12 +845,87 @@
|
|||||||
class="size-5"
|
class="size-5"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z"
|
fill-rule="evenodd"
|
||||||
|
d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0V5.36l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389A5.5 5.5 0 0 1 13.89 6.11l.311.31h-2.432a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.219Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
</VideoInputMenu>
|
||||||
|
{:else}
|
||||||
|
<Tooltip content={$i18n.t('Camera')}>
|
||||||
|
<button
|
||||||
|
class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900"
|
||||||
|
type="button"
|
||||||
|
on:click={async () => {
|
||||||
|
await navigator.mediaDevices.getUserMedia({ video: true });
|
||||||
|
startCamera();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="size-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
on:click={() => {
|
||||||
|
if (assistantSpeaking) {
|
||||||
|
stopAllAudio();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class=" line-clamp-1 text-sm font-medium">
|
||||||
|
{#if loading}
|
||||||
|
{$i18n.t('Thinking...')}
|
||||||
|
{:else if assistantSpeaking}
|
||||||
|
{$i18n.t('Tap to interrupt')}
|
||||||
|
{:else}
|
||||||
|
{$i18n.t('Listening...')}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900"
|
||||||
|
on:click={async () => {
|
||||||
|
showCallOverlay.set(false);
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="size-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user