Explorar o código

refac: use new SensitiveInput component

Jun Siang Cheah hai 10 meses
pai
achega
f8f6943128

+ 3 - 98
src/lib/components/admin/Settings/Audio.svelte

@@ -5,6 +5,7 @@
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import Switch from '$lib/components/common/Switch.svelte';
 	import { getBackendConfig } from '$lib/apis';
 	import { getBackendConfig } from '$lib/apis';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
@@ -18,13 +19,11 @@
 	let TTS_ENGINE = '';
 	let TTS_ENGINE = '';
 	let TTS_MODEL = '';
 	let TTS_MODEL = '';
 	let TTS_VOICE = '';
 	let TTS_VOICE = '';
-	let showTTSKey = false;
 
 
 	let STT_OPENAI_API_BASE_URL = '';
 	let STT_OPENAI_API_BASE_URL = '';
 	let STT_OPENAI_API_KEY = '';
 	let STT_OPENAI_API_KEY = '';
 	let STT_ENGINE = '';
 	let STT_ENGINE = '';
 	let STT_MODEL = '';
 	let STT_MODEL = '';
-	let showSTTKey = false;
 
 
 	let voices = [];
 	let voices = [];
 	let models = [];
 	let models = [];
@@ -145,54 +144,7 @@
 								required
 								required
 							/>
 							/>
 
 
