fix
This commit is contained in:
@@ -9,7 +9,6 @@ import Skeleton from 'primevue/skeleton'
|
||||
import Button from 'primevue/button'
|
||||
import ConfirmDialog from 'primevue/confirmdialog'
|
||||
import { useConfirm } from 'primevue/useconfirm'
|
||||
import Image from 'primevue/image'
|
||||
import Dialog from 'primevue/dialog'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -129,6 +128,14 @@ const removeGeneration = (gen) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --- Image Preview ---
|
||||
const isImagePreviewVisible = ref(false)
|
||||
const previewImage = ref(null)
|
||||
const openImagePreview = (url) => {
|
||||
previewImage.value = { url }
|
||||
isImagePreviewVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -183,9 +190,11 @@ const removeGeneration = (gen) => {
|
||||
<div v-for="gen in generations" :key="gen.id"
|
||||
class="glass-panel rounded-xl overflow-hidden group relative transition-all hover:bg-white/5">
|
||||
|
||||
<div class="aspect-[2/3] w-full bg-slate-800 relative overflow-hidden">
|
||||
<Image :src="gen.result || API_URL + `/assets/${gen.result_list[0]}` + '?thumbnail=true'"
|
||||
preview class="w-full h-full object-cover" imageClass="w-full h-full object-cover" />
|
||||
<div class="aspect-[2/3] w-full bg-slate-800 relative overflow-hidden cursor-pointer"
|
||||
@click="gen.result_list && gen.result_list.length > 0 ? openImagePreview(API_URL + '/assets/' + gen.result_list[0]) : null">
|
||||
<img v-if="gen.result_list && gen.result_list.length > 0"
|
||||
:src="gen.result || API_URL + `/assets/${gen.result_list[0]}` + '?thumbnail=true'"
|
||||
class="w-full h-full object-cover" />
|
||||
|
||||
<!-- Overlay Actions -->
|
||||
<div
|
||||
@@ -230,7 +239,7 @@ const removeGeneration = (gen) => {
|
||||
:class="selectedGenerations.some(g => g.id === gen.id) ? 'border-violet-500 ring-2 ring-violet-500/30' : 'border-transparent hover:border-white/20'">
|
||||
|
||||
<img v-if="gen.result_list && gen.result_list.length > 0"
|
||||
:src="gen.result_list[0].includes('http') ? gen.result_list[0] : (gen.result || API_URL + `/assets/${gen.result_list[0]}` + '?thumbnail=true')"
|
||||
:src="gen.result_list[0].includes('http') ? gen.result_list[0] : (gen.result || API_URL + `/assets/${gen.result_list[0]}`)"
|
||||
class="w-full h-full object-cover" />
|
||||
<!-- Fallback for no result -->
|
||||
<div v-else class="w-full h-full bg-slate-800 flex items-center justify-center text-slate-500">
|
||||
@@ -263,6 +272,18 @@ const removeGeneration = (gen) => {
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- Image Preview Modal -->
|
||||
<Dialog v-model:visible="isImagePreviewVisible" modal dismissableMask
|
||||
:style="{ width: '90vw', maxWidth: '1000px', background: 'transparent', boxShadow: 'none' }"
|
||||
:pt="{ root: { class: '!bg-transparent !border-none !shadow-none' }, header: { class: '!hidden' }, content: { class: '!bg-transparent !p-0' } }">
|
||||
<div class="relative flex items-center justify-center" @click="isImagePreviewVisible = false">
|
||||
<img v-if="previewImage" :src="previewImage.url"
|
||||
class="max-w-full max-h-[85vh] object-contain rounded-xl shadow-2xl" />
|
||||
<Button icon="pi pi-times" @click="isImagePreviewVisible = false" rounded text
|
||||
class="!absolute -top-4 -right-4 !text-white !bg-black/50 hover:!bg-black/70 !w-10 !h-10" />
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ const historyFirst = ref(0)
|
||||
const isSettingsVisible = ref(false)
|
||||
const isSubmitting = ref(false)
|
||||
const activeOverlayId = ref(null) // For mobile tap-to-show overlay
|
||||
const filterCharacter = ref(null) // Character filter for gallery
|
||||
|
||||
// Options
|
||||
const qualityOptions = ref([
|
||||
@@ -120,6 +121,14 @@ watch([prompt, selectedCharacter, selectedAssets, quality, aspectRatio, sendToTe
|
||||
saveSettings()
|
||||
}, { deep: true })
|
||||
|
||||
// Watcher for character filter — reload history when filter changes
|
||||
watch(filterCharacter, async () => {
|
||||
historyGenerations.value = []
|
||||
historyTotal.value = 0
|
||||
historyFirst.value = 0
|
||||
await refreshHistory()
|
||||
})
|
||||
|
||||
|
||||
// --- Data Loading ---
|
||||
const loadData = async () => {
|
||||
@@ -127,7 +136,7 @@ const loadData = async () => {
|
||||
const [charsRes, assetsRes, historyRes] = await Promise.all([
|
||||
dataService.getCharacters(), // Assuming this exists and returns list
|
||||
dataService.getAssets(100, 0, 'all'), // Load a batch of assets
|
||||
aiService.getGenerations(historyRows.value, historyFirst.value)
|
||||
aiService.getGenerations(historyRows.value, historyFirst.value, filterCharacter.value?.id)
|
||||
])
|
||||
|
||||
// Characters
|
||||
@@ -197,7 +206,7 @@ const loadData = async () => {
|
||||
|
||||
const refreshHistory = async () => {
|
||||
try {
|
||||
const response = await aiService.getGenerations(historyRows.value, 0)
|
||||
const response = await aiService.getGenerations(historyRows.value, 0, filterCharacter.value?.id)
|
||||
if (response && response.generations) {
|
||||
// Update existing items and add new ones at the top
|
||||
const newGenerations = []
|
||||
@@ -352,7 +361,7 @@ const loadMoreHistory = async () => {
|
||||
|
||||
try {
|
||||
const nextOffset = historyGenerations.value.length
|
||||
const response = await aiService.getGenerations(historyRows.value, nextOffset)
|
||||
const response = await aiService.getGenerations(historyRows.value, nextOffset, filterCharacter.value?.id)
|
||||
|
||||
if (response && response.generations) {
|
||||
const newGenerations = response.generations.filter(gen =>
|
||||
@@ -602,6 +611,32 @@ const confirmAddToAlbum = async () => {
|
||||
<span class="text-xs text-slate-500 border-l border-white/10 pl-3">History</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Dropdown v-model="filterCharacter" :options="characters" optionLabel="name"
|
||||
placeholder="All Characters" showClear
|
||||
class="!w-48 !bg-slate-800/60 !border-white/10 !text-white !rounded-xl !text-sm" :pt="{
|
||||
root: { class: '!bg-slate-800/60 !h-8' },
|
||||
input: { class: '!text-white !text-xs !py-1 !px-2' },
|
||||
trigger: { class: '!text-slate-400 !w-6' },
|
||||
panel: { class: '!bg-slate-800 !border-white/10' },
|
||||
item: { class: '!text-slate-300 hover:!bg-white/10 hover:!text-white !text-xs !py-1.5' },
|
||||
clearIcon: { class: '!text-slate-400 hover:!text-white' }
|
||||
}">
|
||||
<template #value="slotProps">
|
||||
<div v-if="slotProps.value" class="flex items-center gap-1.5">
|
||||
<img v-if="slotProps.value.avatar_image" :src="API_URL + slotProps.value.avatar_image"
|
||||
class="w-5 h-5 rounded-full object-cover" />
|
||||
<span class="text-xs">{{ slotProps.value.name }}</span>
|
||||
</div>
|
||||
<span v-else class="text-xs text-slate-400">{{ slotProps.placeholder }}</span>
|
||||
</template>
|
||||
<template #option="slotProps">
|
||||
<div class="flex items-center gap-2">
|
||||
<img v-if="slotProps.option.avatar_image" :src="API_URL + slotProps.option.avatar_image"
|
||||
class="w-6 h-6 rounded-full object-cover" />
|
||||
<span>{{ slotProps.option.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
<Button icon="pi pi-refresh" @click="refreshHistory" rounded text
|
||||
class="!text-slate-400 hover:!bg-white/10 !w-8 !h-8 md:hidden" />
|
||||
<Button icon="pi pi-cog" @click="isSettingsVisible = true" rounded text
|
||||
@@ -790,7 +825,8 @@ const confirmAddToAlbum = async () => {
|
||||
class="flex items-center gap-2 mt-2 px-1 animate-in fade-in slide-in-from-top-1">
|
||||
<Checkbox v-model="useProfileImage" :binary="true" inputId="use-profile-img" />
|
||||
<label for="use-profile-img"
|
||||
class="text-xs text-slate-300 cursor-pointer select-none">Use Character
|
||||
class="text-xs text-slate-300 cursor-pointer select-none">Use
|
||||
Character
|
||||
Photo</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -801,7 +837,8 @@ const confirmAddToAlbum = async () => {
|
||||
<div @click="openAssetPicker"
|
||||
class="w-full bg-slate-800 border border-white/10 rounded-xl p-3 min-h-[46px] cursor-pointer hover:bg-slate-700/50 transition-colors flex flex-wrap gap-2">
|
||||
<span v-if="selectedAssets.length === 0"
|
||||
class="text-slate-400 text-sm py-0.5">Select Assets</span>
|
||||
class="text-slate-400 text-sm py-0.5">Select
|
||||
Assets</span>
|
||||
<div v-for="asset in selectedAssets" :key="asset.id"
|
||||
class="px-2 py-1 bg-violet-600/30 border border-violet-500/30 text-violet-200 text-xs rounded-md flex items-center gap-2 animate-in fade-in zoom-in duration-200"
|
||||
@click.stop>
|
||||
|
||||
Reference in New Issue
Block a user