mirror of
https://github.com/open-webui/open-webui
synced 2024-11-06 08:56:39 +00:00
refac: voice input styling
This commit is contained in:
parent
1cd036e768
commit
0cf8f58efe
@ -522,7 +522,8 @@ def transcription(
|
|||||||
else:
|
else:
|
||||||
data = transcribe(file_path)
|
data = transcribe(file_path)
|
||||||
|
|
||||||
return data
|
file_path = file_path.split("/")[-1]
|
||||||
|
return {**data, "filename": file_path}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
@ -361,8 +361,8 @@
|
|||||||
document.getElementById('chat-input')?.focus();
|
document.getElementById('chat-input')?.focus();
|
||||||
}}
|
}}
|
||||||
on:confirm={async (e) => {
|
on:confirm={async (e) => {
|
||||||
const response = e.detail;
|
const { text, filename } = e.detail;
|
||||||
prompt = `${prompt}${response} `;
|
prompt = `${prompt}${text} `;
|
||||||
|
|
||||||
recording = false;
|
recording = false;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { createEventDispatcher, tick, getContext } from 'svelte';
|
import { createEventDispatcher, tick, getContext, onMount, onDestroy } from 'svelte';
|
||||||
import { config, settings } from '$lib/stores';
|
import { config, settings } from '$lib/stores';
|
||||||
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
|
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
|
||||||
|
|
||||||
@ -52,7 +52,7 @@
|
|||||||
let audioChunks = [];
|
let audioChunks = [];
|
||||||
|
|
||||||
const MIN_DECIBELS = -45;
|
const MIN_DECIBELS = -45;
|
||||||
const VISUALIZER_BUFFER_LENGTH = 300;
|
let VISUALIZER_BUFFER_LENGTH = 300;
|
||||||
|
|
||||||
let visualizerData = Array(VISUALIZER_BUFFER_LENGTH).fill(0);
|
let visualizerData = Array(VISUALIZER_BUFFER_LENGTH).fill(0);
|
||||||
|
|
||||||
@ -142,8 +142,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
console.log(res.text);
|
console.log(res);
|
||||||
dispatch('confirm', res.text);
|
dispatch('confirm', res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -278,12 +278,40 @@
|
|||||||
|
|
||||||
stream = null;
|
stream = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let resizeObserver;
|
||||||
|
let containerWidth;
|
||||||
|
|
||||||
|
let maxVisibleItems = 300;
|
||||||
|
$: maxVisibleItems = Math.floor(containerWidth / 5); // 2px width + 0.5px gap
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// listen to width changes
|
||||||
|
resizeObserver = new ResizeObserver(() => {
|
||||||
|
VISUALIZER_BUFFER_LENGTH = Math.floor(window.innerWidth / 4);
|
||||||
|
if (visualizerData.length > VISUALIZER_BUFFER_LENGTH) {
|
||||||
|
visualizerData = visualizerData.slice(visualizerData.length - VISUALIZER_BUFFER_LENGTH);
|
||||||
|
} else {
|
||||||
|
visualizerData = Array(VISUALIZER_BUFFER_LENGTH - visualizerData.length)
|
||||||
|
.fill(0)
|
||||||
|
.concat(visualizerData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(document.body);
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
// remove resize observer
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
bind:clientWidth={containerWidth}
|
||||||
class="{loading
|
class="{loading
|
||||||
? ' bg-gray-100/50 dark:bg-gray-850/50'
|
? ' bg-gray-100/50 dark:bg-gray-850/50'
|
||||||
: 'bg-indigo-300/10 dark:bg-indigo-500/10 '} rounded-full flex {className}"
|
: 'bg-indigo-300/10 dark:bg-indigo-500/10 '} rounded-full flex justify-between {className}"
|
||||||
>
|
>
|
||||||
<div class="flex items-center mr-1">
|
<div class="flex items-center mr-1">
|
||||||
<button
|
<button
|
||||||
@ -318,146 +346,152 @@
|
|||||||
class="flex flex-1 self-center items-center justify-between ml-2 mx-1 overflow-hidden h-6"
|
class="flex flex-1 self-center items-center justify-between ml-2 mx-1 overflow-hidden h-6"
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
>
|
>
|
||||||
<div class="flex-1 flex items-center gap-0.5 h-6">
|
<div
|
||||||
|
class="flex items-center gap-0.5 h-6 w-full max-w-full overflow-hidden overflow-x-hidden flex-wrap"
|
||||||
|
>
|
||||||
{#each visualizerData.slice().reverse() as rms}
|
{#each visualizerData.slice().reverse() as rms}
|
||||||
<div
|
<div class="flex items-center h-full">
|
||||||
class="w-[2px]
|
<div
|
||||||
|
class="w-[2px] flex-shrink-0
|
||||||
|
|
||||||
{loading
|
{loading
|
||||||
? ' bg-gray-500 dark:bg-gray-400 '
|
? ' bg-gray-500 dark:bg-gray-400 '
|
||||||
: 'bg-indigo-500 dark:bg-indigo-400 '}
|
: 'bg-indigo-500 dark:bg-indigo-400 '}
|
||||||
|
|
||||||
inline-block h-full"
|
inline-block h-full"
|
||||||
style="height: {Math.min(100, Math.max(14, rms * 100))}%;"
|
style="height: {Math.min(100, Math.max(14, rms * 100))}%;"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" mx-1.5 pr-1 flex justify-center items-center">
|
<div class="flex">
|
||||||
<div
|
<div class=" mx-1.5 pr-1 flex justify-center items-center">
|
||||||
class="text-sm
|
<div
|
||||||
|
class="text-sm
|
||||||
|
|
||||||
|
|
||||||
{loading ? ' text-gray-500 dark:text-gray-400 ' : ' text-indigo-400 '}
|
{loading ? ' text-gray-500 dark:text-gray-400 ' : ' text-indigo-400 '}
|
||||||
font-medium flex-1 mx-auto text-center"
|
font-medium flex-1 mx-auto text-center"
|
||||||
>
|
|
||||||
{formatSeconds(durationSeconds)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center mr-1">
|
|
||||||
{#if loading}
|
|
||||||
<div class=" text-gray-500 rounded-full cursor-not-allowed">
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="currentColor"
|
|
||||||
><style>
|
|
||||||
.spinner_OSmW {
|
|
||||||
transform-origin: center;
|
|
||||||
animation: spinner_T6mA 0.75s step-end infinite;
|
|
||||||
}
|
|
||||||
@keyframes spinner_T6mA {
|
|
||||||
8.3% {
|
|
||||||
transform: rotate(30deg);
|
|
||||||
}
|
|
||||||
16.6% {
|
|
||||||
transform: rotate(60deg);
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
33.3% {
|
|
||||||
transform: rotate(120deg);
|
|
||||||
}
|
|
||||||
41.6% {
|
|
||||||
transform: rotate(150deg);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
58.3% {
|
|
||||||
transform: rotate(210deg);
|
|
||||||
}
|
|
||||||
66.6% {
|
|
||||||
transform: rotate(240deg);
|
|
||||||
}
|
|
||||||
75% {
|
|
||||||
transform: rotate(270deg);
|
|
||||||
}
|
|
||||||
83.3% {
|
|
||||||
transform: rotate(300deg);
|
|
||||||
}
|
|
||||||
91.6% {
|
|
||||||
transform: rotate(330deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style><g class="spinner_OSmW"
|
|
||||||
><rect x="11" y="1" width="2" height="5" opacity=".14" /><rect
|
|
||||||
x="11"
|
|
||||||
y="1"
|
|
||||||
width="2"
|
|
||||||
height="5"
|
|
||||||
transform="rotate(30 12 12)"
|
|
||||||
opacity=".29"
|
|
||||||
/><rect
|
|
||||||
x="11"
|
|
||||||
y="1"
|
|
||||||
width="2"
|
|
||||||
height="5"
|
|
||||||
transform="rotate(60 12 12)"
|
|
||||||
opacity=".43"
|
|
||||||
/><rect
|
|
||||||
x="11"
|
|
||||||
y="1"
|
|
||||||
width="2"
|
|
||||||
height="5"
|
|
||||||
transform="rotate(90 12 12)"
|
|
||||||
opacity=".57"
|
|
||||||
/><rect
|
|
||||||
x="11"
|
|
||||||
y="1"
|
|
||||||
width="2"
|
|
||||||
height="5"
|
|
||||||
transform="rotate(120 12 12)"
|
|
||||||
opacity=".71"
|
|
||||||
/><rect
|
|
||||||
x="11"
|
|
||||||
y="1"
|
|
||||||
width="2"
|
|
||||||
height="5"
|
|
||||||
transform="rotate(150 12 12)"
|
|
||||||
opacity=".86"
|
|
||||||
/><rect x="11" y="1" width="2" height="5" transform="rotate(180 12 12)" /></g
|
|
||||||
></svg
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="p-1.5 bg-indigo-500 text-white dark:bg-indigo-500 dark:text-blue-950 rounded-full"
|
|
||||||
on:click={async () => {
|
|
||||||
await confirmRecording();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<svg
|
{formatSeconds(durationSeconds)}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
</div>
|
||||||
fill="none"
|
</div>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke-width="2.5"
|
<div class="flex items-center">
|
||||||
stroke="currentColor"
|
{#if loading}
|
||||||
class="size-4"
|
<div class=" text-gray-500 rounded-full cursor-not-allowed">
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
><style>
|
||||||
|
.spinner_OSmW {
|
||||||
|
transform-origin: center;
|
||||||
|
animation: spinner_T6mA 0.75s step-end infinite;
|
||||||
|
}
|
||||||
|
@keyframes spinner_T6mA {
|
||||||
|
8.3% {
|
||||||
|
transform: rotate(30deg);
|
||||||
|
}
|
||||||
|
16.6% {
|
||||||
|
transform: rotate(60deg);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
33.3% {
|
||||||
|
transform: rotate(120deg);
|
||||||
|
}
|
||||||
|
41.6% {
|
||||||
|
transform: rotate(150deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
58.3% {
|
||||||
|
transform: rotate(210deg);
|
||||||
|
}
|
||||||
|
66.6% {
|
||||||
|
transform: rotate(240deg);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
83.3% {
|
||||||
|
transform: rotate(300deg);
|
||||||
|
}
|
||||||
|
91.6% {
|
||||||
|
transform: rotate(330deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style><g class="spinner_OSmW"
|
||||||
|
><rect x="11" y="1" width="2" height="5" opacity=".14" /><rect
|
||||||
|
x="11"
|
||||||
|
y="1"
|
||||||
|
width="2"
|
||||||
|
height="5"
|
||||||
|
transform="rotate(30 12 12)"
|
||||||
|
opacity=".29"
|
||||||
|
/><rect
|
||||||
|
x="11"
|
||||||
|
y="1"
|
||||||
|
width="2"
|
||||||
|
height="5"
|
||||||
|
transform="rotate(60 12 12)"
|
||||||
|
opacity=".43"
|
||||||
|
/><rect
|
||||||
|
x="11"
|
||||||
|
y="1"
|
||||||
|
width="2"
|
||||||
|
height="5"
|
||||||
|
transform="rotate(90 12 12)"
|
||||||
|
opacity=".57"
|
||||||
|
/><rect
|
||||||
|
x="11"
|
||||||
|
y="1"
|
||||||
|
width="2"
|
||||||
|
height="5"
|
||||||
|
transform="rotate(120 12 12)"
|
||||||
|
opacity=".71"
|
||||||
|
/><rect
|
||||||
|
x="11"
|
||||||
|
y="1"
|
||||||
|
width="2"
|
||||||
|
height="5"
|
||||||
|
transform="rotate(150 12 12)"
|
||||||
|
opacity=".86"
|
||||||
|
/><rect x="11" y="1" width="2" height="5" transform="rotate(180 12 12)" /></g
|
||||||
|
></svg
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="p-1.5 bg-indigo-500 text-white dark:bg-indigo-500 dark:text-blue-950 rounded-full"
|
||||||
|
on:click={async () => {
|
||||||
|
await confirmRecording();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
<svg
|
||||||
</svg>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</button>
|
fill="none"
|
||||||
{/if}
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="2.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="size-4"
|
||||||
|
>
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
12
src/lib/components/icons/SparklesSolid.svelte
Normal file
12
src/lib/components/icons/SparklesSolid.svelte
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let className = 'w-4 h-4';
|
||||||
|
export let strokeWidth = '1.5';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M9 4.5a.75.75 0 0 1 .721.544l.813 2.846a3.75 3.75 0 0 0 2.576 2.576l2.846.813a.75.75 0 0 1 0 1.442l-2.846.813a3.75 3.75 0 0 0-2.576 2.576l-.813 2.846a.75.75 0 0 1-1.442 0l-.813-2.846a3.75 3.75 0 0 0-2.576-2.576l-2.846-.813a.75.75 0 0 1 0-1.442l2.846-.813A3.75 3.75 0 0 0 7.466 7.89l.813-2.846A.75.75 0 0 1 9 4.5ZM18 1.5a.75.75 0 0 1 .728.568l.258 1.036c.236.94.97 1.674 1.91 1.91l1.036.258a.75.75 0 0 1 0 1.456l-1.036.258c-.94.236-1.674.97-1.91 1.91l-.258 1.036a.75.75 0 0 1-1.456 0l-.258-1.036a2.625 2.625 0 0 0-1.91-1.91l-1.036-.258a.75.75 0 0 1 0-1.456l1.036-.258a2.625 2.625 0 0 0 1.91-1.91l.258-1.036A.75.75 0 0 1 18 1.5ZM16.5 15a.75.75 0 0 1 .712.513l.394 1.183c.15.447.5.799.948.948l1.183.395a.75.75 0 0 1 0 1.422l-1.183.395c-.447.15-.799.5-.948.948l-.395 1.183a.75.75 0 0 1-1.422 0l-.395-1.183a1.5 1.5 0 0 0-.948-.948l-1.183-.395a.75.75 0 0 1 0-1.422l1.183-.395c.447-.15.799-.5.948-.948l.395-1.183A.75.75 0 0 1 16.5 15Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
121
src/lib/components/playground/Notes.svelte
Normal file
121
src/lib/components/playground/Notes.svelte
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<script>
|
||||||
|
import { getContext } from 'svelte';
|
||||||
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
import RichTextInput from '../common/RichTextInput.svelte';
|
||||||
|
import Spinner from '../common/Spinner.svelte';
|
||||||
|
import Sparkles from '../icons/Sparkles.svelte';
|
||||||
|
import SparklesSolid from '../icons/SparklesSolid.svelte';
|
||||||
|
import Mic from '../icons/Mic.svelte';
|
||||||
|
import VoiceRecording from '../chat/MessageInput/VoiceRecording.svelte';
|
||||||
|
import Tooltip from '../common/Tooltip.svelte';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
|
|
||||||
|
let name = '';
|
||||||
|
let content = '';
|
||||||
|
|
||||||
|
let voiceInput = false;
|
||||||
|
let loading = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative flex-1 w-full h-full flex justify-center overflow-auto px-5 py-1">
|
||||||
|
{#if loading}
|
||||||
|
<div class=" absolute top-0 bottom-0 left-0 right-0 flex">
|
||||||
|
<div class="m-auto">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class=" w-full flex flex-col gap-2 {loading ? 'opacity-20' : ''}">
|
||||||
|
<div class="flex-shrink-0 w-full flex justify-between items-center">
|
||||||
|
<div class="w-full">
|
||||||
|
<input
|
||||||
|
class="w-full text-2xl font-medium bg-transparent outline-none"
|
||||||
|
type="text"
|
||||||
|
bind:value={name}
|
||||||
|
placeholder={$i18n.t('Title')}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=" flex-1 w-full h-full">
|
||||||
|
<RichTextInput
|
||||||
|
className=" input-prose-sm"
|
||||||
|
bind:value={content}
|
||||||
|
placeholder={$i18n.t('Write something...')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="absolute bottom-0 left-0 right-0 p-5 max-w-full flex justify-end">
|
||||||
|
<div class="flex gap-0.5 justify-end w-full">
|
||||||
|
{#if voiceInput}
|
||||||
|
<div class="flex-1 w-full">
|
||||||
|
<VoiceRecording
|
||||||
|
bind:recording={voiceInput}
|
||||||
|
className="p-1 w-full max-w-full"
|
||||||
|
on:cancel={() => {
|
||||||
|
voiceInput = false;
|
||||||
|
}}
|
||||||
|
on:confirm={(e) => {
|
||||||
|
const { text, filename } = e.detail;
|
||||||
|
|
||||||
|
// url is hostname + /cache/audio/transcription/ + filename
|
||||||
|
const url = `${window.location.origin}/cache/audio/transcription/${filename}`;
|
||||||
|
|
||||||
|
// Open in new tab
|
||||||
|
|
||||||
|
if (content.trim() !== '') {
|
||||||
|
content = `${content}\n\n${text}\n\nRecording: ${url}\n\n`;
|
||||||
|
} else {
|
||||||
|
content = `${content}${text}\n\nRecording: ${url}\n\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
voiceInput = false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<Tooltip content={$i18n.t('Voice Input')}>
|
||||||
|
<button
|
||||||
|
class="cursor-pointer p-2.5 flex rounded-full hover:bg-gray-100 dark:hover:bg-gray-850 transition shadow-xl"
|
||||||
|
type="button"
|
||||||
|
on:click={async () => {
|
||||||
|
try {
|
||||||
|
let stream = await navigator.mediaDevices
|
||||||
|
.getUserMedia({ audio: true })
|
||||||
|
.catch(function (err) {
|
||||||
|
toast.error(
|
||||||
|
$i18n.t(`Permission denied when accessing microphone: {{error}}`, {
|
||||||
|
error: err
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stream) {
|
||||||
|
voiceInput = true;
|
||||||
|
const tracks = stream.getTracks();
|
||||||
|
tracks.forEach((track) => track.stop());
|
||||||
|
}
|
||||||
|
stream = null;
|
||||||
|
} catch {
|
||||||
|
toast.error($i18n.t('Permission denied when accessing microphone'));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Mic className="size-4" />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- <button
|
||||||
|
class="cursor-pointer p-2.5 flex rounded-full hover:bg-gray-100 dark:hover:bg-gray-850 transition shadow-xl"
|
||||||
|
>
|
||||||
|
<SparklesSolid className="size-4" />
|
||||||
|
</button> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -85,8 +85,8 @@
|
|||||||
voiceInput = false;
|
voiceInput = false;
|
||||||
}}
|
}}
|
||||||
on:confirm={(e) => {
|
on:confirm={(e) => {
|
||||||
const response = e.detail;
|
const { text, filename } = e.detail;
|
||||||
content = `${content}${response} `;
|
content = `${content}${text} `;
|
||||||
|
|
||||||
voiceInput = false;
|
voiceInput = false;
|
||||||
}}
|
}}
|
||||||
|
@ -51,6 +51,15 @@
|
|||||||
href="/playground">{$i18n.t('Chat')}</a
|
href="/playground">{$i18n.t('Chat')}</a
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="min-w-fit rounded-full p-1.5 px-3 {$page.url.pathname.includes(
|
||||||
|
'/playground/notes'
|
||||||
|
)
|
||||||
|
? 'bg-gray-50 dark:bg-gray-850'
|
||||||
|
: ''} transition"
|
||||||
|
href="/playground/notes">{$i18n.t('Notes')}</a
|
||||||
|
>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="min-w-fit rounded-full p-1.5 px-3 {$page.url.pathname.includes(
|
class="min-w-fit rounded-full p-1.5 px-3 {$page.url.pathname.includes(
|
||||||
'/playground/completions'
|
'/playground/completions'
|
||||||
|
5
src/routes/(app)/playground/notes/+page.svelte
Normal file
5
src/routes/(app)/playground/notes/+page.svelte
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<script>
|
||||||
|
import Notes from '$lib/components/playground/Notes.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Notes />
|
Loading…
Reference in New Issue
Block a user