-							<div class="flex flex-1">
-								<input
-									class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-									placeholder={$i18n.t('API Key')}
-									bind:value={STT_OPENAI_API_KEY}
-									required
-									{...{ type: showSTTKey ? 'text' : 'password' }}
-								/>
-								<button
-									class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-									on:click={(e) => {
-										e.preventDefault();
-										showSTTKey = !showSTTKey;
-									}}
-								>
-									{#if showSTTKey}
-										<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>
-									{:else}
-										<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>
-									{/if}
-								</button>
-							</div>
+							<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={STT_OPENAI_API_KEY} />
 						</div>
 						</div>
 					</div>
 					</div>
 
 
@@ -257,54 +209,7 @@
 								required
 								required
 							/>
 							/>
 
 
-							<div class="flex-1 flex">
-								<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('API Key')}
-									bind:value={TTS_OPENAI_API_KEY}
-									required
-									{...{ type: showTTSKey ? 'text' : 'password' }}
-								/>
-								<button
-									class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-									on:click={(e) => {
-										e.preventDefault();
-										showTTSKey = !showTTSKey;
-									}}
-								>
-									{#if showSTTKey}
-										<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>
-									{:else}
-										<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>
-									{/if}
-								</button>
-							</div>
+							<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={TTS_OPENAI_API_KEY} />
 						</div>
 						</div>
 					</div>
 					</div>
 				{/if}
 				{/if}

+ 2 - 49
src/lib/components/admin/Settings/Connections.svelte

@@ -25,6 +25,7 @@
 	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Spinner from '$lib/components/common/Spinner.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import { getModels as _getModels } from '$lib/apis';
 	import { getModels as _getModels } from '$lib/apis';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -38,7 +39,6 @@
 
 
 	let OPENAI_API_KEYS = [''];
 	let OPENAI_API_KEYS = [''];
 	let OPENAI_API_BASE_URLS = [''];
 	let OPENAI_API_BASE_URLS = [''];
-	let showAPIKeys = [false];
 
 
 	let pipelineUrls = {};
 	let pipelineUrls = {};
 
 
@@ -230,54 +230,7 @@
 										{/if}
 										{/if}
 									</div>
 									</div>
 
 
-									<div class="flex-1 flex">
-										<input
-											class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											placeholder={$i18n.t('API Key')}
-											bind:value={OPENAI_API_KEYS[idx]}
-											autocomplete="off"
-											{...{ type: showAPIKeys[idx] ? 'text' : 'password' }}
-										/>
-										<button
-											class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-											on:click={(e) => {
-												e.preventDefault();
-												showAPIKeys[idx] = !showAPIKeys[idx];
-											}}
-										>
-											{#if showAPIKeys[idx]}
-												<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>
-											{:else}
-												<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>
-											{/if}
-										</button>
-									</div>
+									<SensitiveInput placeholder={$i18n.t('API Key')} value={OPENAI_API_KEYS[idx]} />
 									<div class="self-center flex items-center">
 									<div class="self-center flex items-center">
 										{#if idx === 0}
 										{#if idx === 0}
 											<button
 											<button

+ 2 - 49
src/lib/components/admin/Settings/Documents.svelte

@@ -20,6 +20,7 @@
 	import { documents, models } from '$lib/stores';
 	import { documents, models } from '$lib/stores';
 	import { onMount, getContext } from 'svelte';
 	import { onMount, getContext } from 'svelte';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -41,7 +42,6 @@
 	let pdfExtractImages = true;
 	let pdfExtractImages = true;
 
 
 	let OpenAIKey = '';
 	let OpenAIKey = '';
-	let showOpenAIKey = false;
 	let OpenAIUrl = '';
 	let OpenAIUrl = '';
 	let OpenAIBatchSize = 1;
 	let OpenAIBatchSize = 1;
 
 
@@ -341,54 +341,7 @@
 						required
 						required
 					/>
 					/>
 
 
-					<div class="flex-1 flex">
-						<input
-							class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-							placeholder={$i18n.t('API Key')}
-							bind:value={OpenAIKey}
-							required
-							{...{ type: showOpenAIKey ? 'text' : 'password' }}
-						/>
-						<button
-							class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-							on:click={(e) => {
-								e.preventDefault();
-								showOpenAIKey = !showOpenAIKey;
-							}}
-						>
-							{#if showOpenAIKey}
-								<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>
-							{:else}
-								<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>
-							{/if}
-						</button>
-					</div>
+					<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OpenAIKey} />
 				</div>
 				</div>
 				<div class="flex mt-0.5 space-x-2">
 				<div class="flex mt-0.5 space-x-2">
 					<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Batch Size')}</div>
 					<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Batch Size')}</div>

+ 3 - 58
src/lib/components/admin/Settings/Images.svelte

@@ -19,6 +19,7 @@
 		updateOpenAIConfig
 		updateOpenAIConfig
 	} from '$lib/apis/images';
 	} from '$lib/apis/images';
 	import { getBackendConfig } from '$lib/apis';
 	import { getBackendConfig } from '$lib/apis';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 	const dispatch = createEventDispatcher();
 	const dispatch = createEventDispatcher();
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
@@ -34,7 +35,6 @@
 
 
 	let OPENAI_API_BASE_URL = '';
 	let OPENAI_API_BASE_URL = '';
 	let OPENAI_API_KEY = '';
 	let OPENAI_API_KEY = '';
-	let showOpenAIKey = false;
 
 
 	let selectedModel = '';
 	let selectedModel = '';
 	let models = null;
 	let models = null;
@@ -277,15 +277,7 @@
 			</div>
 			</div>
 
 
 			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Api Auth String')}</div>
 			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Api Auth String')}</div>
-			<div class="flex w-full">
-				<div class="flex-1 mr-2">
-					<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('Enter api auth string (e.g. username:password)')}
-						bind:value={AUTOMATIC1111_API_AUTH}
-					/>
-				</div>
-			</div>
+			<SensitiveInput placeholder={$i18n.t('Enter api auth string (e.g. username:password)')} bind:value={AUTOMATIC1111_API_AUTH} />
 
 
 			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
 				{$i18n.t('Include `--api-auth` flag when running stable-diffusion-webui')}
 				{$i18n.t('Include `--api-auth` flag when running stable-diffusion-webui')}
@@ -340,54 +332,7 @@
 						required
 						required
 					/>
 					/>
 
 
-					<div class="flex-1 flex">
-						<input
-							class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-							placeholder={$i18n.t('API Key')}
-							bind:value={OPENAI_API_KEY}
-							required
-							{...{ type: showOpenAIKey ? 'text' : 'password' }}
-						/>
-						<button
-							class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-							on:click={(e) => {
-								e.preventDefault();
-								showOpenAIKey = !showOpenAIKey;
-							}}
-						>
-							{#if showOpenAIKey}
-								<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>
-							{:else}
-								<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>
-							{/if}
-						</button>
-					</div>
+					<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OPENAI_API_KEY} />
 				</div>
 				</div>
 			</div>
 			</div>
 		{/if}
 		{/if}

+ 7 - 301
src/lib/components/admin/Settings/WebSearch.svelte

@@ -5,6 +5,7 @@
 	import { documents, models } from '$lib/stores';
 	import { documents, models } from '$lib/stores';
 	import { onMount, getContext } from 'svelte';
 	import { onMount, getContext } from 'svelte';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -22,7 +23,6 @@
 		'tavily',
 		'tavily',
 		'jina'
 		'jina'
 	];
 	];
-	let showApiKey = false;
 
 
 	let youtubeLanguage = 'en';
 	let youtubeLanguage = 'en';
 	let youtubeTranslation = null;
 	let youtubeTranslation = null;
@@ -116,56 +116,7 @@
 									{$i18n.t('Google PSE API Key')}
 									{$i18n.t('Google PSE API Key')}
 								</div>
 								</div>
 
 
