General.svelte 16.1 KB
Newer Older
1
2
<script lang="ts">
	import { getDocs } from '$lib/apis/documents';
3
	import {
Timothy J. Baek's avatar
Timothy J. Baek committed
4
5
		getRAGConfig,
		updateRAGConfig,
6
		getQuerySettings,
7
		scanDocs,
Timothy J. Baek's avatar
Timothy J. Baek committed
8
		updateQuerySettings,
9
		resetVectorDB,
Timothy J. Baek's avatar
Timothy J. Baek committed
10
11
		getEmbeddingConfig,
		updateEmbeddingConfig
12
	} from '$lib/apis/rag';
Timothy J. Baek's avatar
Timothy J. Baek committed
13

Timothy J. Baek's avatar
Timothy J. Baek committed
14
	import { documents, models } from '$lib/stores';
15
	import { onMount, getContext } from 'svelte';
Jannik Streidl's avatar
Jannik Streidl committed
16
	import { toast } from 'svelte-sonner';
17

18
19
	import Tooltip from '$lib/components/common/Tooltip.svelte';

20
21
	const i18n = getContext('i18n');

22
	export let saveHandler: Function;
Timothy J. Baek's avatar
Timothy J. Baek committed
23

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
24
25
	let scanDirLoading = false;
	let updateEmbeddingModelLoading = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
26

Timothy J. Baek's avatar
Timothy J. Baek committed
27
28
	let showResetConfirm = false;

29
	let embeddingEngine = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
30
31
	let embeddingModel = '';

Timothy J. Baek's avatar
Timothy J. Baek committed
32
33
	let chunkSize = 0;
	let chunkOverlap = 0;
Timothy J. Baek's avatar
Timothy J. Baek committed
34
	let pdfExtractImages = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
35

36
37
38
39
	let querySettings = {
		template: '',
		k: 4
	};
40

41
	const scanHandler = async () => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
42
		scanDirLoading = true;
43
		const res = await scanDocs(localStorage.token);
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
44
		scanDirLoading = false;
45
46
47

		if (res) {
			await documents.set(await getDocs(localStorage.token));
48
			toast.success($i18n.t('Scan complete!'));
49
50
51
		}
	};

52
	const embeddingModelUpdateHandler = async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
53
54
55
56
57
58
59
60
61
62
		if (embeddingModel === '') {
			toast.error(
				$i18n.t(
					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
				)
			);
			return;
		}

		if (embeddingEngine === '' && embeddingModel.split('/').length - 1 > 1) {
Self Denial's avatar
Self Denial committed
63
64
65
66
67
			toast.error(
				$i18n.t(
					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
				)
			);
68
69
70
			return;
		}

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
71
		console.log('Update embedding model attempt:', embeddingModel);
72

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
73
		updateEmbeddingModelLoading = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
74
75
		const res = await updateEmbeddingConfig(localStorage.token, {
			embedding_engine: embeddingEngine,
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
76
			embedding_model: embeddingModel
Timothy J. Baek's avatar
Timothy J. Baek committed
77
		}).catch(async (error) => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
78
			toast.error(error);
Timothy J. Baek's avatar
Timothy J. Baek committed
79
80
81
82
83
84

			const embeddingConfig = await getEmbeddingConfig(localStorage.token);
			if (embeddingConfig) {
				embeddingEngine = embeddingConfig.embedding_engine;
				embeddingModel = embeddingConfig.embedding_model;
			}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
85
86
			return null;
		});
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
87
		updateEmbeddingModelLoading = false;
88
89
90

		if (res) {
			console.log('embeddingModelUpdateHandler:', res);
91
			if (res.status === true) {
Self Denial's avatar
Self Denial committed
92
93
94
				toast.success($i18n.t('Model {{embedding_model}} update complete!', res), {
					duration: 1000 * 10
				});
95
96
97
98
			}
		}
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
99
	const submitHandler = async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
100
101
102
103
104
105
106
		const res = await updateRAGConfig(localStorage.token, {
			pdf_extract_images: pdfExtractImages,
			chunk: {
				chunk_overlap: chunkOverlap,
				chunk_size: chunkSize
			}
		});
107
		querySettings = await updateQuerySettings(localStorage.token, querySettings);
Timothy J. Baek's avatar
Timothy J. Baek committed
108
109
110
	};

	onMount(async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
111
		const res = await getRAGConfig(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
112
113

		if (res) {
Timothy J. Baek's avatar
Timothy J. Baek committed
114
115
116
117
			pdfExtractImages = res.pdf_extract_images;

			chunkSize = res.chunk.chunk_size;
			chunkOverlap = res.chunk.chunk_overlap;
Timothy J. Baek's avatar
Timothy J. Baek committed
118
		}
119

Timothy J. Baek's avatar
Timothy J. Baek committed
120
121
122
123
124
125
		const embeddingConfig = await getEmbeddingConfig(localStorage.token);

		if (embeddingConfig) {
			embeddingEngine = embeddingConfig.embedding_engine;
			embeddingModel = embeddingConfig.embedding_model;
		}
126

127
		querySettings = await getQuerySettings(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
128
	});
129
130
131
132
133
</script>

<form
	class="flex flex-col h-full justify-between space-y-3 text-sm"
	on:submit|preventDefault={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
134
		submitHandler();
135
136
137
		saveHandler();
	}}
