Files
ai-service-front/src/views/ImageToPromptView.vue

184 lines
9.4 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { dataService } from '../services/dataService'
import Button from 'primevue/button'
const router = useRouter()
const imageFiles = ref([])
const imagePreviews = ref([])
const userPrompt = ref('')
const generatedPrompt = ref('')
const loading = ref(false)
const API_URL = import.meta.env.VITE_API_URL
const onFileSelect = (event) => {
const files = Array.from(event.target.files)
if (!files.length) return
const remainingSlots = 3 - imageFiles.value.length
if (remainingSlots <= 0) return
const filesToAdd = files.slice(0, remainingSlots)
filesToAdd.forEach(file => {
imageFiles.value.push(file)
imagePreviews.value.push(URL.createObjectURL(file))
})
// Reset input so same file can be selected again if needed
event.target.value = ''
}
const removeImage = (index) => {
URL.revokeObjectURL(imagePreviews.value[index])
imageFiles.value.splice(index, 1)
imagePreviews.value.splice(index, 1)
}
const generate = async () => {
if (imageFiles.value.length === 0) return
loading.value = true
try {
const result = await dataService.generatePromptFromImage(imageFiles.value, userPrompt.value)
generatedPrompt.value = result
} catch (e) {
console.error('Generation failed', e)
// Fallback for demo if service fails
generatedPrompt.value = "Failed to generate prompt. Please try again."
} finally {
loading.value = false
}
}
const copyToClipboard = () => {
navigator.clipboard.writeText(generatedPrompt.value)
}
</script>
<template>
<div class="flex flex-col h-full p-8 overflow-y-auto w-full text-slate-100">
<!-- Main Content (Sidebar removed) -->
<div class="flex-1 flex flex-col">
<header class="mb-8">
<h1 class="text-4xl font-bold m-0">Image to Prompt</h1>
<p class="mt-2 mb-0 text-slate-400">Transform your images into descriptive prompts</p>
</header>
<div class="flex flex-col lg:flex-row gap-8 h-full pb-8">
<!-- Input Section -->
<div class="flex-1 flex flex-col gap-6">
<!-- Image Upload -->
<div
class="glass-panel p-6 rounded-2xl border-dashed border-2 border-white/10 hover:border-violet-500/50 transition-colors relative flex flex-col items-center justify-center min-h-[300px] bg-black/20 group">
<!-- Upload Input (only if < 3 images) -->
<input v-if="imageFiles.length < 3" type="file" @change="onFileSelect" accept="image/*" multiple
class="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-20" title=" " />
<!-- Empty State -->
<div v-if="imageFiles.length === 0"
class="text-center p-8 transition-transform duration-300 group-hover:scale-105 pointer-events-none">
<span class="text-6xl mb-6 block opacity-50">🖼</span>
<h3 class="font-bold text-xl mb-2">Drop images here</h3>
<p class="text-slate-400 text-sm">Upload up to 3 images<br />JPG, PNG, WEBP</p>
</div>
<!-- Images Grid -->
<div v-else class="w-full h-full p-4">
<div class="grid grid-cols-3 gap-4 h-full">
<!-- Existing Images -->
<div v-for="(preview, index) in imagePreviews" :key="index"
class="relative rounded-xl overflow-hidden border border-white/10 group/img aspect-square bg-black/40">
<img :src="preview" class="w-full h-full object-cover" />
<!-- Remove Button -->
<div @click.stop="removeImage(index)"
class="absolute top-2 right-2 bg-red-500/80 hover:bg-red-500 text-white rounded-full p-1.5 cursor-pointer opacity-0 group-hover/img:opacity-100 transition-opacity z-30">
<i class="pi pi-times text-xs"></i>
</div>
</div>
<!-- Add More Placeholder (if < 3) -->
<div v-if="imageFiles.length < 3"
class="relative rounded-xl border-2 border-dashed border-white/20 flex flex-col items-center justify-center bg-white/5 hover:bg-white/10 transition-colors aspect-square text-slate-400 hover:text-white">
<span class="text-3xl mb-2">+</span>
<span class="text-xs font-bold text-center">Add<br>Image</span>
</div>
</div>
<!-- Hint -->
<div class="absolute bottom-4 left-0 w-full text-center pointer-events-none">
<span v-if="imageFiles.length < 3"
class="text-xs text-violet-400 bg-black/50 px-3 py-1 rounded-full backdrop-blur-sm">
Click empty space or drop more files
</span>
<span v-else
class="text-xs text-orange-400 bg-black/50 px-3 py-1 rounded-full backdrop-blur-sm">
Max 3 images reached
</span>
</div>
</div>
</div>
<!-- Optional Prompt -->
<div class="glass-panel p-1 rounded-2xl border border-white/5">
<div class="p-4 border-b border-white/5">
<label class="text-sm font-bold text-slate-300 flex items-center gap-2">
<i class="pi pi-align-left"></i> Additional Instructions <span
class="text-slate-500 font-normal">(Optional)</span>
</label>
</div>
<textarea v-model="userPrompt"
class="w-full bg-transparent border-none p-4 text-slate-100 focus:outline-none focus:ring-0 placeholder-slate-600 min-h-[100px] resize-none"
placeholder="e.g. Focus on the lighting and atmosphere..."></textarea>
</div>
<Button label="Generate Prompt" @click="generate" :loading="loading"
:disabled="imageFiles.length === 0" icon="pi pi-sparkles"
class="w-full py-4 text-lg font-bold !bg-gradient-to-r !from-violet-600 !to-cyan-600 hover:!from-violet-500 hover:!to-cyan-500 !border-none !rounded-xl !shadow-lg !shadow-violet-500/20 !text-white transition-all transform hover:scale-[1.01] active:scale-[0.99]" />
</div>
<!-- Output Section -->
<div
class="flex-1 glass-panel p-6 rounded-2xl flex flex-col relative border border-white/5 bg-gradient-to-b from-white/5 to-transparent">
<div class="flex justify-between items-center mb-6">
<div class="flex items-center gap-3">
<div
class="w-8 h-8 rounded-lg bg-green-500/20 text-green-400 flex items-center justify-center">
<i class="pi pi-check-circle"></i>
</div>
<h2 class="text-xl font-bold m-0">Result</h2>
</div>
<Button icon="pi pi-copy" @click="copyToClipboard" label="Copy" text size="small"
class="!text-slate-400 hover:!text-white hover:!bg-white/10" v-if="generatedPrompt" />
</div>
<div
class="flex-1 bg-black/40 rounded-xl p-6 font-mono text-sm leading-7 text-slate-300 overflow-y-auto border border-white/5 shadow-inner">
<template v-if="loading">
<div
class="flex flex-col items-center justify-center h-full text-slate-500 gap-6 animate-pulse">
<div
class="w-16 h-16 rounded-full border-4 border-violet-500/30 border-t-violet-500 animate-spin">
</div>
<span class="font-medium">Analyzing image details...</span>
</div>
</template>
<template v-else-if="generatedPrompt">
{{ generatedPrompt }}
</template>
<template v-else>
<div class="flex flex-col items-center justify-center h-full text-slate-600 gap-4">
<i class="pi pi-image text-4xl opacity-50"></i>
<span>Upload an image to generate a prompt</span>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
</template>