123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- <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">
- {#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>
|