{#if note} { changeDebounceHandler(); }} /> {/if} { deleteNoteHandler(note.id); showDeleteConfirm = false; }} > {$i18n.t('This will delete')} {note.title}. {#if loading} {:else} { showSidebar.set(!$showSidebar); }} aria-label="Toggle Sidebar" > { titleInputFocused = true; }} on:blur={(e) => { // check if target is generate button if (e.relatedTarget?.id === 'generate-title-button') { return; } titleInputFocused = false; changeDebounceHandler(); }} /> {#if titleInputFocused && !titleGenerating} { e.preventDefault(); e.stopImmediatePropagation(); e.stopPropagation(); generateTitleHandler(); titleInputFocused = false; }} > {/if} {#if editor} { editor.chain().focus().undo().run(); // versionNavigateHandler('prev'); }} disabled={!editor.can().undo()} > { editor.chain().focus().redo().run(); // versionNavigateHandler('next'); }} disabled={!editor.can().redo()} > {/if} { if (showPanel && selectedPanel === 'chat') { showPanel = false; } else { if (!showPanel) { showPanel = true; } selectedPanel = 'chat'; } }} > { if (showPanel && selectedPanel === 'settings') { showPanel = false; } else { if (!showPanel) { showPanel = true; } selectedPanel = 'settings'; } }} > { downloadHandler(type); }} onCopyLink={async () => { const baseUrl = window.location.origin; const res = await copyToClipboard(`${baseUrl}/notes/${note.id}`); if (res) { toast.success($i18n.t('Copied link to clipboard')); } else { toast.error($i18n.t('Failed to copy link')); } }} onCopyToClipboard={async () => { const res = await copyToClipboard(note.data.content.md).catch((error) => { toast.error(`${error}`); return null; }); if (res) { toast.success($i18n.t('Copied to clipboard')); } }} onDelete={() => { showDeleteConfirm = true; }} > { if (e.deltaY !== 0) { e.preventDefault(); e.currentTarget.scrollLeft += e.deltaY; } }} > {#if dayjs(note.created_at / 1000000).isSame(dayjs(), 'day')} {dayjs(note.created_at / 1000000).format($i18n.t('[Today at] h:mm A'))} {:else if dayjs(note.created_at / 1000000).isSame(dayjs().subtract(1, 'day'), 'day')} {dayjs(note.created_at / 1000000).format( $i18n.t('[Yesterday at] h:mm A') )} {:else if dayjs(note.created_at / 1000000).isSame(dayjs().subtract(1, 'week'), 'week')} {dayjs(note.created_at / 1000000).format( $i18n.t('[Last] dddd [at] h:mm A') )} {:else} {dayjs(note.created_at / 1000000).format($i18n.t('DD/MM/YYYY'))} {/if} { showAccessControlModal = true; }} disabled={note?.user_id !== $user?.id && $user?.role !== 'admin'} > {note?.access_control ? $i18n.t('Private') : $i18n.t('Everyone')} {#if editor} {$i18n.t('{{COUNT}} words', { COUNT: wordCount })} {$i18n.t('{{COUNT}} characters', { COUNT: charCount })} {/if} {#if editing} {/if} {#if files && files.length > 0} {#each files as file, fileIdx} {#if file.type === 'image'} { files = files.filter((item, idx) => idx !== fileIdx); note.data.files = files.length > 0 ? files : null; }} /> {:else} { files = files.filter((item) => item?.id !== file.id); note.data.files = files.length > 0 ? files : null; }} /> {/if} {/each} {/if} { note.data.content.html = content.html; note.data.content.md = content.md; if (editor) { wordCount = editor.storage.characterCount.words(); charCount = editor.storage.characterCount.characters(); } }} /> {/if} {#if recording} { recording = false; displayMediaRecord = false; }} onConfirm={(data) => { if (data?.file) { uploadFileHandler(data?.file); } recording = false; displayMediaRecord = false; }} /> {:else} { displayMediaRecord = false; 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) { recording = true; const tracks = stream.getTracks(); tracks.forEach((track) => track.stop()); } stream = null; } catch { toast.error($i18n.t('Permission denied when accessing microphone')); } }} onCaptureAudio={async () => { displayMediaRecord = true; recording = true; }} onUpload={async () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'audio/*'; input.multiple = false; input.click(); input.onchange = async (e) => { const files = e.target.files; if (files && files.length > 0) { await uploadFileHandler(files[0]); } }; }} > {#if editing} { stopResponseHandler(); }} type="button" > {:else} { enhanceNoteHandler(); }} onChat={() => { showPanel = true; selectedPanel = 'chat'; }} > {/if} {/if} {#if selectedPanel === 'chat'} { insertNoteVersion(note); }} scrollToBottomHandler={scrollToBottom} /> {:else if selectedPanel === 'settings'} {/if}