Images.svelte 12.9 KB
Newer Older
Timothy J. Baek's avatar
Timothy J. Baek committed
1
<script lang="ts">
Jannik Streidl's avatar
Jannik Streidl committed
2
	import { toast } from 'svelte-sonner';
Timothy J. Baek's avatar
Timothy J. Baek committed
3

4
	import { createEventDispatcher, onMount, getContext } from 'svelte';
5
	import { config, user } from '$lib/stores';
Timothy J. Baek's avatar
Timothy J. Baek committed
6
	import {
Timothy J. Baek's avatar
Timothy J. Baek committed
7
8
9
		getImageGenerationModels,
		getDefaultImageGenerationModel,
		updateDefaultImageGenerationModel,
Timothy J. Baek's avatar
Timothy J. Baek committed
10
		getImageSize,
Timothy J. Baek's avatar
Timothy J. Baek committed
11
12
		getImageGenerationConfig,
		updateImageGenerationConfig,
Timothy J. Baek's avatar
Timothy J. Baek committed
13
14
		getImageGenerationEngineUrls,
		updateImageGenerationEngineUrls,
15
		updateImageSize,
16
		getImageSteps,
Timothy J. Baek's avatar
Timothy J. Baek committed
17
		updateImageSteps,
18
19
		getOpenAIConfig,
		updateOpenAIConfig
Timothy J. Baek's avatar
Timothy J. Baek committed
20
	} from '$lib/apis/images';
21
	import { getBackendConfig } from '$lib/apis';
Timothy J. Baek's avatar
Timothy J. Baek committed
22
23
	const dispatch = createEventDispatcher();

24
25
	const i18n = getContext('i18n');

Timothy J. Baek's avatar
Timothy J. Baek committed
26
27
	let loading = false;

Timothy J. Baek's avatar
Timothy J. Baek committed
28
	let imageGenerationEngine = '';
29
	let enableImageGeneration = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
30

Timothy J. Baek's avatar
Timothy J. Baek committed
31
	let AUTOMATIC1111_BASE_URL = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
32
33
	let COMFYUI_BASE_URL = '';

34
	let OPENAI_API_BASE_URL = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
35
	let OPENAI_API_KEY = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
36
37

	let selectedModel = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
38
	let models = null;
Timothy J. Baek's avatar
Timothy J. Baek committed
39

Timothy J. Baek's avatar
Timothy J. Baek committed
40
	let imageSize = '';
41
	let steps = 50;
Timothy J. Baek's avatar
Timothy J. Baek committed
42

Timothy J. Baek's avatar
Timothy J. Baek committed
43
	const getModels = async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
