{#if note} { if (id) { try { await updateNoteAccessGrants( localStorage.token, id, note.access_grants ?? [], ); toast.success($i18n.t("Saved")); } catch (error) { toast.error(`${error}`); } } }} /> {/if} { deleteNoteHandler(note.id); showDeleteConfirm = false; }} > {$i18n.t("This will delete")} {note.title}. {#if loading} {:else} {#if $mobile} { showSidebar.set(!$showSidebar); }} > {/if} { titleInputFocused = true; }} on:blur={(e) => { // check if target is generate button if (ignoreBlur) { ignoreBlur = false; return; } titleInputFocused = false; changeDebounceHandler(); }} /> {#if titleInputFocused && !titleGenerating} { ignoreBlur = true; }} on:click={(e) => { e.preventDefault(); e.stopImmediatePropagation(); e.stopPropagation(); generateTitleHandler(); titleInputFocused = false; }} > {/if} {#if note?.write_access} {#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"; } }} > {/if} { 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, note.data.content.html, true, ).catch((error) => { toast.error(`${error}`); return null; }); if (res) { toast.success( $i18n.t("Copied to clipboard"), ); } }} onDelete={() => { showDeleteConfirm = true; }} isPinned={note.is_pinned ?? false} onPin={async () => { await toggleNotePinnedStatusById( localStorage.token, note.id, ); note = await getNoteById( localStorage.token, note.id, ); pinnedNotes.set( await getPinnedNoteList( localStorage.token, ).catch(() => []), ); }} > {#if note?.write_access} { showAccessControlModal = true; }} disabled={note?.user_id !== $user?.id && $user?.role !== "admin"} > {$i18n.t("Access")} {:else} {$i18n.t("Read-Only Access")} {/if} { 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} {#if editor} {$i18n.t("{{COUNT}} words", { COUNT: wordCount, })} {$i18n.t("{{COUNT}} characters", { COUNT: charCount, })} {/if} {#if editing} {/if} { const { from, to } = editor.state.selection; const selectedText = editor.state.doc.textBetween(from, to, " "); if (selectedText.length === 0) { selectedContent = null; } else { selectedContent = { text: selectedText, from: from, to: to, }; } }} onChange={(content) => { note.data.content.html = content.html; note.data.content.md = content.md; if (editor) { wordCount = editor.storage.characterCount.words(); charCount = editor.storage.characterCount.characters(); } }} fileHandler={true} onFileDrop={(currentEditor, files, pos) => { files.forEach(async (file) => { const fileItem = await inputFileHandler( file, ).catch((error) => { return null; }); if (fileItem.type === "image") { // If the file is an image, insert it directly currentEditor .chain() .insertContentAt(pos, { type: "image", attrs: { src: `data://${fileItem.id}`, }, }) .focus() .run(); } }); }} onFilePaste={() => {}} on:paste={async (e) => { e = e.detail.event || e; const clipboardData = e.clipboardData || window.clipboardData; console.log("Clipboard data:", clipboardData); if (clipboardData && clipboardData.items) { console.log( "Clipboard data items:", clipboardData.items, ); for (const item of clipboardData.items) { console.log("Clipboard item:", item); if (item.type.indexOf("image") !== -1) { const blob = item.getAsFile(); const fileItem = await inputFileHandler(blob); if (editor) { editor ?.chain() .insertContentAt( editor.state.selection .$anchor.pos, { type: "image", attrs: { src: `data://${fileItem.id}`, // Use data URI for the image }, }, ) .focus() .run(); } } else if (item?.kind === "file") { const file = item.getAsFile(); await inputFileHandler(file); e.preventDefault(); } } } }} /> {/if} {#if recording} { recording = false; displayMediaRecord = false; }} onConfirm={(data) => { if (data?.file) { uploadFileHandler(data?.file); } recording = false; displayMediaRecord = false; }} /> {:else} {#if editing} { stopResponseHandler(); }} type="button" > {:else} { enhanceNoteHandler(); }} onChat={() => { showPanel = true; selectedPanel = "chat"; }} > {/if} { 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} {#if selectedPanel === "chat"} { insertNoteVersion(note); }} scrollToBottomHandler={scrollToBottom} /> {:else if selectedPanel === "settings"} { changeDebounceHandler(); }} /> {/if}