Ver Fonte

enh: temp chat

deprecates chat history setting and introduces temp chat from model selector
Timothy J. Baek há 8 meses atrás
pai
commit
dc6ca61548

+ 9 - 8
src/lib/components/chat/Chat.svelte

@@ -26,7 +26,8 @@
 		socket,
 		showCallOverlay,
 		tools,
-		currentChatPage
+		currentChatPage,
+		temporaryChatEnabled
 	} from '$lib/stores';
 	import {
 		convertMessagesToHistory,
@@ -238,7 +239,7 @@
 				}
 			});
 		} else {
-			if (!($settings.saveChatHistory ?? true)) {
+			if ($temporaryChatEnabled) {
 				await goto('/');
 			}
 		}
@@ -414,7 +415,7 @@
 		}
 
 		if ($chatId == chatId) {
-			if ($settings.saveChatHistory ?? true) {
+			if (!$temporaryChatEnabled) {
 				chat = await updateChatById(localStorage.token, chatId, {
 					models: selectedModels,
 					messages: messages,
@@ -462,7 +463,7 @@
 		}
 
 		if ($chatId == chatId) {
-			if ($settings.saveChatHistory ?? true) {
+			if (!$temporaryChatEnabled) {
 				chat = await updateChatById(localStorage.token, chatId, {
 					models: selectedModels,
 					messages: messages,
@@ -620,7 +621,7 @@
 
 		// Create new chat if only one message in messages
 		if (newChat && messages.length == 2) {
-			if ($settings.saveChatHistory ?? true) {
+			if (!$temporaryChatEnabled) {
 				chat = await createNewChat(localStorage.token, {
 					id: $chatId,
 					title: $i18n.t('New Chat'),
@@ -954,7 +955,7 @@
 			}
 
 			if ($chatId == _chatId) {
-				if ($settings.saveChatHistory ?? true) {
+				if (!$temporaryChatEnabled) {
 					chat = await updateChatById(localStorage.token, _chatId, {
 						messages: messages,
 						history: history,
@@ -1227,7 +1228,7 @@
 				}
 
 				if ($chatId == _chatId) {
-					if ($settings.saveChatHistory ?? true) {
+					if (!$temporaryChatEnabled) {
 						chat = await updateChatById(localStorage.token, _chatId, {
 							models: selectedModels,
 							messages: messages,
@@ -1400,7 +1401,7 @@
 			title = _title;
 		}
 
-		if ($settings.saveChatHistory ?? true) {
+		if (!$temporaryChatEnabled) {
 			chat = await updateChatById(localStorage.token, _chatId, { title: _title });
 
 			currentChatPage.set(1);

+ 14 - 1
src/lib/components/chat/Messages/Placeholder.svelte

@@ -2,7 +2,7 @@
 	import { WEBUI_BASE_URL } from '$lib/constants';
 	import { marked } from 'marked';
 
-	import { config, user, models as _models } from '$lib/stores';
+	import { config, user, models as _models, temporaryChatEnabled } from '$lib/stores';
 	import { onMount, getContext } from 'svelte';
 
 	import { blur, fade } from 'svelte/transition';
@@ -10,6 +10,7 @@
 	import Suggestions from '../MessageInput/Suggestions.svelte';
 	import { sanitizeResponseContent } from '$lib/utils';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import EyeSlash from '$lib/components/icons/EyeSlash.svelte';
 
 	const i18n = getContext('i18n');
 
@@ -64,6 +65,18 @@
 			</div>
 		</div>
 
+		{#if $temporaryChatEnabled}
+			<Tooltip
+				content="This chat won't appear in history and your messages will not be saved."
+				className="w-fit"
+				placement="top-start"
+			>
+				<div class="flex items-center gap-2 text-gray-500 font-medium text-lg my-2 w-fit">
+					<EyeSlash strokeWidth="2.5" className="size-5" /> Temporary Chat
+				</div>
+			</Tooltip>
+		{/if}
+
 		<div
 			class=" mt-2 mb-4 text-3xl text-gray-800 dark:text-gray-100 font-semibold text-left flex items-center gap-4 font-primary"
 		>

+ 1 - 0
src/lib/components/chat/ModelSelector.svelte

@@ -46,6 +46,7 @@
 							label: model.name,
 							model: model
 						}))}
+						showTemporaryChatControl={true}
 						bind:value={selectedModel}
 					/>
 				</div>

+ 29 - 1
src/lib/components/chat/ModelSelector/Selector.svelte

@@ -12,12 +12,14 @@
 
 	import { deleteModel, getOllamaVersion, pullModel } from '$lib/apis/ollama';
 
-	import { user, MODEL_DOWNLOAD_POOL, models, mobile } from '$lib/stores';
+	import { user, MODEL_DOWNLOAD_POOL, models, mobile, temporaryChatEnabled } from '$lib/stores';
 	import { toast } from 'svelte-sonner';
 	import { capitalizeFirstLetter, sanitizeResponseContent, splitStream } from '$lib/utils';
 	import { getModels } from '$lib/apis';
 
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import ChatBubbleOval from '$lib/components/icons/ChatBubbleOval.svelte';
 
 	const i18n = getContext('i18n');
 	const dispatch = createEventDispatcher();
@@ -27,6 +29,8 @@
 	export let searchEnabled = true;
 	export let searchPlaceholder = $i18n.t('Search a model');
 
+	export let showTemporaryChatControl = false;
+
 	export let items: {
 		label: string;
 		value: string;
@@ -514,6 +518,30 @@
 				{/each}
 			</div>
 
+			{#if showTemporaryChatControl}
+				<hr class="border-gray-100 dark:border-gray-800" />
+
+				<div class="flex items-center mx-2 my-2">
+					<button
+						class="flex justify-between w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 px-3 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
+						on:click={() => {
+							temporaryChatEnabled.set(!$temporaryChatEnabled);
+							show = false;
+						}}
+					>
+						<div class="flex gap-2.5 items-center">
+							<ChatBubbleOval className="size-4" strokeWidth="2.5" />
+
+							{$i18n.t(`Temporary Chat`)}
+						</div>
+
+						<div>
+							<Switch state={$temporaryChatEnabled} />
+						</div>
+					</button>
+				</div>
+			{/if}
+
 			<div class="hidden w-[42rem]" />
 			<div class="hidden w-[32rem]" />
 		</slot>

+ 1 - 73
src/lib/components/chat/Settings/Chats.svelte

@@ -20,8 +20,8 @@
 	const i18n = getContext('i18n');
 
 	export let saveSettings: Function;
+
 	// Chats
-	let saveChatHistory = true;
 	let importFiles;
 
 	let showArchiveConfirm = false;
@@ -95,82 +95,10 @@
 		await chats.set(await getChatList(localStorage.token, $currentChatPage));
 		scrollPaginationEnabled.set(true);
 	};
-
-	const toggleSaveChatHistory = async () => {
-		saveChatHistory = !saveChatHistory;
-		console.log(saveChatHistory);
-
-		if (saveChatHistory === false) {
-			await goto('/');
-		}
-		saveSettings({ saveChatHistory: saveChatHistory });
-	};
-
-	onMount(async () => {
-		saveChatHistory = $settings.saveChatHistory ?? true;
-	});
 </script>
 
 <div class="flex flex-col h-full justify-between space-y-3 text-sm max-h-[22rem]">
 	<div class=" space-y-2">
-		<div
-			class="flex flex-col justify-between rounded-md items-center py-2 px-3.5 w-full transition"
-		>
-			<div class="flex w-full justify-between">
-				<div class=" self-center text-sm font-medium">{$i18n.t('Chat History')}</div>
-
-				<button
-					class="p-1 px-3 text-xs flex rounded transition"
-					type="button"
-					on:click={() => {
-						toggleSaveChatHistory();
-					}}
-				>
-					{#if saveChatHistory === true}
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 16 16"
-							fill="currentColor"
-							class="w-4 h-4"
-						>
-							<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" />
-							<path
-								fill-rule="evenodd"
-								d="M1.38 8.28a.87.87 0 0 1 0-.566 7.003 7.003 0 0 1 13.238.006.87.87 0 0 1 0 .566A7.003 7.003 0 0 1 1.379 8.28ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
-								clip-rule="evenodd"
-							/>
-						</svg>
-
-						<span class="ml-2 self-center"> {$i18n.t('On')} </span>
-					{:else}
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 16 16"
-							fill="currentColor"
-							class="w-4 h-4"
-						>
-							<path
-								fill-rule="evenodd"
-								d="M3.28 2.22a.75.75 0 0 0-1.06 1.06l10.5 10.5a.75.75 0 1 0 1.06-1.06l-1.322-1.323a7.012 7.012 0 0 0 2.16-3.11.87.87 0 0 0 0-.567A7.003 7.003 0 0 0 4.82 3.76l-1.54-1.54Zm3.196 3.195 1.135 1.136A1.502 1.502 0 0 1 9.45 8.389l1.136 1.135a3 3 0 0 0-4.109-4.109Z"
-								clip-rule="evenodd"
-							/>
-							<path
-								d="m7.812 10.994 1.816 1.816A7.003 7.003 0 0 1 1.38 8.28a.87.87 0 0 1 0-.566 6.985 6.985 0 0 1 1.113-2.039l2.513 2.513a3 3 0 0 0 2.806 2.806Z"
-							/>
-						</svg>
-
-						<span class="ml-2 self-center">{$i18n.t('Off')}</span>
-					{/if}
-				</button>
-			</div>
-
-			<div class="text-xs text-left w-full font-medium mt-0.5">
-				{$i18n.t('This setting does not sync across browsers or devices.')}
-			</div>
-		</div>
-
-		<hr class=" dark:border-gray-850" />
-
 		<div class="flex flex-col">
 			<input
 				id="chat-import-input"

+ 0 - 126
src/lib/components/documents/Settings/ChunkParams.svelte

@@ -1,126 +0,0 @@
-<script lang="ts">
-	import { getDocs } from '$lib/apis/documents';
-	import {
-		getRAGConfig,
-		updateRAGConfig,
-		getQuerySettings,
-		scanDocs,
-		updateQuerySettings,
-		resetVectorDB,
-		getEmbeddingConfig,
-		updateEmbeddingConfig,
-		getRerankingConfig,
-		updateRerankingConfig
-	} from '$lib/apis/rag';
-
-	import { documents, models } from '$lib/stores';
-	import { onMount, getContext } from 'svelte';
-	import { toast } from 'svelte-sonner';
-
-	import Tooltip from '$lib/components/common/Tooltip.svelte';
-
-	const i18n = getContext('i18n');
-
-	export let saveHandler: Function;
-
-	let scanDirLoading = false;
-	let updateEmbeddingModelLoading = false;
-	let updateRerankingModelLoading = false;
-
-	let showResetConfirm = false;
-
-	let chunkSize = 0;
-	let chunkOverlap = 0;
-	let pdfExtractImages = true;
-
-	const submitHandler = async () => {
-		const res = await updateRAGConfig(localStorage.token, {
-			pdf_extract_images: pdfExtractImages,
-			chunk: {
-				chunk_overlap: chunkOverlap,
-				chunk_size: chunkSize
-			}
-		});
-	};
-
-	onMount(async () => {
-		const res = await getRAGConfig(localStorage.token);
-
-		if (res) {
-			pdfExtractImages = res.pdf_extract_images;
-
-			chunkSize = res.chunk.chunk_size;
-			chunkOverlap = res.chunk.chunk_overlap;
-		}
-	});
-</script>
-
-<form
-	class="flex flex-col h-full justify-between space-y-3 text-sm"
-	on:submit|preventDefault={() => {
-		submitHandler();
-		saveHandler();
-	}}
->
-	<div class=" space-y-3 pr-1.5 overflow-y-scroll h-full max-h-[22rem]">
-		<div class=" ">
-			<div class=" text-sm font-medium">{$i18n.t('Chunk Params')}</div>
-
-			<div class=" flex">
-				<div class="  flex w-full justify-between">
-					<div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Size')}</div>
-
-					<div class="self-center p-3">
-						<input
-							class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-							type="number"
-							placeholder={$i18n.t('Enter Chunk Size')}
-							bind:value={chunkSize}
-							autocomplete="off"
-							min="0"
-						/>
-					</div>
-				</div>
-
-				<div class="flex w-full">
-					<div class=" self-center text-xs font-medium min-w-fit">
-						{$i18n.t('Chunk Overlap')}
-					</div>
-
-					<div class="self-center p-3">
-						<input
-							class="w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-							type="number"
-							placeholder={$i18n.t('Enter Chunk Overlap')}
-							bind:value={chunkOverlap}
-							autocomplete="off"
-							min="0"
-						/>
-					</div>
-				</div>
-			</div>
-
-			<div class="pr-2">
-				<div class="flex justify-between items-center text-xs">
-					<div class=" text-xs font-medium">{$i18n.t('PDF Extract Images (OCR)')}</div>
-
-					<button
-						class=" text-xs font-medium text-gray-500"
-						type="button"
-						on:click={() => {
-							pdfExtractImages = !pdfExtractImages;
-						}}>{pdfExtractImages ? $i18n.t('On') : $i18n.t('Off')}</button
-					>
-				</div>
-			</div>
-		</div>
-	</div>
-	<div class="flex justify-end pt-3 text-sm font-medium">
-		<button
-			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
-			type="submit"
-		>
-			{$i18n.t('Save')}
-		</button>
-	</div>
-</form>

+ 0 - 119
src/lib/components/documents/Settings/QueryParams.svelte

@@ -1,119 +0,0 @@
-<script lang="ts">
-	import { getDocs } from '$lib/apis/documents';
-	import {
-		getRAGConfig,
-		updateRAGConfig,
-		getQuerySettings,
-		scanDocs,
-		updateQuerySettings,
-		resetVectorDB,
-		getEmbeddingConfig,
-		updateEmbeddingConfig,
-		getRerankingConfig,
-		updateRerankingConfig
-	} from '$lib/apis/rag';
-
-	import { documents, models } from '$lib/stores';
-	import { onMount, getContext } from 'svelte';
-	import { toast } from 'svelte-sonner';
-
-	import Tooltip from '$lib/components/common/Tooltip.svelte';
-
-	const i18n = getContext('i18n');
-
-	export let saveHandler: Function;
-
-	let querySettings = {
-		template: '',
-		r: 0.0,
-		k: 4,
-		hybrid: false
-	};
-
-	const submitHandler = async () => {
-		querySettings = await updateQuerySettings(localStorage.token, querySettings);
-	};
-
-	onMount(async () => {
-		querySettings = await getQuerySettings(localStorage.token);
-	});
-</script>
-
-<form
-	class="flex flex-col h-full justify-between space-y-3 text-sm"
-	on:submit|preventDefault={() => {
-		submitHandler();
-		saveHandler();
-	}}
->
-	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[25rem]">
-		<div class=" ">
-			<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>
-
-			<div class=" flex">
-				<div class="  flex w-full justify-between">
-					<div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Top K')}</div>
-
-					<div class="self-center p-3">
-						<input
-							class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-							type="number"
-							placeholder={$i18n.t('Enter Top K')}
-							bind:value={querySettings.k}
-							autocomplete="off"
-							min="0"
-						/>
-					</div>
-				</div>
-
-				{#if querySettings.hybrid === true}
-					<div class="flex w-full">
-						<div class=" self-center text-xs font-medium min-w-fit">
-							{$i18n.t('Minimum Score')}
-						</div>
-
-						<div class="self-center p-3">
-							<input
-								class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-								type="number"
-								step="0.01"
-								placeholder={$i18n.t('Enter Score')}
-								bind:value={querySettings.r}
-								autocomplete="off"
-								min="0.0"
-								title={$i18n.t('The score should be a value between 0.0 (0%) and 1.0 (100%).')}
-							/>
-						</div>
-					</div>
-				{/if}
-			</div>
-
-			{#if querySettings.hybrid === true}
-				<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
-					{$i18n.t(
-						'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
-					)}
-				</div>
-
-				<hr class=" dark:border-gray-850 my-3" />
-			{/if}
-
-			<div>
-				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
-				<textarea
-					bind:value={querySettings.template}
-					class="w-full rounded-lg px-4 py-3 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
-					rows="4"
-				/>
-			</div>
-		</div>
-	</div>
-	<div class="flex justify-end pt-3 text-sm font-medium">
-		<button
-			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
-			type="submit"
-		>
-			{$i18n.t('Save')}
-		</button>
-	</div>
-</form>

+ 0 - 285
src/lib/components/documents/Settings/WebParams.svelte

@@ -1,285 +0,0 @@
-<script lang="ts">
-	import { getRAGConfig, updateRAGConfig } from '$lib/apis/rag';
-	import Switch from '$lib/components/common/Switch.svelte';
-
-	import { documents, models } from '$lib/stores';
-	import { onMount, getContext } from 'svelte';
-	import { toast } from 'svelte-sonner';
-
-	const i18n = getContext('i18n');
-
-	export let saveHandler: Function;
-
-	let webConfig = null;
-	let webSearchEngines = ['searxng', 'google_pse', 'brave', 'serpstack', 'serper', 'serply'];
-
-	let youtubeLanguage = 'en';
-	let youtubeTranslation = null;
-
-	const submitHandler = async () => {
-		const res = await updateRAGConfig(localStorage.token, {
-			web: webConfig,
-			youtube: {
-				language: youtubeLanguage.split(',').map((lang) => lang.trim()),
-				translation: youtubeTranslation
-			}
-		});
-	};
-
-	onMount(async () => {
-		const res = await getRAGConfig(localStorage.token);
-
-		if (res) {
-			webConfig = res.web;
-
-			youtubeLanguage = res.youtube.language.join(',');
-			youtubeTranslation = res.youtube.translation;
-		}
-	});
-</script>
-
-<form
-	class="flex flex-col h-full justify-between space-y-3 text-sm"
-	on:submit|preventDefault={async () => {
-		await submitHandler();
-		saveHandler();
-	}}
->
-	<div class=" space-y-3 pr-1.5 overflow-y-scroll h-full max-h-[22rem]">
-		{#if webConfig}
-			<div>
-				<div class=" mb-1 text-sm font-medium">
-					{$i18n.t('Web Search')}
-				</div>
-
-				<div>
-					<div class=" py-0.5 flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">
-							{$i18n.t('Enable Web Search')}
-						</div>
-
-						<Switch bind:state={webConfig.search.enabled} />
-					</div>
-				</div>
-
-				<div class=" py-0.5 flex w-full justify-between">
-					<div class=" self-center text-xs font-medium">{$i18n.t('Web Search Engine')}</div>
-					<div class="flex items-center relative">
-						<select
-							class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
-							bind:value={webConfig.search.engine}
-							placeholder={$i18n.t('Select a engine')}
-							required
-						>
-							<option disabled selected value="">{$i18n.t('Select a engine')}</option>
-							{#each webSearchEngines as engine}
-								<option value={engine}>{engine}</option>
-							{/each}
-						</select>
-					</div>
-				</div>
-
-				{#if webConfig.search.engine !== ''}
-					<div class="mt-1.5">
-						{#if webConfig.search.engine === 'searxng'}
-							<div>
-								<div class=" self-center text-xs font-medium mb-1">
-									{$i18n.t('Searxng Query URL')}
-								</div>
-
-								<div class="flex w-full">
-									<div class="flex-1">
-										<input
-											class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											type="text"
-											placeholder={$i18n.t('Enter Searxng Query URL')}
-											bind:value={webConfig.search.searxng_query_url}
-											autocomplete="off"
-										/>
-									</div>
-								</div>
-							</div>
-						{:else if webConfig.search.engine === 'google_pse'}
-							<div>
-								<div class=" self-center text-xs font-medium mb-1">
-									{$i18n.t('Google PSE API Key')}
-								</div>
-
-								<div class="flex w-full">
-									<div class="flex-1">
-										<input
-											class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											type="text"
-											placeholder={$i18n.t('Enter Google PSE API Key')}
-											bind:value={webConfig.search.google_pse_api_key}
-											autocomplete="off"
-										/>
-									</div>
-								</div>
-							</div>
-							<div class="mt-1.5">
-								<div class=" self-center text-xs font-medium mb-1">
-									{$i18n.t('Google PSE Engine Id')}
-								</div>
-
-								<div class="flex w-full">
-									<div class="flex-1">
-										<input
-											class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											type="text"
-											placeholder={$i18n.t('Enter Google PSE Engine Id')}
-											bind:value={webConfig.search.google_pse_engine_id}
-											autocomplete="off"
-										/>
-									</div>
-								</div>
-							</div>
-						{:else if webConfig.search.engine === 'brave'}
-							<div>
-								<div class=" self-center text-xs font-medium mb-1">
-									{$i18n.t('Brave Search API Key')}
-								</div>
-
-								<div class="flex w-full">
-									<div class="flex-1">
-										<input
-											class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											type="text"
-											placeholder={$i18n.t('Enter Brave Search API Key')}
-											bind:value={webConfig.search.brave_search_api_key}
-											autocomplete="off"
-										/>
-									</div>
-								</div>
-							</div>
-						{:else if webConfig.search.engine === 'serpstack'}
-							<div>
-								<div class=" self-center text-xs font-medium mb-1">
-									{$i18n.t('Serpstack API Key')}
-								</div>
-
-								<div class="flex w-full">
-									<div class="flex-1">
-										<input
-											class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											type="text"
-											placeholder={$i18n.t('Enter Serpstack API Key')}
-											bind:value={webConfig.search.serpstack_api_key}
-											autocomplete="off"
-										/>
-									</div>
-								</div>
-							</div>
-						{:else if webConfig.search.engine === 'serper'}
-							<div>
-								<div class=" self-center text-xs font-medium mb-1">
-									{$i18n.t('Serper API Key')}
-								</div>
-
-								<div class="flex w-full">
-									<div class="flex-1">
-										<input
-											class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											type="text"
-											placeholder={$i18n.t('Enter Serper API Key')}
-											bind:value={webConfig.search.serper_api_key}
-											autocomplete="off"
-										/>
-									</div>
-								</div>
-							</div>
-						{/if}
-					</div>
-				{/if}
-
-				{#if webConfig.search.enabled}
-					<div class="mt-2 flex gap-2 mb-1">
-						<div class="w-full">
-							<div class=" self-center text-xs font-medium mb-1">
-								{$i18n.t('Search Result Count')}
-							</div>
-
-							<input
-								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-								placeholder={$i18n.t('Search Result Count')}
-								bind:value={webConfig.search.result_count}
-								required
-							/>
-						</div>
-
-						<div class="w-full">
-							<div class=" self-center text-xs font-medium mb-1">
-								{$i18n.t('Concurrent Requests')}
-							</div>
-
-							<input
-								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-								placeholder={$i18n.t('Concurrent Requests')}
-								bind:value={webConfig.search.concurrent_requests}
-								required
-							/>
-						</div>
-					</div>
-				{/if}
-			</div>
-
-			<hr class=" dark:border-gray-850 my-2" />
-
-			<div>
-				<div class=" mb-1 text-sm font-medium">
-					{$i18n.t('Web Loader Settings')}
-				</div>
-
-				<div>
-					<div class=" py-0.5 flex w-full justify-between">
-						<div class=" self-center text-xs font-medium">
-							{$i18n.t('Bypass SSL verification for Websites')}
-						</div>
-
-						<button
-							class="p-1 px-3 text-xs flex rounded transition"
-							on:click={() => {
-								webConfig.ssl_verification = !webConfig.ssl_verification;
-								submitHandler();
-							}}
-							type="button"
-						>
-							{#if webConfig.ssl_verification === true}
-								<span class="ml-2 self-center">{$i18n.t('On')}</span>
-							{:else}
-								<span class="ml-2 self-center">{$i18n.t('Off')}</span>
-							{/if}
-						</button>
-					</div>
-				</div>
-
-				<div class=" mt-2 mb-1 text-sm font-medium">
-					{$i18n.t('Youtube Loader Settings')}
-				</div>
-
-				<div>
-					<div class=" py-0.5 flex w-full justify-between">
-						<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div>
-						<div class=" flex-1 self-center">
-							<input
-								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-								type="text"
-								placeholder={$i18n.t('Enter language codes')}
-								bind:value={youtubeLanguage}
-								autocomplete="off"
-							/>
-						</div>
-					</div>
-				</div>
-			</div>
-		{/if}
-	</div>
-	<div class="flex justify-end pt-3 text-sm font-medium">
-		<button
-			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
-			type="submit"
-		>
-			{$i18n.t('Save')}
-		</button>
-	</div>
-</form>

+ 0 - 187
src/lib/components/documents/SettingsModal.svelte

@@ -1,187 +0,0 @@
-<script>
-	import { getContext, tick } from 'svelte';
-	import Modal from '../common/Modal.svelte';
-	import General from './Settings/General.svelte';
-	import ChunkParams from './Settings/ChunkParams.svelte';
-	import QueryParams from './Settings/QueryParams.svelte';
-	import WebParams from './Settings/WebParams.svelte';
-	import { toast } from 'svelte-sonner';
-	import { config } from '$lib/stores';
-	import { getBackendConfig } from '$lib/apis';
-
-	const i18n = getContext('i18n');
-
-	export let show = false;
-
-	let selectedTab = 'general';
-</script>
-
-<Modal bind:show>
-	<div>
-		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
-			<div class=" text-lg font-medium self-center">{$i18n.t('Document Settings')}</div>
-			<button
-				class="self-center"
-				on:click={() => {
-					show = false;
-				}}
-			>
-				<svg
-					xmlns="http://www.w3.org/2000/svg"
-					viewBox="0 0 20 20"
-					fill="currentColor"
-					class="w-5 h-5"
-				>
-					<path
-						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
-					/>
-				</svg>
-			</button>
-		</div>
-
-		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
-			<div
-				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
-			>
-				<button
-					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
-					'general'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
-					on:click={() => {
-						selectedTab = 'general';
-					}}
-				>
-					<div class=" self-center mr-2">
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 16 16"
-							fill="currentColor"
-							class="w-4 h-4"
-						>
-							<path
-								fill-rule="evenodd"
-								d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
-								clip-rule="evenodd"
-							/>
-						</svg>
-					</div>
-					<div class=" self-center">{$i18n.t('General')}</div>
-				</button>
-
-				<button
-					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
-					'chunk'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
-					on:click={() => {
-						selectedTab = 'chunk';
-					}}
-				>
-					<div class=" self-center mr-2">
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 24 24"
-							fill="currentColor"
-							class="w-4 h-4"
-						>
-							<path
-								fill-rule="evenodd"
-								d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875ZM12.75 12a.75.75 0 0 0-1.5 0v2.25H9a.75.75 0 0 0 0 1.5h2.25V18a.75.75 0 0 0 1.5 0v-2.25H15a.75.75 0 0 0 0-1.5h-2.25V12Z"
-								clip-rule="evenodd"
-							/>
-							<path
-								d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
-							/>
-						</svg>
-					</div>
-					<div class=" self-center">{$i18n.t('Chunk Params')}</div>
-				</button>
-
-				<button
-					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
-					'query'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
-					on:click={() => {
-						selectedTab = 'query';
-					}}
-				>
-					<div class=" self-center mr-2">
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 24 24"
-							fill="currentColor"
-							class="w-4 h-4"
-						>
-							<path d="M11.625 16.5a1.875 1.875 0 1 0 0-3.75 1.875 1.875 0 0 0 0 3.75Z" />
-							<path
-								fill-rule="evenodd"
-								d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm6 16.5c.66 0 1.277-.19 1.797-.518l1.048 1.048a.75.75 0 0 0 1.06-1.06l-1.047-1.048A3.375 3.375 0 1 0 11.625 18Z"
-								clip-rule="evenodd"
-							/>
-							<path
-								d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
-							/>
-						</svg>
-					</div>
-					<div class=" self-center">{$i18n.t('Query Params')}</div>
-				</button>
-
-				<button
-					class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
-					'web'
-						? 'bg-gray-200 dark:bg-gray-700'
-						: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
-					on:click={() => {
-						selectedTab = 'web';
-					}}
-				>
-					<div class=" self-center mr-2">
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 24 24"
-							fill="currentColor"
-							class="w-4 h-4"
-						>
-							<path
-								d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z"
-							/>
-						</svg>
-					</div>
-					<div class=" self-center">{$i18n.t('Web Params')}</div>
-				</button>
-			</div>
-			<div class="flex-1 md:min-h-[380px]">
-				{#if selectedTab === 'general'}
-					<General
-						saveHandler={() => {
-							toast.success($i18n.t('Settings saved successfully!'));
-						}}
-					/>
-				{:else if selectedTab === 'chunk'}
-					<ChunkParams
-						saveHandler={() => {
-							toast.success($i18n.t('Settings saved successfully!'));
-						}}
-					/>
-				{:else if selectedTab === 'query'}
-					<QueryParams
-						saveHandler={() => {
-							toast.success($i18n.t('Settings saved successfully!'));
-						}}
-					/>
-				{:else if selectedTab === 'web'}
-					<WebParams
-						saveHandler={async () => {
-							toast.success($i18n.t('Settings saved successfully!'));
-
-							await tick();
-							await config.set(await getBackendConfig());
-						}}
-					/>
-				{/if}
-			</div>
-		</div>
-	</div>
-</Modal>

+ 19 - 0
src/lib/components/icons/ChatBubbleOval.svelte

@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M12 20.25c4.97 0 9-3.694 9-8.25s-4.03-8.25-9-8.25S3 7.444 3 12c0 2.104.859 4.023 2.273 5.48.432.447.74 1.04.586 1.641a4.483 4.483 0 0 1-.923 1.785A5.969 5.969 0 0 0 6 21c1.282 0 2.47-.402 3.445-1.087.81.22 1.668.337 2.555.337Z"
+	/>
+</svg>

+ 19 - 0
src/lib/components/icons/EyeSlash.svelte

@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88"
+	/>
+</svg>

+ 6 - 42
src/lib/components/layout/Sidebar.svelte

@@ -13,7 +13,8 @@
 		showArchivedChats,
 		pinnedChats,
 		scrollPaginationEnabled,
-		currentChatPage
+		currentChatPage,
+		temporaryChatEnabled
 	} from '$lib/stores';
 	import { onMount, getContext, tick } from 'svelte';
 
@@ -380,47 +381,10 @@
 		{/if}
 
 		<div class="relative flex flex-col flex-1 overflow-y-auto">
-			{#if !($settings.saveChatHistory ?? true)}
-				<div class="absolute z-40 w-full h-full bg-gray-50/90 dark:bg-black/90 flex justify-center">
-					<div class=" text-left px-5 py-2">
-						<div class=" font-medium">{$i18n.t('Chat History is off for this browser.')}</div>
-						<div class="text-xs mt-2">
-							{$i18n.t(
-								"When history is turned off, new chats on this browser won't appear in your history on any of your devices."
-							)}
-							<span class=" font-semibold"
-								>{$i18n.t('This setting does not sync across browsers or devices.')}</span
-							>
-						</div>
-
-						<div class="mt-3">
-							<button
-								class="flex justify-center items-center space-x-1.5 px-3 py-2.5 rounded-lg text-xs bg-gray-100 hover:bg-gray-200 transition text-gray-800 font-medium w-full"
-								type="button"
-								on:click={() => {
-									saveSettings({
-										saveChatHistory: true
-									});
-								}}
-							>
-								<svg
-									xmlns="http://www.w3.org/2000/svg"
-									viewBox="0 0 16 16"
-									fill="currentColor"
-									class="w-3 h-3"
-								>
-									<path
-										fill-rule="evenodd"
-										d="M8 1a.75.75 0 0 1 .75.75v6.5a.75.75 0 0 1-1.5 0v-6.5A.75.75 0 0 1 8 1ZM4.11 3.05a.75.75 0 0 1 0 1.06 5.5 5.5 0 1 0 7.78 0 .75.75 0 0 1 1.06-1.06 7 7 0 1 1-9.9 0 .75.75 0 0 1 1.06 0Z"
-										clip-rule="evenodd"
-									/>
-								</svg>
-
-								<div>{$i18n.t('Enable Chat History')}</div>
-							</button>
-						</div>
-					</div>
-				</div>
+			{#if $temporaryChatEnabled}
+				<div
+					class="absolute z-40 w-full h-full bg-gray-50/90 dark:bg-black/90 flex justify-center"
+				></div>
 			{/if}
 
 			<div class="px-2 mt-0.5 mb-2 flex justify-center space-x-2">

+ 1 - 1
src/lib/stores/index.ts

@@ -42,6 +42,7 @@ export const showArchivedChats = writable(false);
 export const showChangelog = writable(false);
 export const showCallOverlay = writable(false);
 
+export const temporaryChatEnabled = writable(false);
 export const scrollPaginationEnabled = writable(false);
 export const currentChatPage = writable(1);
 
@@ -83,7 +84,6 @@ type Settings = {
 	responseAutoPlayback?: boolean;
 	audio?: AudioSettings;
 	showUsername?: boolean;
-	saveChatHistory?: boolean;
 	notificationEnabled?: boolean;
 	title?: TitleSettings;
 	splitLargeDeltas?: boolean;