piclets / src /lib /components /ImageGeneration /FluxGenerator.svelte
Fraser's picture
add UI options
62fead0
<script lang="ts">
import type { GradioClient, FluxGenerationParams, FluxGenerationResult } from '$lib/types';
interface Props {
client: GradioClient | null;
onImageGenerated?: ((result: FluxGenerationResult, imageBlob: Blob) => void) | null;
}
let { client = null, onImageGenerated = null }: Props = $props();
let params: FluxGenerationParams = $state({
prompt: "",
seed: 0,
randomizeSeed: true,
width: 1024,
height: 1024,
steps: 4
});
let isGenerating = $state(false);
let result: FluxGenerationResult | null = $state(null);
let error: string | null = $state(null);
async function handleSubmit(e: Event) {
e.preventDefault();
if (!client || !params.prompt.trim()) return;
isGenerating = true;
error = null;
try {
const output = await client.predict("/infer", [
params.prompt,
params.seed,
params.randomizeSeed,
params.width,
params.height,
params.steps
]);
const [image, usedSeed] = output.data;
let url: string | undefined;
if (typeof image === "string") url = image;
else if (image && image.url) url = image.url;
else if (image && image.path) url = image.path;
if (url) {
result = {
imageUrl: url,
seed: usedSeed,
prompt: params.prompt
};
// Fetch image as blob for captioning
if (onImageGenerated) {
try {
const response = await fetch(url);
const blob = await response.blob();
onImageGenerated(result, blob);
} catch (err) {
console.warn("Could not fetch generated image for captioning:", err);
}
}
} else {
error = "Unexpected image format returned from API";
}
} catch (err) {
console.error(err);
error = `Generation failed: ${err}`;
} finally {
isGenerating = false;
}
}
</script>
<form class="generator-form" onsubmit={handleSubmit}>
<h3>Generate Image with FLUX-1 Schnell</h3>
<label for="prompt">Prompt</label>
<input
type="text"
id="prompt"
bind:value={params.prompt}
placeholder="Enter your prompt"
required
disabled={isGenerating}
/>
<label for="seed">Seed</label>
<input
type="number"
id="seed"
bind:value={params.seed}
min="0"
max="2147483647"
disabled={isGenerating}
/>
<label>
<input
type="checkbox"
bind:checked={params.randomizeSeed}
disabled={isGenerating}
/>
Randomize seed
</label>
<div class="input-row">
<div class="input-group">
<label for="width">Width</label>
<input
type="number"
id="width"
bind:value={params.width}
min="256"
max="2048"
step="32"
disabled={isGenerating}
/>
</div>
<div class="input-group">
<label for="height">Height</label>
<input
type="number"
id="height"
bind:value={params.height}
min="256"
max="2048"
step="32"
disabled={isGenerating}
/>
</div>
</div>
<label for="steps">Number of inference steps</label>
<input
type="number"
id="steps"
bind:value={params.steps}
min="1"
max="50"
disabled={isGenerating}
/>
<button
type="submit"
class="generate-button"
disabled={isGenerating || !client}
>
{isGenerating ? 'Generating…' : 'Generate Image'}
</button>
</form>
{#if error}
<div class="error-message">{error}</div>
{/if}
{#if isGenerating}
<div class="status-message">Generating image…</div>
{/if}
{#if result}
<div class="result">
<img src={result.imageUrl} alt="Generated" />
<p><strong>Seed</strong>: {result.seed}</p>
<p><strong>Prompt</strong>: {result.prompt}</p>
</div>
{/if}
<style>
.generator-form {
margin-top: 2rem;
padding-top: 2rem;
border-top: 1px solid #eee;
}
h3 {
margin-top: 0;
margin-bottom: 1.5rem;
}
label {
font-weight: 600;
margin-bottom: 0.25rem;
display: block;
}
input[type="text"],
input[type="number"] {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
margin-bottom: 1rem;
}
input[type="checkbox"] {
margin-right: 0.5rem;
}
.input-row {
display: flex;
gap: 1rem;
}
.input-group {
flex: 1;
}
.generate-button {
background: #007bff;
color: #fff;
border: none;
padding: 0.6rem 1.4rem;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.2s;
}
.generate-button:hover:not(:disabled) {
background: #0056b3;
}
.generate-button:disabled {
background: #9ac7ff;
cursor: not-allowed;
}
.result {
margin-top: 1rem;
}
.result img {
max-width: 100%;
border-radius: 8px;
margin-bottom: 1rem;
}
.error-message {
color: #dc3545;
margin-top: 1rem;
padding: 0.5rem;
background: #f8d7da;
border-radius: 4px;
}
.status-message {
color: #0c5460;
margin-top: 1rem;
padding: 0.5rem;
background: #d1ecf1;
border-radius: 4px;
}
</style>