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
28
29
	export let saveSettings: Function;

	let loading = false;

Timothy J. Baek's avatar
Timothy J. Baek committed
30
	let imageGenerationEngine = '';
31
	let enableImageGeneration = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
32

Timothy J. Baek's avatar
Timothy J. Baek committed
33
	let AUTOMATIC1111_BASE_URL = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
34
35
	let COMFYUI_BASE_URL = '';

36
	let OPENAI_API_BASE_URL = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
37
	let OPENAI_API_KEY = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
38
39

	let selectedModel = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
40
	let models = null;
Timothy J. Baek's avatar
Timothy J. Baek committed
41

Timothy J. Baek's avatar
Timothy J. Baek committed
42
	let imageSize = '';
43
	let steps = 50;
Timothy J. Baek's avatar
Timothy J. Baek committed
44

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

Timothy J. Baek's avatar
Timothy J. Baek committed
55
56
57
58
59
	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
60
				toast.error(error);
Timothy J. Baek's avatar
Timothy J. Baek committed
61
62

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

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

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
69
				await getModels();
Timothy J. Baek's avatar
Timothy J. Baek committed
70

Timothy J. Baek's avatar
Timothy J. Baek committed
71
72
73
74
75
				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
76
			}
Timothy J. Baek's avatar
Timothy J. Baek committed
77
		} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
78
79
80
81
82
83
84
85
86
87
			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;

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
88
				await getModels();
Timothy J. Baek's avatar
Timothy J. Baek committed
89
90
91
92
93
94
95

				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
96
97
		}
	};
Timothy J. Baek's avatar
Timothy J. Baek committed
98
99
100
101
102
103
104
105
106
	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
107

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

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

	onMount(async () => {
		if ($user.role === 'admin') {
Timothy J. Baek's avatar
Timothy J. Baek committed
121
122
123
124
125
126
127
128
129
			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
130
131
132
133
134
			const URLS = await getImageGenerationEngineUrls(localStorage.token);

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

135
136
137
138
			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
139

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

			if (enableImageGeneration) {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
144
				getModels();
Timothy J. Baek's avatar
Timothy J. Baek committed
145
146
147
148
149
150
151
152
153
			}
		}
	});
</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
154

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

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

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

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

Timothy J. Baek's avatar
Timothy J. Baek committed
178
			<div class=" py-0.5 flex w-full justify-between">
Ased Mammad's avatar
Ased Mammad committed
179
				<div class=" self-center text-xs font-medium">{$i18n.t('Image Generation Engine')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
180
181
182
183
				<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
184
						placeholder={$i18n.t('Select a mode')}
Timothy J. Baek's avatar
Timothy J. Baek committed
185
186
187
188
						on:change={async () => {
							await updateImageGeneration();
						}}
					>
Ased Mammad's avatar
Ased Mammad committed
189
						<option value="">{$i18n.t('Default (Automatic1111)')}</option>
Timothy J. Baek's avatar
Timothy J. Baek committed
190
						<option value="comfyui">{$i18n.t('ComfyUI')}</option>
Ased Mammad's avatar
Ased Mammad committed
191
						<option value="openai">{$i18n.t('Open AI (Dall-E)')}</option>
Timothy J. Baek's avatar
Timothy J. Baek committed
192
193
194
195
					</select>
				</div>
			</div>

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

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

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

Timothy J. Baek's avatar
Timothy J. Baek committed
233
		{#if imageGenerationEngine === ''}
234
			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Base URL')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
235
236
237
			<div class="flex w-full">
				<div class="flex-1 mr-2">
					<input
Timothy J. Baek's avatar
Timothy J. Baek committed
238
						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
239
						placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
240
						bind:value={AUTOMATIC1111_BASE_URL}
Timothy J. Baek's avatar
Timothy J. Baek committed
241
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
242
243
				</div>
				<button
Timothy J. Baek's avatar
Timothy J. Baek committed
244
					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
245
246
					type="button"
					on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
247
						updateUrlHandler();
Timothy J. Baek's avatar
Timothy J. Baek committed
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
					}}
				>
					<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
263
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
264
265

			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
Ased Mammad's avatar
Ased Mammad committed
266
				{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
Timothy J. Baek's avatar
Timothy J. Baek committed
267
268
269
270
				<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
271
				>
Ased Mammad's avatar
Ased Mammad committed
272
					{$i18n.t('(e.g. `sh webui.sh --api`)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
273
274
				</a>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
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
304
305
		{: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
306
		{:else if imageGenerationEngine === 'openai'}
307
308
309
310
311
312
313
314
315
316
317
			<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
318
319
					<input
						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
320
						placeholder={$i18n.t('API Key')}
Timothy J. Baek's avatar
Timothy J. Baek committed
321
						bind:value={OPENAI_API_KEY}
322
						required
Timothy J. Baek's avatar
Timothy J. Baek committed
323
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
324
325
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
326
		{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
327
328
329
330

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

Timothy J. Baek's avatar
Timothy J. Baek committed
331
			<div>
332
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
333
334
				<div class="flex w-full">
					<div class="flex-1 mr-2">
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
366
367
						{#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
368
369
370
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
371
372

			<div>
373
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
374
375
376
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
377
							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
378
							placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
379
380
381
382
383
							bind:value={imageSize}
						/>
					</div>
				</div>
			</div>
384
385

			<div>
386
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
387
388
389
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
390
							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
391
							placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
392
393
394
395
396
							bind:value={steps}
						/>
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
397
398
399
400
401
		{/if}
	</div>

	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
Timothy J. Baek's avatar
Timothy J. Baek committed
402
			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
403
404
405
406
407
				? ' cursor-not-allowed'
				: ''}"
			type="submit"
			disabled={loading}
		>
Jannik Streidl's avatar
Jannik Streidl committed
408
			{$i18n.t('Save')}
Timothy J. Baek's avatar
Timothy J. Baek committed
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
438
439

			{#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>