Fraser's picture
add UI options
62fead0
<script lang="ts">
import type { GradioClient, CaptionParams, CaptionResult, CaptionType, CaptionLength } from '$lib/types';
interface Props {
client: GradioClient | null;
currentImage?: Blob | null;
}
let { client = null, currentImage = null }: Props = $props();
let imageInput: HTMLInputElement;
let uploadedImage: File | null = $state(null);
let captionType: CaptionType = $state("Descriptive");
let captionLength: CaptionLength = $state("long");
let isGenerating = $state(false);
let result: CaptionResult | null = $state(null);
let error: string | null = $state(null);
const captionTypes: CaptionType[] = [
"Descriptive",
"Descriptive (Informal)",
"Training Prompt",
"MidJourney",
"Booru tag list",
"Booru-like tag list",
"Art Critic",
"Product Listing",
"Social Media Post"
];
const captionLengths: CaptionLength[] = [
"any",
"very short",
"short",
"medium-length",
"long",
"very long"
];
function handleFileChange(e: Event) {
const target = e.target as HTMLInputElement;
if (target.files && target.files[0]) {
uploadedImage = target.files[0];
}
}
async function handleSubmit(e: Event) {
e.preventDefault();
const imageToCaption = uploadedImage || currentImage;
if (!client || !imageToCaption) {
error = "Please upload an image or generate one above.";
return;
}
isGenerating = true;
error = null;
result = null;
try {
const output = await client.predict("/stream_chat", [
imageToCaption,
captionType,
captionLength,
[], // extra_options
"", // name_input
"" // custom_prompt
]);
const [prompt, caption] = output.data;
result = {
caption,
type: captionType,
length: captionLength
};
} catch (err) {
console.error(err);
error = `Caption generation failed: ${err}`;
} finally {
isGenerating = false;
}
}
</script>
<form class="caption-form" onsubmit={handleSubmit}>
<h3>Caption Image</h3>
<label for="imageInput">
Upload Image {currentImage ? 'or use generated image' : ''}
</label>
<input
type="file"
id="imageInput"
accept="image/*"
onchange={handleFileChange}
bind:this={imageInput}
disabled={isGenerating}
/>
<label for="captionType">Caption Type</label>
<select
id="captionType"
bind:value={captionType}
disabled={isGenerating}
>
{#each captionTypes as type}
<option value={type}>{type}</option>
{/each}
</select>
<label for="captionLength">Caption Length</label>
<select
id="captionLength"
bind:value={captionLength}
disabled={isGenerating}
>
{#each captionLengths as length}
<option value={length}>
{length.charAt(0).toUpperCase() + length.slice(1).replace('-', ' ')}
</option>
{/each}
</select>
<button
type="submit"
class="generate-button"
disabled={isGenerating || !client}
>
{isGenerating ? 'Generating Caption…' : 'Generate Caption'}
</button>
</form>
{#if error}
<div class="error-message">{error}</div>
{/if}
{#if result}
<div class="caption-result">
<h4>Generated Caption</h4>
<p><strong>Type:</strong> {result.type}</p>
<p><strong>Length:</strong> {result.length}</p>
<p><strong>Caption:</strong></p>
<p class="caption-text">{result.caption}</p>
</div>
{/if}
<style>
.caption-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="file"],
select {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
margin-bottom: 1rem;
}
.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;
}
.caption-result {
background: #f8f9fa;
padding: 1rem;
border-radius: 6px;
margin-top: 1rem;
}
.caption-result h4 {
margin-top: 0;
}
.caption-text {
font-style: italic;
}
.error-message {
color: #dc3545;
margin-top: 1rem;
padding: 0.5rem;
background: #f8d7da;
border-radius: 4px;
}
</style>