>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
138
	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
139
		<div>
140
			<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
141

142
143
144
145
146
147
148
			<div class=" flex w-full justify-between">
				<div class=" self-center text-xs font-medium">{$i18n.t('Embedding 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={embeddingEngine}
						placeholder="Select an embedding engine"
Timothy J. Baek's avatar
Timothy J. Baek committed
149
150
151
						on:change={() => {
							embeddingModel = '';
						}}
152
153
154
155
					>
						<option value="">{$i18n.t('Default (SentenceTransformer)')}</option>
						<option value="ollama">{$i18n.t('Ollama')}</option>
					</select>
156
				</div>
157
			</div>
158
		</div>
159

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
160
161
		<div class="space-y-2">
			<div>
Timothy J. Baek's avatar
Timothy J. Baek committed
162
163
				<div class=" mb-2 text-sm font-medium">{$i18n.t('Update Embedding Model')}</div>

164
				{#if embeddingEngine === 'ollama'}
Timothy J. Baek's avatar
Timothy J. Baek committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
					<div class="flex w-full">
						<div class="flex-1 mr-2">
							<select
								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
								bind:value={embeddingModel}
								placeholder={$i18n.t('Select a model')}
								required
							>
								{#if !embeddingModel}
									<option value="" disabled selected>{$i18n.t('Select a model')}</option>
								{/if}
								{#each $models.filter((m) => m.id && !m.external) as model}
									<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
										>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
									>
								{/each}
							</select>
						</div>
						<button
							class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
							on:click={() => {
								embeddingModelUpdateHandler();
							}}
							disabled={updateEmbeddingModelLoading}
						>
							{#if updateEmbeddingModelLoading}
								<div class="self-center">
									<svg
										class=" w-4 h-4"
										viewBox="0 0 24 24"
										fill="currentColor"
										xmlns="http://www.w3.org/2000/svg"
										><style>
											.spinner_ajPY {
												transform-origin: center;
												animation: spinner_AtaB 0.75s infinite linear;
											}
											@keyframes spinner_AtaB {
												100% {
													transform: rotate(360deg);
												}
											}
										</style><path
											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
											opacity=".25"
										/><path
											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
											class="spinner_ajPY"
										/></svg
									>
								</div>
							{: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="M12.416 3.376a.75.75 0 0 1 .208 1.04l-5 7.5a.75.75 0 0 1-1.154.114l-3-3a.75.75 0 0 1 1.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 0 1 1.04-.207Z"
										clip-rule="evenodd"
									/>
								</svg>
							{/if}
						</button>
					</div>
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
				{:else}
					<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('Update embedding model (e.g. {{model}})', {
									model: embeddingModel.slice(-40)
								})}
								bind:value={embeddingModel}
							/>
						</div>
						<button
							class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
							on:click={() => {
								embeddingModelUpdateHandler();
							}}
							disabled={updateEmbeddingModelLoading}
						>
							{#if updateEmbeddingModelLoading}
								<div class="self-center">
									<svg
										class=" w-4 h-4"
										viewBox="0 0 24 24"
										fill="currentColor"
										xmlns="http://www.w3.org/2000/svg"
										><style>
											.spinner_ajPY {
												transform-origin: center;
												animation: spinner_AtaB 0.75s infinite linear;
											}
											@keyframes spinner_AtaB {
												100% {
													transform: rotate(360deg);
												}
											}
										</style><path
											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
											opacity=".25"
										/><path
											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
											class="spinner_ajPY"
										/></svg
									>
								</div>
							{:else}
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 16 16"
									fill="currentColor"
									class="w-4 h-4"
								>
									<path
										d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
									/>
									<path
										d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
									/>
								</svg>
							{/if}
						</button>
292
					</div>
293
294
				{/if}

Timothy J. Baek's avatar
Timothy J. Baek committed
295
296
297
298
299
300
				<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
					{$i18n.t(
						'Warning: If you update or change your embedding model, you will need to re-import all documents.'
					)}
				</div>

301
302
303
304
305
306
307
				<hr class=" dark:border-gray-700 my-3" />

				<div class="  flex w-full justify-between">
					<div class=" self-center text-xs font-medium">
						{$i18n.t('Scan for documents from {{path}}', { path: '/data/docs' })}
					</div>

308
					<button
309
310
311
						class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded-lg flex flex-row space-x-1 items-center {scanDirLoading
							? ' cursor-not-allowed'
							: ''}"
312
						on:click={() => {
313
314
							scanHandler();
							console.log('check');
315
						}}
316
317
						type="button"
						disabled={scanDirLoading}
318
					>
319
320
321
322
						<div class="self-center font-medium">{$i18n.t('Scan')}</div>

						{#if scanDirLoading}
							<div class="ml-3 self-center">
323
								<svg
324
									class=" w-3 h-3"
325
326
327
328
329
330
331
									viewBox="0 0 24 24"
									fill="currentColor"
									xmlns="http://www.w3.org/2000/svg"
									><style>
										.spinner_ajPY {
											transform-origin: center;
											animation: spinner_AtaB 0.75s infinite linear;
332
										}
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
										@keyframes spinner_AtaB {
											100% {
												transform: rotate(360deg);
											}
										}
									</style><path
										d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
										opacity=".25"
									/><path
										d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
										class="spinner_ajPY"
									/></svg
								>
							</div>
						{/if}
					</button>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
349
				</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
350

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
351
				<hr class=" dark:border-gray-700 my-3" />
Timothy J. Baek's avatar
Timothy J. Baek committed
352

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
353
354
				<div class=" ">
					<div class=" text-sm font-medium">{$i18n.t('Chunk Params')}</div>
355

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
356
357
358
					<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>
359

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
360
361
							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
362
									class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
363
364
365
366
367
368
369
370
									type="number"
									placeholder={$i18n.t('Enter Chunk Size')}
									bind:value={chunkSize}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
371

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
372
373
374
375
						<div class="flex w-full">
							<div class=" self-center text-xs font-medium min-w-fit">
								{$i18n.t('Chunk Overlap')}
							</div>
376

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
377
378
							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
379
									class="w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
380
381
382
383
384
385
386
387
388
									type="number"
									placeholder={$i18n.t('Enter Chunk Overlap')}
									bind:value={chunkOverlap}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
					</div>
389

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
390
					<div class="pr-2">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
391
392
393
394
395
396
397
398
399
400
401
						<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>
402
403
404
					</div>
				</div>

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
405
406
				<hr class=" dark:border-gray-700 my-3" />

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
407
408
409
410
411
412
413
414
415
				<div>
					<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 flex-1">{$i18n.t('Top K')}</div>

							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
416
									class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
417
418
419
420
421
422
423
424
425
									type="number"
									placeholder={$i18n.t('Enter Top K')}
									bind:value={querySettings.k}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
426

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
427
428
429
430
					<div>
						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
						<textarea
							bind:value={querySettings.template}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
431
							class="w-full rounded-lg px-4 py-3 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
432
							rows="4"
Timothy J. Baek's avatar
Timothy J. Baek committed
433
						/>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
434
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
435
436
				</div>

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
437
				<hr class=" dark:border-gray-700 my-3" />
Timothy J. Baek's avatar
Timothy J. Baek committed
438

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
				{#if showResetConfirm}
					<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
						<div class="flex items-center space-x-3">
							<svg
								xmlns="http://www.w3.org/2000/svg"
								viewBox="0 0 16 16"
								fill="currentColor"
								class="w-4 h-4"
							>
								<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
								<path
									fill-rule="evenodd"
									d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM5.72 7.47a.75.75 0 0 1 1.06 0L8 8.69l1.22-1.22a.75.75 0 1 1 1.06 1.06L9.06 9.75l1.22 1.22a.75.75 0 1 1-1.06 1.06L8 10.81l-1.22 1.22a.75.75 0 0 1-1.06-1.06l1.22-1.22-1.22-1.22a.75.75 0 0 1 0-1.06Z"
									clip-rule="evenodd"
								/>
							</svg>
							<span>{$i18n.t('Are you sure?')}</span>
						</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
457

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
						<div class="flex space-x-1.5 items-center">
							<button
								class="hover:text-white transition"
								on:click={() => {
									const res = resetVectorDB(localStorage.token).catch((error) => {
										toast.error(error);
										return null;
									});

									if (res) {
										toast.success($i18n.t('Success'));
									}

									showResetConfirm = false;
								}}
							>
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 20 20"
									fill="currentColor"
									class="w-4 h-4"
								>
									<path
										fill-rule="evenodd"
										d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
										clip-rule="evenodd"
									/>
								</svg>
							</button>
							<button
								class="hover:text-white transition"
								on:click={() => {
									showResetConfirm = false;
								}}
							>
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 20 20"
									fill="currentColor"
									class="w-4 h-4"
								>
									<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>
				{:else}
Timothy J. Baek's avatar
Timothy J. Baek committed
507
					<button
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
508
						class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
Timothy J. Baek's avatar
Timothy J. Baek committed
509
						on:click={() => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
510
							showResetConfirm = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
511
512
						}}
					>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
						<div class=" self-center mr-3">
							<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.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 12.5 4H9.621a1.5 1.5 0 0 1-1.06-.44L7.439 2.44A1.5 1.5 0 0 0 6.38 2H3.5Zm6.75 7.75a.75.75 0 0 0 0-1.5h-4.5a.75.75 0 0 0 0 1.5h4.5Z"
									clip-rule="evenodd"
								/>
							</svg>
						</div>
						<div class=" self-center text-sm font-medium">{$i18n.t('Reset Vector Storage')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
528
					</button>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
529
				{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
530
			</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
531
		</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
532
	</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
533
534
535
536
537
538
539
540
	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
			class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
			type="submit"
		>
			{$i18n.t('Save')}
		</button>
	</div>
541
</form>