-								<div class="flex w-full">
-									<div class="flex-1 flex">
-										<input
-											class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											placeholder={$i18n.t('Enter Google PSE API Key')}
-											bind:value={webConfig.search.google_pse_api_key}
-											autocomplete="off"
-											{...{ type: showApiKey ? 'text' : 'password' }}
-										/>
-										<button
-											class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-											on:click={(e) => {
-												e.preventDefault();
-												showApiKey = !showApiKey;
-											}}
-										>
-											{#if showApiKey}
-												<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>
-											{:else}
-												<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>
-											{/if}
-										</button>
-									</div>
-								</div>
+								<SensitiveInput placeholder={$i18n.t('Enter Google PSE API Key')} bind:value={webConfig.search.google_pse_api_key} />
 							</div>
 							</div>
 							<div class="mt-1.5">
 							<div class="mt-1.5">
 								<div class=" self-center text-xs font-medium mb-1">
 								<div class=" self-center text-xs font-medium mb-1">
@@ -190,56 +141,7 @@
 									{$i18n.t('Brave Search API Key')}
 									{$i18n.t('Brave Search API Key')}
 								</div>
 								</div>
 
 
-								<div class="flex w-full">
-									<div class="flex-1 flex">
-										<input
-											class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											placeholder={$i18n.t('Enter Brave Search API Key')}
-											bind:value={webConfig.search.brave_search_api_key}
-											autocomplete="off"
-											{...{ type: showApiKey ? 'text' : 'password' }}
-										/>
-										<button
-											class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-											on:click={(e) => {
-												e.preventDefault();
-												showApiKey = !showApiKey;
-											}}
-										>
-											{#if showApiKey}
-												<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>
-											{:else}
-												<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>
-											{/if}
-										</button>
-									</div>
-								</div>
+								<SensitiveInput placeholder={$i18n.t('Enter Brave Search API Key')} bind:value={webConfig.search.brave_search_api_key} />
 							</div>
 							</div>
 						{:else if webConfig.search.engine === 'serpstack'}
 						{:else if webConfig.search.engine === 'serpstack'}
 							<div>
 							<div>
@@ -247,56 +149,7 @@
 									{$i18n.t('Serpstack API Key')}
 									{$i18n.t('Serpstack API Key')}
 								</div>
 								</div>
 
 
-								<div class="flex w-full">
-									<div class="flex-1 flex">
-										<input
-											class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											placeholder={$i18n.t('Enter Serpstack API Key')}
-											bind:value={webConfig.search.serpstack_api_key}
-											autocomplete="off"
-											{...{ type: showApiKey ? 'text' : 'password' }}
-										/>
-										<button
-											class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-											on:click={(e) => {
-												e.preventDefault();
-												showApiKey = !showApiKey;
-											}}
-										>
-											{#if showApiKey}
-												<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>
-											{:else}
-												<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>
-											{/if}
-										</button>
-									</div>
-								</div>
+								<SensitiveInput placeholder={$i18n.t('Enter Serpstack API Key')} bind:value={webConfig.search.serpstack_api_key} />
 							</div>
 							</div>
 						{:else if webConfig.search.engine === 'serper'}
 						{:else if webConfig.search.engine === 'serper'}
 							<div>
 							<div>
@@ -304,56 +157,7 @@
 									{$i18n.t('Serper API Key')}
 									{$i18n.t('Serper API Key')}
 								</div>
 								</div>
 
 
-								<div class="flex w-full">
-									<div class="flex-1 flex">
-										<input
-											class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											placeholder={$i18n.t('Enter Serper API Key')}
-											bind:value={webConfig.search.serper_api_key}
-											autocomplete="off"
-											{...{ type: showApiKey ? 'text' : 'password' }}
-										/>
-										<button
-											class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-											on:click={(e) => {
-												e.preventDefault();
-												showApiKey = !showApiKey;
-											}}
-										>
-											{#if showApiKey}
-												<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>
-											{:else}
-												<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>
-											{/if}
-										</button>
-									</div>
-								</div>
+								<SensitiveInput placeholder={$i18n.t('Enter Serper API Key')} bind:value={webConfig.search.serper_api_key} />
 							</div>
 							</div>
 						{:else if webConfig.search.engine === 'serply'}
 						{:else if webConfig.search.engine === 'serply'}
 							<div>
 							<div>
@@ -361,56 +165,7 @@
 									{$i18n.t('Serply API Key')}
 									{$i18n.t('Serply API Key')}
 								</div>
 								</div>
 
 
-								<div class="flex w-full">
-									<div class="flex-1 flex">
-										<input
-											class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											placeholder={$i18n.t('Enter Serply API Key')}
-											bind:value={webConfig.search.serply_api_key}
-											autocomplete="off"
-											{...{ type: showApiKey ? 'text' : 'password' }}
-										/>
-										<button
-											class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-											on:click={(e) => {
-												e.preventDefault();
-												showApiKey = !showApiKey;
-											}}
-										>
-											{#if showApiKey}
-												<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>
-											{:else}
-												<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>
-											{/if}
-										</button>
-									</div>
-								</div>
+								<SensitiveInput placeholder={$i18n.t('Enter Serply API Key')} bind:value={webConfig.search.serply_api_key} />
 							</div>
 							</div>
 						{:else if webConfig.search.engine === 'tavily'}
 						{:else if webConfig.search.engine === 'tavily'}
 							<div>
 							<div>
@@ -418,56 +173,7 @@
 									{$i18n.t('Tavily API Key')}
 									{$i18n.t('Tavily API Key')}
 								</div>
 								</div>
 
 
-								<div class="flex w-full">
-									<div class="flex-1 flex">
-										<input
-											class="w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
-											placeholder={$i18n.t('Enter Tavily API Key')}
-											bind:value={webConfig.search.tavily_api_key}
-											autocomplete="off"
-											{...{ type: showApiKey ? 'text' : 'password' }}
-										/>
-										<button
-											class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-											on:click={(e) => {
-												e.preventDefault();
-												showApiKey = !showApiKey;
-											}}
-										>
-											{#if showApiKey}
-												<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>
-											{:else}
-												<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>
-											{/if}
-										</button>
-									</div>
-								</div>
+								<SensitiveInput placeholder={$i18n.t('Enter Tavily API Key')} bind:value={webConfig.search.tavily_api_key} />
 							</div>
 							</div>
 						{/if}
 						{/if}
 					</div>
 					</div>

+ 3 - 96
src/lib/components/chat/Settings/Account.svelte

@@ -11,6 +11,7 @@
 	import { copyToClipboard } from '$lib/utils';
 	import { copyToClipboard } from '$lib/utils';
 	import Plus from '$lib/components/icons/Plus.svelte';
 	import Plus from '$lib/components/icons/Plus.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
 	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
@@ -21,11 +22,9 @@
 
 
 	let showAPIKeys = false;
 	let showAPIKeys = false;
 
 
-	let showJWTToken = false;
 	let JWTTokenCopied = false;
 	let JWTTokenCopied = false;
 
 
 	let APIKey = '';
 	let APIKey = '';
-	let showAPIKey = false;
 	let APIKeyCopied = false;
 	let APIKeyCopied = false;
 
 
 	let profileImageInputElement: HTMLInputElement;
 	let profileImageInputElement: HTMLInputElement;
@@ -255,53 +254,7 @@
 					</div>
 					</div>
 
 
 					<div class="flex mt-2">
 					<div class="flex mt-2">
-						<div class="flex w-full">
-							<input
-								class="w-full rounded-l-lg py-1.5 pl-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-none"
-								type={showJWTToken ? 'text' : 'password'}
-								value={localStorage.token}
-								disabled
-							/>
-
-							<button
-								class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-								on:click={() => {
-									showJWTToken = !showJWTToken;
-								}}
-							>
-								{#if showJWTToken}
-									<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>
-								{:else}
-									<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>
-								{/if}
-							</button>
-						</div>
+						<SensitiveInput value={localStorage.token} readOnly={true} />
 
 
 						<button
 						<button
 							class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"
 							class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"
@@ -355,53 +308,7 @@
 
 
 					<div class="flex mt-2">
 					<div class="flex mt-2">
 						{#if APIKey}
 						{#if APIKey}
-							<div class="flex w-full">
-								<input
-									class="w-full rounded-l-lg py-1.5 pl-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-none"
-									type={showAPIKey ? 'text' : 'password'}
-									value={APIKey}
-									disabled
-								/>
-
-								<button
-									class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
-									on:click={() => {
-										showAPIKey = !showAPIKey;
-									}}
-								>
-									{#if showAPIKey}
-										<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>
-									{:else}
-										<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>
-									{/if}
-								</button>
-							</div>
+							<SensitiveInput value={APIKey} readOnly={true} />
 
 
 							<button
 							<button
 								class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"
 								class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"

+ 62 - 0
src/lib/components/common/SensitiveInput.svelte

@@ -0,0 +1,62 @@
+<script lang="ts">
+	export let value: string;
+	export let placeholder = "";
+	export let readOnly = false;
+	export let outerClassName = 'flex flex-1';
+	export let inputClassName =
+		'w-full rounded-l-lg py-2 pl-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none';
+	export let showButtonClassName = 'px-2 transition rounded-r-lg bg-white dark:bg-gray-850';
+
+	let show = false;
+</script>
+
+<div class={outerClassName}>
+	<input
+		class={inputClassName}
+		{placeholder}
+		bind:value
+		required={!readOnly}
+		disabled={readOnly}
+		autocomplete="off"
+		{...{ type: show ? 'text' : 'password' }}
+	/>
+	<button
+		class={showButtonClassName}
+		on:click={(e) => {
+			e.preventDefault();
+			show = !show;
+		}}
+	>
+		{#if show}
+			<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>
+		{:else}
+			<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>
+		{/if}
+	</button>
+</div>