"docs/starts/install.md" did not exist on "e646c5901e7910228e128861e39d8de16241afc1"
Images.svelte 13.8 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';
22
	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
Timothy J. Baek's avatar
Timothy J. Baek committed
23
24
	const dispatch = createEventDispatcher();

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

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

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

Timothy J. Baek's avatar
Timothy J. Baek committed
32
	let AUTOMATIC1111_BASE_URL = '';
33
	let AUTOMATIC1111_API_AUTH = '';
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
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
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
			const res = await updateImageGenerationEngineUrls(localStorage.token, {
sihuangwlp's avatar
sihuangwlp committed
79
				AUTOMATIC1111_BASE_URL: AUTOMATIC1111_BASE_URL,
80
				AUTOMATIC1111_API_AUTH: AUTOMATIC1111_API_AUTH
Timothy J. Baek's avatar
Timothy J. Baek committed
81
82
83
84
85
86
87
			}).catch((error) => {
				toast.error(error);
				return null;
			});

			if (res) {
				AUTOMATIC1111_BASE_URL = res.AUTOMATIC1111_BASE_URL;
88
				AUTOMATIC1111_API_AUTH = res.AUTOMATIC1111_API_AUTH;
Timothy J. Baek's avatar
Timothy J. Baek committed
89
90
91
92
93
94
95

				await getModels();

				if (models) {
					toast.success($i18n.t('Server connection verified'));
				}
			} else {
Timothy J. Baek's avatar
Timothy J. Baek committed
96
97
98
				({ AUTOMATIC1111_BASE_URL, AUTOMATIC1111_API_AUTH } = await getImageGenerationEngineUrls(
					localStorage.token
				));
Timothy J. Baek's avatar
Timothy J. Baek committed
99
			}
Timothy J. Baek's avatar
Timothy J. Baek committed
100
101
		}
	};
Timothy J. Baek's avatar
Timothy J. Baek committed
102
103
104
105
106
107
108
109
110
	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
111

Timothy J. Baek's avatar
Timothy J. Baek committed
112
113
114
115
116
117
118
119
		if (res) {
			imageGenerationEngine = res.engine;
			enableImageGeneration = res.enabled;
		}

		if (enableImageGeneration) {
			config.set(await getBackendConfig(localStorage.token));
			getModels();
120
		}
Timothy J. Baek's avatar
Timothy J. Baek committed
121
122
123
124
	};

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

			AUTOMATIC1111_BASE_URL = URLS.AUTOMATIC1111_BASE_URL;
137
			AUTOMATIC1111_API_AUTH = URLS.AUTOMATIC1111_API_AUTH;
Timothy J. Baek's avatar
Timothy J. Baek committed
138
139
			COMFYUI_BASE_URL = URLS.COMFYUI_BASE_URL;

140
141
142
143
			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
144

Timothy J. Baek's avatar
Timothy J. Baek committed
145
146
147
148
			imageSize = await getImageSize(localStorage.token);
			steps = await getImageSteps(localStorage.token);

			if (enableImageGeneration) {
Timothy J. Baek's avatar
Timothy J. Baek committed
149
				getModels();
Timothy J. Baek's avatar
Timothy J. Baek committed
150
151
152
153
154
155
156
157
158
			}
		}
	});
</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
159

160
		if (imageGenerationEngine === 'openai') {
161
			await updateOpenAIConfig(localStorage.token, OPENAI_API_BASE_URL, OPENAI_API_KEY);
162
		}
Timothy J. Baek's avatar
Timothy J. Baek committed
163

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

Timothy J. Baek's avatar
Timothy J. Baek committed
166
167
168
169
		await updateImageSize(localStorage.token, imageSize).catch((error) => {
			toast.error(error);
			return null;
		});
170
171
172
173
		await updateImageSteps(localStorage.token, steps).catch((error) => {
			toast.error(error);
			return null;
		});
Timothy J. Baek's avatar
Timothy J. Baek committed
174
175
176
177
178

		dispatch('save');
		loading = false;
	}}
>
Timothy J. Baek's avatar
Timothy J. Baek committed
179
	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden">
Timothy J. Baek's avatar
Timothy J. Baek committed
180
		<div>
