mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-04 05:02:17 +00:00
133 lines
4.3 KiB
Vue
133 lines
4.3 KiB
Vue
<template>
|
|
<Card
|
|
ref="cardRef"
|
|
:data-testid="`template-workflow-${template.name}`"
|
|
class="w-64 template-card rounded-2xl overflow-hidden cursor-pointer shadow-elevation-2 dark-theme:bg-dark-elevation-1 h-full"
|
|
:pt="{
|
|
body: { class: 'p-0 h-full flex flex-col' }
|
|
}"
|
|
@click="$emit('loadWorkflow', template.name)"
|
|
>
|
|
<template #header>
|
|
<div class="flex items-center justify-center">
|
|
<div class="relative overflow-hidden rounded-t-lg">
|
|
<template v-if="template.mediaType === 'audio'">
|
|
<AudioThumbnail :src="baseThumbnailSrc" />
|
|
</template>
|
|
<template v-else-if="template.thumbnailVariant === 'compareSlider'">
|
|
<CompareSliderThumbnail
|
|
:base-image-src="baseThumbnailSrc"
|
|
:overlay-image-src="overlayThumbnailSrc"
|
|
:alt="title"
|
|
:is-hovered="isHovered"
|
|
/>
|
|
</template>
|
|
<template v-else-if="template.thumbnailVariant === 'hoverDissolve'">
|
|
<HoverDissolveThumbnail
|
|
:base-image-src="baseThumbnailSrc"
|
|
:overlay-image-src="overlayThumbnailSrc"
|
|
:alt="title"
|
|
:is-hovered="isHovered"
|
|
/>
|
|
</template>
|
|
<template v-else>
|
|
<DefaultThumbnail
|
|
:src="baseThumbnailSrc"
|
|
:alt="title"
|
|
:is-hovered="isHovered"
|
|
:hover-zoom="
|
|
template.thumbnailVariant === 'zoomHover'
|
|
? UPSCALE_ZOOM_SCALE
|
|
: DEFAULT_ZOOM_SCALE
|
|
"
|
|
/>
|
|
</template>
|
|
<ProgressSpinner
|
|
v-if="loading"
|
|
class="absolute inset-0 z-1 w-3/12 h-full"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #content>
|
|
<div class="flex items-center px-4 py-3">
|
|
<div class="flex-1 flex flex-col">
|
|
<h3 class="line-clamp-2 text-lg font-normal mb-0 h-12" :title="title">
|
|
{{ title }}
|
|
</h3>
|
|
<p class="line-clamp-2 text-sm text-muted grow" :title="description">
|
|
{{ description }}
|
|
</p>
|
|
</div>
|
|
<div
|
|
class="flex md:hidden xl:flex items-center justify-center ml-4 w-10 h-10 rounded-full"
|
|
>
|
|
<i class="pi pi-angle-right text-2xl" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</Card>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useElementHover } from '@vueuse/core'
|
|
import Card from 'primevue/card'
|
|
import ProgressSpinner from 'primevue/progressspinner'
|
|
import { computed, ref } from 'vue'
|
|
|
|
import AudioThumbnail from '@/components/templates/thumbnails/AudioThumbnail.vue'
|
|
import CompareSliderThumbnail from '@/components/templates/thumbnails/CompareSliderThumbnail.vue'
|
|
import DefaultThumbnail from '@/components/templates/thumbnails/DefaultThumbnail.vue'
|
|
import HoverDissolveThumbnail from '@/components/templates/thumbnails/HoverDissolveThumbnail.vue'
|
|
import { api } from '@/scripts/api'
|
|
import { TemplateInfo } from '@/types/workflowTemplateTypes'
|
|
|
|
const UPSCALE_ZOOM_SCALE = 16 // for upscale templates, exaggerate the hover zoom
|
|
const DEFAULT_ZOOM_SCALE = 5
|
|
|
|
const { sourceModule, loading, template } = defineProps<{
|
|
sourceModule: string
|
|
categoryTitle: string
|
|
loading: boolean
|
|
template: TemplateInfo
|
|
}>()
|
|
|
|
const cardRef = ref<HTMLElement | null>(null)
|
|
const isHovered = useElementHover(cardRef)
|
|
|
|
const getThumbnailUrl = (index = '') => {
|
|
const basePath =
|
|
sourceModule === 'default'
|
|
? api.fileURL(`/templates/${template.name}`)
|
|
: api.apiURL(`/workflow_templates/${sourceModule}/${template.name}`)
|
|
|
|
// For templates from custom nodes, multiple images is not yet supported
|
|
const indexSuffix = sourceModule === 'default' && index ? `-${index}` : ''
|
|
|
|
return `${basePath}${indexSuffix}.${template.mediaSubtype}`
|
|
}
|
|
|
|
const baseThumbnailSrc = computed(() =>
|
|
getThumbnailUrl(sourceModule === 'default' ? '1' : '')
|
|
)
|
|
const overlayThumbnailSrc = computed(() =>
|
|
getThumbnailUrl(sourceModule === 'default' ? '2' : '')
|
|
)
|
|
|
|
const description = computed(() => {
|
|
return sourceModule === 'default'
|
|
? template.localizedDescription ?? ''
|
|
: template.description.replace(/[-_]/g, ' ').trim()
|
|
})
|
|
|
|
const title = computed(() => {
|
|
return sourceModule === 'default'
|
|
? template.localizedTitle ?? ''
|
|
: template.name
|
|
})
|
|
|
|
defineEmits<{
|
|
loadWorkflow: [name: string]
|
|
}>()
|
|
</script>
|