Timothy Jaeryang Baek 5 月之前
父节点
当前提交
e8b23ef0c1

+ 12 - 0
src/lib/components/icons/Bars3BottomLeft.svelte

@@ -0,0 +1,12 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M2 3.75A.75.75 0 0 1 2.75 3h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 3.75ZM2 8a.75.75 0 0 1 .75-.75h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 8Zm0 4.25a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Z"
+		clip-rule="evenodd"
+	/>
+</svg>

+ 61 - 9
src/lib/components/notes/NoteEditor.svelte

@@ -14,7 +14,7 @@
 	import { config, settings, showSidebar } from '$lib/stores';
 	import { goto } from '$app/navigation';
 
-	import { compressImage } from '$lib/utils';
+	import { compressImage, copyToClipboard } from '$lib/utils';
 	import { WEBUI_API_BASE_URL } from '$lib/constants';
 	import { uploadFile } from '$lib/apis/files';
 
@@ -58,6 +58,10 @@
 	import RecordMenu from './RecordMenu.svelte';
 	import NoteMenu from './Notes/NoteMenu.svelte';
 	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
+	import Sparkles from '../icons/Sparkles.svelte';
+	import SparklesSolid from '../icons/SparklesSolid.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import Bars3BottomLeft from '../icons/Bars3BottomLeft.svelte';
 
 	export let id: null | string = null;
 
@@ -79,6 +83,8 @@
 
 	let files = [];
 
+	let selectedVersion = 'note';
+
 	let recording = false;
 	let displayMediaRecord = false;
 
@@ -135,6 +141,8 @@
 		init();
 	}
 
+	const versionToggleHandler = () => {};
+
 	const uploadFileHandler = async (file) => {
 		const tempItemId = uuidv4();
 		const fileItem = {
@@ -434,6 +442,16 @@
 							onDownload={(type) => {
 								downloadHandler(type);
 							}}
+							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;
 							}}
@@ -465,7 +483,7 @@
 				</div>
 			</div>
 
-			<div class=" flex-1 w-full h-full overflow-auto px-4 pb-5">
+			<div class=" flex-1 w-full h-full overflow-auto px-4 pb-14">
 				{#if files && files.length > 0}
 					<div class="mb-3.5 mt-1.5 w-full flex gap-1 flex-wrap z-40">
 						{#each files as file, fileIdx}
@@ -516,12 +534,12 @@
 	{/if}
 </div>
 
-<div class="absolute bottom-0 right-0 p-5 max-w-full flex justify-end">
-	<div
-		class="flex gap-0.5 justify-end w-full {$showSidebar && recording
-			? 'md:max-w-[calc(100%-260px)]'
-			: ''} max-w-full"
-	>
+<div
+	class="absolute bottom-0 right-0 p-5 max-w-full {$showSidebar
+		? 'md:max-w-[calc(100%-260px)]'
+		: ''} w-full flex justify-end"
+>
+	<div class="flex gap-1 justify-between w-full max-w-full">
 		{#if recording}
 			<div class="flex-1 w-full">
 				<VoiceRecording
@@ -592,12 +610,46 @@
 				}}
 			>
 				<button
-					class="cursor-pointer p-2.5 flex rounded-full border border-gray-50 dark:border-none dark:bg-gray-850 hover:bg-gray-50 dark:hover:bg-gray-800 transition shadow-xl"
+					class="cursor-pointer p-2.5 flex rounded-full border border-gray-50 bg-white dark:border-none dark:bg-gray-850 hover:bg-gray-50 dark:hover:bg-gray-800 transition shadow-xl"
 					type="button"
 				>
 					<MicSolid className="size-4.5" />
 				</button>
 			</RecordMenu>
+
+			<div
+				class="cursor-pointer p-0.5 flex gap-0.5 rounded-full border border-gray-50 dark:border-gray-850 dark:bg-gray-850 transition shadow-xl"
+			>
+				<Tooltip content={$i18n.t('My Notes')} placement="top">
+					<button
+						class="p-2 size-8.5 flex justify-center items-center {selectedVersion === 'note'
+							? 'bg-gray-100 dark:bg-gray-800 '
+							: ' hover:bg-gray-50 dark:hover:bg-gray-800'}  rounded-full transition shrink-0"
+						type="button"
+						on:click={() => {
+							selectedVersion = 'note';
+							versionToggleHandler();
+						}}
+					>
+						<Bars3BottomLeft />
+					</button>
+				</Tooltip>
+
+				<Tooltip content={$i18n.t('Enhance Notes')} placement="top">
+					<button
+						class="p-2 size-8.5 flex justify-center items-center {selectedVersion === 'ai'
+							? 'bg-gray-100 dark:bg-gray-800 '
+							: ' hover:bg-gray-50 dark:hover:bg-gray-800'} rounded-full transition shrink-0"
+						on:click={() => {
+							selectedVersion = 'ai';
+							versionToggleHandler();
+						}}
+						type="button"
+					>
+						<SparklesSolid />
+					</button>
+				</Tooltip>
+			</div>
 		{/if}
 	</div>
 </div>

+ 1 - 1
src/lib/components/notes/Notes.svelte

@@ -388,7 +388,7 @@
 			<div class="flex gap-0.5 justify-end w-full">
 				<Tooltip content={$i18n.t('Create Note')}>
 					<button
-						class="cursor-pointer p-2.5 flex rounded-full border border-gray-50 dark:border-none dark:bg-gray-850 hover:bg-gray-50 dark:hover:bg-gray-800 transition shadow-xl"
+						class="cursor-pointer p-2.5 flex rounded-full border border-gray-50 bg-white dark:border-none dark:bg-gray-850 hover:bg-gray-50 dark:hover:bg-gray-800 transition shadow-xl"
 						type="button"
 						on:click={async () => {
 							createNoteHandler();

+ 30 - 1
src/lib/components/notes/Notes/NoteMenu.svelte

@@ -11,14 +11,17 @@
 	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
 	import Download from '$lib/components/icons/Download.svelte';
 	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
 
 	const i18n = getContext('i18n');
 
 	export let show = false;
-	export let className = 'max-w-[160px]';
+	export let className = 'max-w-[180px]';
 
 	export let onDownload = (type) => {};
 	export let onDelete = () => {};
+	export let onCopyToClipboard = () => {};
 
 	export let onChange = () => {};
 </script>
@@ -73,6 +76,32 @@
 					</DropdownMenu.Item>
 				</DropdownMenu.SubContent>
 			</DropdownMenu.Sub>
+
+			<DropdownMenu.Sub>
+				<DropdownMenu.SubTrigger
+					class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				>
+					<Share strokeWidth="2" />
+
+					<div class="flex items-center">{$i18n.t('Share')}</div>
+				</DropdownMenu.SubTrigger>
+				<DropdownMenu.SubContent
+					class="w-full rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+					transition={flyAndScale}
+					sideOffset={8}
+				>
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							onCopyToClipboard();
+						}}
+					>
+						<DocumentDuplicate strokeWidth="2" />
+						<div class="flex items-center">{$i18n.t('Copy to clipboard')}</div>
+					</DropdownMenu.Item>
+				</DropdownMenu.SubContent>
+			</DropdownMenu.Sub>
+
 			<DropdownMenu.Item
 				class="flex  gap-2  items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
 				on:click={() => {