lottis_imagen/web/src/routes/+page.svelte

219 lines
5.4 KiB
Svelte

<script lang="ts">
import { ProgressRadial, getToastStore, RadioGroup, RadioItem } from '@skeletonlabs/skeleton';
let inputText = '';
let imageUrl = '';
const models = [
{
name: "Playground v2.5",
value: "playground-v2.5",
},
{
name: "Dall-E 3",
value: "dall-e-3",
},
{
name: "Stable Diffusion 3",
value: "stable-diffusion-3",
},
{
name: "DreamShaper XL Turbo",
value: "dreamshaper-xl-turbo"
},
{
name: "RealVis XL 4",
value: "realvisxl-v4",
},
{
name: "CyberRealistic",
value: "cyberrealistic-v3.3",
}
]
let currentModel = models[0].value;
let generateFutureAbortController = new AbortController();
let generateFutureAbortSignal = generateFutureAbortController.signal;
function resetAbortController() {
generateFutureAbortController = new AbortController();
generateFutureAbortSignal = generateFutureAbortController.signal;
}
function promiseState(p) {
const t = {};
return Promise.race([p, t]).then(
(v) => (v === t ? 'pending' : 'fulfilled'),
() => 'rejected'
);
}
let generateFuture: Promise<string> | null = null;
let isGenerating = false;
const toastStore = getToastStore();
async function generateImage(signal?: AbortSignal): Promise<string> {
if (isGenerating) {
throw Error('Bilderzeugung bereits im Gange');
}
isGenerating = true;
try {
const response = await fetch(
`/api/forward/gen_image?prompt=${encodeURIComponent(inputText)}&model=${encodeURIComponent(currentModel)}`,
{ signal }
);
if (response.ok) {
const data = await response.json();
return data.url;
} else {
throw new Error(
`Fehler beim Generieren des Bildes<br\><b>Grund:</b> ${await response.text()}`
);
}
} catch (error) {
if (error.name === 'AbortError') {
console.log('Anfrage wurde abgebrochen');
throw Error('Bilderzeugung abgebrochen');
} else {
console.error('Fehler:', error);
toastStore.trigger({
message: `Generierung fehlgeschlagen: ${(error as Error).message}`,
background: 'variant-filled-error'
});
// rethrow
throw error;
}
} finally {
isGenerating = false;
}
}
async function handleSubmit() {
try {
if (generateFuture !== null && (await promiseState(generateFuture)) === 'pending') {
console.log('Wird bereits generiert');
toastStore.trigger({
message: `Generierung bereits im Gange`,
background: 'variant-filled-warning',
action: {
label: 'Abbrechen',
response: () => {
generateFutureAbortController.abort();
resetAbortController();
toastStore.trigger({
message: 'Bilderzeugung erfolgreich abgebrochen',
background: 'variant-filled-success'
});
}
}
});
} else {
generateFuture = generateImage(generateFutureAbortSignal);
}
} catch (error) {
toastStore.trigger({
message: `Generierung fehlgeschlagen: ${(error as Error).message}`,
background: 'variant-filled-error'
});
} finally {
}
}
let improvePromptRunning: boolean = false;
async function improvePrompt() {
if (improvePromptRunning) {
toastStore.trigger({
message: 'Prompt-Verbesserung läuft. Bitte warten...',
background: 'variant-filled-warning'
});
}
improvePromptRunning = true;
try {
const response = await fetch(
`/api/forward/gen_prompt?prompt=${encodeURIComponent(inputText)}`
);
if (response.ok) {
const data = await response.json();
inputText = data.text;
} else {
throw new Error(
`Fehler beim Verbessern des Prompts<br\><b>Grund:</b> ${await response.text()}`
);
}
} catch (error) {
toastStore.trigger({
message: `Prompt-Verbesserung fehlgeschlagen: ${(error as Error).message}`,
background: 'variant-filled-error'
});
} finally {
improvePromptRunning = false;
}
}
</script>
<div class="container mx-auto p-8">
<h1 class="mb-4 text-2xl font-bold">Bildgenerator</h1>
<center>
<div
class="rounded-container-token flex aspect-square w-full items-center justify-center bg-gray-800 md:m-16 md:w-[70%]"
>
{#await generateFuture}
<ProgressRadial />
{:then url}
{#if url !== null}
<div class="h-full w-full">
<img src={url} alt="Generiertes Bild" class="h-full w-full object-cover" />
</div>
{:else}
<p>Generiere ein Bild!</p>
{/if}
{:catch error}
<div class="card variant-glass-error p-4">
<p>{@html error.message}</p>
</div>
{/await}
</div>
<div class="mb-4 px-4">
<p>Bildgenerations-Model</p>
<RadioGroup active="variant-filled-primary" hover="hover:variant-soft-primary">
{#each models as model}
<RadioItem bind:group={currentModel} value={model.value}>
{model.name}
</RadioItem>
{/each}
</RadioGroup>
</div>
</center>
<form on:submit|preventDefault={handleSubmit} class="flex flex-col md:flex-row space-x-4">
<label class="label flex-1">
<span>Prompt eingeben:</span>
<textarea class="textarea p-2" rows="4" bind:value={inputText} disabled={improvePromptRunning}
></textarea>
</label>
<div class="flex flex-col items-center justify-center space-y-4 pt-2">
<button class="btn variant-filled-primary mt-4" type="submit">
{#if isGenerating}
Generiere...
{:else}
Bild generieren
{/if}
</button>
<button class="btn variant-filled-secondary" on:click|preventDefault={improvePrompt}>
{#if improvePromptRunning}
Verbessere...
{:else}
Prompt verbessern
{/if}
</button>
</div>
</form>
</div>