181
			<div class=" mb-1 text-sm font-medium">{$i18n.t('Image Settings')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
182

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

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

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

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

Timothy J. Baek's avatar
Timothy J. Baek committed
238
		{#if imageGenerationEngine === ''}
239
			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Base URL')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
240
241
242
			<div class="flex w-full">
				<div class="flex-1 mr-2">
					<input
Timothy J. Baek's avatar
Timothy J. Baek committed
243
						class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
Ased Mammad's avatar
Ased Mammad committed
244
						placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
245
						bind:value={AUTOMATIC1111_BASE_URL}
Timothy J. Baek's avatar
Timothy J. Baek committed
246
					/>
Timothy J. Baek's avatar
Timothy J. Baek committed
247
248
				</div>
				<button
Timothy J. Baek's avatar
Timothy J. Baek committed
249
					class="px-2.5 bg-gray-50 hover:bg-gray-100 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
250
251
					type="button"
					on:click={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
252
						updateUrlHandler();
Timothy J. Baek's avatar
Timothy J. Baek committed
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
					}}
				>
					<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
268
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
269
270

			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
Ased Mammad's avatar
Ased Mammad committed
271
				{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
Timothy J. Baek's avatar
Timothy J. Baek committed
272
273
274
275
				<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
276
				>
Ased Mammad's avatar
Ased Mammad committed
277
					{$i18n.t('(e.g. `sh webui.sh --api`)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
278
279
				</a>
			</div>
280
281

			<div class=" mb-2.5 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Api Auth String')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
282
283
284
285
			<SensitiveInput
				placeholder={$i18n.t('Enter api auth string (e.g. username:password)')}
				bind:value={AUTOMATIC1111_API_AUTH}
			/>
286
287
288
289
290
291
292
293

			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
				{$i18n.t('Include `--api-auth` flag when running stable-diffusion-webui')}
				<a
					class=" text-gray-300 font-medium"
					href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/13993"
					target="_blank"
				>
Timothy J. Baek's avatar
Timothy J. Baek committed
294
					{$i18n.t('(e.g. `sh webui.sh --api --api-auth username_password`)').replace('_', ':')}
295
296
				</a>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
297
298
299
300
301
		{: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
Timothy J. Baek's avatar
Timothy J. Baek committed
302
						class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
Timothy J. Baek's avatar
Timothy J. Baek committed
303
304
305
306
307
						placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
						bind:value={COMFYUI_BASE_URL}
					/>
				</div>
				<button
Timothy J. Baek's avatar
Timothy J. Baek committed
308
					class="px-2.5 bg-gray-50 hover:bg-gray-100 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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
					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
328
		{:else if imageGenerationEngine === 'openai'}
329
330
331
332
333
			<div>
				<div class=" mb-1.5 text-sm font-medium">{$i18n.t('OpenAI API Config')}</div>

				<div class="flex gap-2 mb-1">
					<input
Timothy J. Baek's avatar
Timothy J. Baek committed
334
						class="flex-1 w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
335
336
337
338
339
						placeholder={$i18n.t('API Base URL')}
						bind:value={OPENAI_API_BASE_URL}
						required
					/>

340
					<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OPENAI_API_KEY} />
Timothy J. Baek's avatar
Timothy J. Baek committed
341
342
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
343
		{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
344
345

		{#if enableImageGeneration}
Timothy J. Baek's avatar
Timothy J. Baek committed
346
			<hr class=" dark:border-gray-850" />
Timothy J. Baek's avatar
Timothy J. Baek committed
347

Timothy J. Baek's avatar
Timothy J. Baek committed
348
			<div>
349
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
350
351
				<div class="flex w-full">
					<div class="flex-1 mr-2">
352
353
354
355
356
						{#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"
Timothy J. Baek's avatar
Timothy J. Baek committed
357
										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
358
359
360
361
362
363
364
365
366
367
368
369
370
										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
Timothy J. Baek's avatar
Timothy J. Baek committed
371
								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
372
373
374
375
376
377
378
379
380
381
382
383
384
								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
385
386
387
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
388
389

			<div>
390
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
391
392
393
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
394
							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
Ased Mammad's avatar
Ased Mammad committed
395
							placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
396
397
398
399
400
							bind:value={imageSize}
						/>
					</div>
				</div>
			</div>
401
402

			<div>
403
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
404
405
406
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
407
							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
Ased Mammad's avatar
Ased Mammad committed
408
							placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
409
410
411
412
413
							bind:value={steps}
						/>
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
414
415
416
417
418
		{/if}
	</div>

	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
Timothy J. Baek's avatar
Timothy J. Baek committed
419
			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
420
421
422
423
424
				? ' cursor-not-allowed'
				: ''}"
			type="submit"
			disabled={loading}
		>
Jannik Streidl's avatar
Jannik Streidl committed
425
			{$i18n.t('Save')}
Timothy J. Baek's avatar
Timothy J. Baek committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456

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