44
		models = await getImageGenerationModels(localStorage.token).catch((error) => {
Timothy J. Baek's avatar
Timothy J. Baek committed
45
			toast.error(error);
Timothy J. Baek's avatar
Timothy J. Baek committed
46
			return null;
Timothy J. Baek's avatar
Timothy J. Baek committed
47
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
48
		selectedModel = await getDefaultImageGenerationModel(localStorage.token).catch((error) => {
Timothy J. Baek's avatar
Timothy J. Baek committed
49
			return '';
Timothy J. Baek's avatar
Timothy J. Baek committed
50
51
52
		});
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
53
54
55
56
57
	const updateUrlHandler = async () => {
		if (imageGenerationEngine === 'comfyui') {
			const res = await updateImageGenerationEngineUrls(localStorage.token, {
				COMFYUI_BASE_URL: COMFYUI_BASE_URL
			}).catch((error) => {
Timothy J. Baek's avatar
Timothy J. Baek committed
58
				toast.error(error);
Timothy J. Baek's avatar
Timothy J. Baek committed
59
60

				console.log(error);
Timothy J. Baek's avatar
Timothy J. Baek committed
61
				return null;
Timothy J. Baek's avatar
Timothy J. Baek committed
62
			});
Timothy J. Baek's avatar
Timothy J. Baek committed
63

Timothy J. Baek's avatar
Timothy J. Baek committed
64
65
			if (res) {
				COMFYUI_BASE_URL = res.COMFYUI_BASE_URL;
Timothy J. Baek's avatar
Timothy J. Baek committed
66

Timothy J. Baek's avatar
Timothy J. Baek committed
67
				await getModels();
Timothy J. Baek's avatar
Timothy J. Baek committed
68

Timothy J. Baek's avatar
Timothy J. Baek committed
69
70
71
72
73
				if (models) {
					toast.success($i18n.t('Server connection verified'));
				}
			} else {
				({ COMFYUI_BASE_URL } = await getImageGenerationEngineUrls(localStorage.token));
Timothy J. Baek's avatar
Timothy J. Baek committed
74
			}
Timothy J. Baek's avatar
Timothy J. Baek committed
75
		} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
			const res = await updateImageGenerationEngineUrls(localStorage.token, {
				AUTOMATIC1111_BASE_URL: AUTOMATIC1111_BASE_URL
			}).catch((error) => {
				toast.error(error);
				return null;
			});

			if (res) {
				AUTOMATIC1111_BASE_URL = res.AUTOMATIC1111_BASE_URL;

				await getModels();

				if (models) {
					toast.success($i18n.t('Server connection verified'));
				}
			} else {
				({ AUTOMATIC1111_BASE_URL } = await getImageGenerationEngineUrls(localStorage.token));
			}
Timothy J. Baek's avatar
Timothy J. Baek committed
94
95
		}
	};
Timothy J. Baek's avatar
Timothy J. Baek committed
96
97
98
99
100
101
102
103
104
	const updateImageGeneration = async () => {
		const res = await updateImageGenerationConfig(
			localStorage.token,
			imageGenerationEngine,
			enableImageGeneration
		).catch((error) => {
			toast.error(error);
			return null;
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
105

Timothy J. Baek's avatar
Timothy J. Baek committed
106
107
108
109
110
111
112
113
		if (res) {
			imageGenerationEngine = res.engine;
			enableImageGeneration = res.enabled;
		}

		if (enableImageGeneration) {
			config.set(await getBackendConfig(localStorage.token));
			getModels();
114
		}
Timothy J. Baek's avatar
Timothy J. Baek committed
115
116
117
118
	};

	onMount(async () => {
		if ($user.role === 'admin') {
Timothy J. Baek's avatar
Timothy J. Baek committed
119
120
121
122
123
124
125
126
127
			const res = await getImageGenerationConfig(localStorage.token).catch((error) => {
				toast.error(error);
				return null;
			});

			if (res) {
				imageGenerationEngine = res.engine;
				enableImageGeneration = res.enabled;
			}
Timothy J. Baek's avatar
Timothy J. Baek committed
128
129
130
131
132
			const URLS = await getImageGenerationEngineUrls(localStorage.token);

			AUTOMATIC1111_BASE_URL = URLS.AUTOMATIC1111_BASE_URL;
			COMFYUI_BASE_URL = URLS.COMFYUI_BASE_URL;

133
134
135
136
			const config = await getOpenAIConfig(localStorage.token);

			OPENAI_API_KEY = config.OPENAI_API_KEY;
			OPENAI_API_BASE_URL = config.OPENAI_API_BASE_URL;
Timothy J. Baek's avatar
Timothy J. Baek committed
137

Timothy J. Baek's avatar
Timothy J. Baek committed
138
139
140
141
			imageSize = await getImageSize(localStorage.token);
			steps = await getImageSteps(localStorage.token);

			if (enableImageGeneration) {
Timothy J. Baek's avatar
Timothy J. Baek committed
142
				getModels();
Timothy J. Baek's avatar
Timothy J. Baek committed
143
144
145
146
147
148
149
150
151
			}
		}
	});
</script>

<form
	class="flex flex-col h-full justify-between space-y-3 text-sm"
	on:submit|preventDefault={async () => {
		loading = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
152

153
		if (imageGenerationEngine === 'openai') {
154
			await updateOpenAIConfig(localStorage.token, OPENAI_API_BASE_URL, OPENAI_API_KEY);
155
		}
Timothy J. Baek's avatar
Timothy J. Baek committed
156

Timothy J. Baek's avatar
Timothy J. Baek committed
157
		await updateDefaultImageGenerationModel(localStorage.token, selectedModel);
158

Timothy J. Baek's avatar
Timothy J. Baek committed
159
160
161
162
		await updateImageSize(localStorage.token, imageSize).catch((error) => {
			toast.error(error);
			return null;
		});
163
164
165
166
		await updateImageSteps(localStorage.token, steps).catch((error) => {
			toast.error(error);
			return null;
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
167
168
169
170
171

		dispatch('save');
		loading = false;
	}}
>
Timothy J. Baek's avatar
Timothy J. Baek committed
172
	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[24rem]">
Timothy J. Baek's avatar
Timothy J. Baek committed
173
		<div>
174
			<div class=" mb-1 text-sm font-medium">{$i18n.t('Image Settings')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
175

Timothy J. Baek's avatar
Timothy J. Baek committed
176
			<div class=" py-0.5 flex w-full justify-between">
Ased Mammad's avatar
Ased Mammad committed
177
				<div class=" self-center text-xs font-medium">{$i18n.t('Image Generation Engine')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
178
179
180
181
				<div class="flex items-center relative">
					<select
						class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
						bind:value={imageGenerationEngine}
Ased Mammad's avatar
Ased Mammad committed
182
						placeholder={$i18n.t('Select a mode')}
Timothy J. Baek's avatar
Timothy J. Baek committed
183
184
185
186
						on:change={async () => {
							await updateImageGeneration();
						}}
					>
Ased Mammad's avatar
Ased Mammad committed
187
						<option value="">{$i18n.t('Default (Automatic1111)')}</option>
Timothy J. Baek's avatar
Timothy J. Baek committed
188
						<option value="comfyui">{$i18n.t('ComfyUI')}</option>
Ased Mammad's avatar
Ased Mammad committed
189
						<option value="openai">{$i18n.t('Open AI (Dall-E)')}</option>
Timothy J. Baek's avatar
Timothy J. Baek committed
190
191
192
193
					</select>
				</div>
			</div>

Timothy J. Baek's avatar
Timothy J. Baek committed
194
195
			<div>
				<div class=" py-0.5 flex w-full justify-between">
196
197
198
					<div class=" self-center text-xs font-medium">
						{$i18n.t('Image Generation (Experimental)')}
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
199
200
201
202

					<button
						class="p-1 px-3 text-xs flex rounded transition"
						on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
203
							if (imageGenerationEngine === '' && AUTOMATIC1111_BASE_URL === '') {
Ased Mammad's avatar
Ased Mammad committed
204
								toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
Timothy J. Baek's avatar
Timothy J. Baek committed
205
								enableImageGeneration = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
206
207
208
							} else if (imageGenerationEngine === 'comfyui' && COMFYUI_BASE_URL === '') {
								toast.error($i18n.t('ComfyUI Base URL is required.'));
								enableImageGeneration = false;
209
							} else if (imageGenerationEngine === 'openai' && OPENAI_API_KEY === '') {
Ased Mammad's avatar
Ased Mammad committed
210
								toast.error($i18n.t('OpenAI API Key is required.'));
211
								enableImageGeneration = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
212
213
214
							} else {
								enableImageGeneration = !enableImageGeneration;
							}
Timothy J. Baek's avatar
Timothy J. Baek committed
215
216

							updateImageGeneration();
Timothy J. Baek's avatar
Timothy J. Baek committed
217
218
219
220
						}}
						type="button"
					>
						{#if enableImageGeneration === true}
221
							<span class="ml-2 self-center">{$i18n.t('On')}</span>
Timothy J. Baek's avatar
Timothy J. Baek committed
222
						{:else}
223
							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
Timothy J. Baek's avatar
Timothy J. Baek committed
224
225
226
227
228
229
230
						{/if}
					</button>
				</div>
			</div>
		</div>
		<hr class=" dark:border-gray-700" />

Timothy J. Baek's avatar
Timothy J. Baek committed
231
		{#if imageGenerationEngine === ''}
232
			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Base URL')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
233
234
235
			<div class="flex w-full">
				<div class="flex-1 mr-2">
					<input
Timothy J. Baek's avatar
Timothy J. Baek committed
236
						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Ased Mammad's avatar
Ased Mammad committed
237
						placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
238
						bind:value={AUTOMATIC1111_BASE_URL}
Timothy J. Baek's avatar
Timothy J. Baek committed
239
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
240
241
				</div>
				<button
Timothy J. Baek's avatar
Timothy J. Baek committed
242
					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"
Timothy J. Baek's avatar
Timothy J. Baek committed
243
244
					type="button"
					on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
245
						updateUrlHandler();
Timothy J. Baek's avatar
Timothy J. Baek committed
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
					}}
				>
					<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="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
							clip-rule="evenodd"
						/>
					</svg>
				</button>
Timothy J. Baek's avatar
Timothy J. Baek committed
261
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
262
263

			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
Ased Mammad's avatar
Ased Mammad committed
264
				{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
Timothy J. Baek's avatar
Timothy J. Baek committed
265
266
267
268
				<a
					class=" text-gray-300 font-medium"
					href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/3734"
					target="_blank"
Timothy J. Baek's avatar
Timothy J. Baek committed
269
				>
Ased Mammad's avatar
Ased Mammad committed
270
					{$i18n.t('(e.g. `sh webui.sh --api`)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
271
272
				</a>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
		{:else if imageGenerationEngine === 'comfyui'}
			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('ComfyUI Base URL')}</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 URL (e.g. http://127.0.0.1:7860/)')}
						bind:value={COMFYUI_BASE_URL}
					/>
				</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"
					type="button"
					on:click={() => {
						updateUrlHandler();
					}}
				>
					<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="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
							clip-rule="evenodd"
						/>
					</svg>
				</button>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
304
		{:else if imageGenerationEngine === 'openai'}
305
306
307
308
309
310
311
312
313
314
315
			<div>
				<div class=" mb-1.5 text-sm font-medium">{$i18n.t('OpenAI API Config')}</div>

				<div class="flex gap-2 mb-1">
					<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 Base URL')}
						bind:value={OPENAI_API_BASE_URL}
						required
					/>

Timothy J. Baek's avatar
Timothy J. Baek committed
316
317
					<input
						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
318
						placeholder={$i18n.t('API Key')}
Timothy J. Baek's avatar
Timothy J. Baek committed
319
						bind:value={OPENAI_API_KEY}
320
						required
Timothy J. Baek's avatar
Timothy J. Baek committed
321
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
322
323
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
324
		{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
325
326
327
328

		{#if enableImageGeneration}
			<hr class=" dark:border-gray-700" />

Timothy J. Baek's avatar
Timothy J. Baek committed
329
			<div>
330
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
331
332
				<div class="flex w-full">
					<div class="flex-1 mr-2">
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
						{#if imageGenerationEngine === 'openai' && !OPENAI_API_BASE_URL.includes('https://api.openai.com')}
							<div class="flex w-full">
								<div class="flex-1">
									<input
										list="model-list"
										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
										bind:value={selectedModel}
										placeholder="Select a model"
									/>

									<datalist id="model-list">
										{#each models ?? [] as model}
											<option value={model.id}>{model.name}</option>
										{/each}
									</datalist>
								</div>
							</div>
						{:else}
							<select
								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
								bind:value={selectedModel}
								placeholder={$i18n.t('Select a model')}
								required
							>
								{#if !selectedModel}
									<option value="" disabled selected>{$i18n.t('Select a model')}</option>
								{/if}
								{#each models ?? [] as model}
									<option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option
									>
								{/each}
							</select>
						{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
366
367
368
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
369
370

			<div>
371
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
372
373
374
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
375
							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Ased Mammad's avatar
Ased Mammad committed
376
							placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
377
378
379
380
381
							bind:value={imageSize}
						/>
					</div>
				</div>
			</div>
382
383

			<div>
384
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
385
386
387
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
388
							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Ased Mammad's avatar
Ased Mammad committed
389
							placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
390
391
392
393
394
							bind:value={steps}
						/>
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
395
396
397
398
399
		{/if}
	</div>

	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
Timothy J. Baek's avatar
Timothy J. Baek committed
400
			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg flex flex-row space-x-1 items-center {loading
Timothy J. Baek's avatar
Timothy J. Baek committed
401
402
403
404
405
				? ' cursor-not-allowed'
				: ''}"
			type="submit"
			disabled={loading}
		>
Jannik Streidl's avatar
Jannik Streidl committed
406
			{$i18n.t('Save')}
Timothy J. Baek's avatar
Timothy J. Baek committed
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437

			{#if loading}
				<div class="ml-2 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>
			{/if}
		</button>
	</div>
</form>