Images.svelte 15.5 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 = '';
32
	let AUTOMATIC1111_API_AUTH = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
33
34
	let COMFYUI_BASE_URL = '';

35
	let OPENAI_API_BASE_URL = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
36
	let OPENAI_API_KEY = '';
37
	let showOpenAIKey = false;
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 {
96
				({ AUTOMATIC1111_BASE_URL,AUTOMATIC1111_API_AUTH } = await getImageGenerationEngineUrls(localStorage.token));
Timothy J. Baek's avatar
Timothy J. Baek committed
97
			}
Timothy J. Baek's avatar
Timothy J. Baek committed
98
99
		}
	};
Timothy J. Baek's avatar
Timothy J. Baek committed
100
101
102
103
104
105
106
107
108
	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
109

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

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

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

			AUTOMATIC1111_BASE_URL = URLS.AUTOMATIC1111_BASE_URL;
135
			AUTOMATIC1111_API_AUTH = URLS.AUTOMATIC1111_API_AUTH;
Timothy J. Baek's avatar
Timothy J. Baek committed
136
137
			COMFYUI_BASE_URL = URLS.COMFYUI_BASE_URL;

138
139
140
141
			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
142

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

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

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

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

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

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

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

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

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

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

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

			<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
Ased Mammad's avatar
Ased Mammad committed
269
				{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
Timothy J. Baek's avatar
Timothy J. Baek committed
270
271
272
273
				<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
274
				>
Ased Mammad's avatar
Ased Mammad committed
275
					{$i18n.t('(e.g. `sh webui.sh --api`)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
276
277
				</a>
			</div>
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296

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

			<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"
				>
sihuangwlp's avatar
sihuangwlp committed
297
                    {$i18n.t('(e.g. `sh webui.sh --api --api-auth username_password`)').replace('_',':')}
298
299
				</a>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
		{: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
331
		{:else if imageGenerationEngine === 'openai'}
332
333
334
335
336
			<div>
				<div class=" mb-1.5 text-sm font-medium">{$i18n.t('OpenAI API Config')}</div>

				<div class="flex gap-2 mb-1">
					<input
337
						class="flex-1 w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
338
339
340
341
342
						placeholder={$i18n.t('API Base URL')}
						bind:value={OPENAI_API_BASE_URL}
						required
					/>

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
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
					<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>
Timothy J. Baek's avatar
Timothy J. Baek committed
391
392
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
393
		{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
394
395

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

Timothy J. Baek's avatar
Timothy J. Baek committed
398
			<div>
399
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
400
401
				<div class="flex w-full">
					<div class="flex-1 mr-2">
402
403
404
405
406
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
						{#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
435
436
437
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
438
439

			<div>
440
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
441
442
443
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
444
							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
445
							placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
Timothy J. Baek's avatar
Timothy J. Baek committed
446
447
448
449
450
							bind:value={imageSize}
						/>
					</div>
				</div>
			</div>
451
452

			<div>
453
				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
454
455
456
				<div class="flex w-full">
					<div class="flex-1 mr-2">
						<input
Timothy J. Baek's avatar
Timothy J. Baek committed
457
							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
458
							placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
459
460
461
462
463
							bind:value={steps}
						/>
					</div>
				</div>
			</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
464
465
466
467
468
		{/if}
	</div>

	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
Timothy J. Baek's avatar
Timothy J. Baek committed
469
			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
470
471
472
473
474
				? ' cursor-not-allowed'
				: ''}"
			type="submit"
			disabled={loading}
		>
Jannik Streidl's avatar
Jannik Streidl committed
475
			{$i18n.t('Save')}
Timothy J. Baek's avatar
Timothy J. Baek committed
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

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