mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-20 20:37:33 +00:00
Compare commits
6 Commits
fix/load-a
...
feat/check
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0548e68b6 | ||
|
|
588f59c6ae | ||
|
|
316755b5db | ||
|
|
e00886e538 | ||
|
|
72949d93e6 | ||
|
|
9a3547a24b |
@@ -1,5 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import TemplateWorkflowCard from '@/components/templates/TemplateWorkflowCard.vue'
|
||||
@@ -109,6 +110,10 @@ vi.mock('@/composables/useTemplateWorkflows', () => ({
|
||||
}))
|
||||
|
||||
describe('TemplateWorkflowCard', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
})
|
||||
|
||||
const createTemplate = (overrides = {}): TemplateInfo => ({
|
||||
name: 'test-template',
|
||||
mediaType: 'image',
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
<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.5 h-full"
|
||||
class="w-64 template-card rounded-2xl overflow-hidden shadow-elevation-2 dark-theme:bg-dark-elevation-1.5 h-full"
|
||||
:class="{
|
||||
'cursor-pointer': isCompatible,
|
||||
'cursor-not-allowed opacity-60 grayscale': !isCompatible
|
||||
}"
|
||||
:pt="{
|
||||
body: { class: 'p-0 h-full flex flex-col' }
|
||||
}"
|
||||
@click="$emit('loadWorkflow', template.name)"
|
||||
@click="handleCardClick"
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="flex items-center justify-center relative">
|
||||
<div class="relative overflow-hidden rounded-t-lg">
|
||||
<template v-if="template.mediaType === 'audio'">
|
||||
<AudioThumbnail :src="baseThumbnailSrc" />
|
||||
@@ -19,7 +23,7 @@
|
||||
:base-image-src="baseThumbnailSrc"
|
||||
:overlay-image-src="overlayThumbnailSrc"
|
||||
:alt="title"
|
||||
:is-hovered="isHovered"
|
||||
:is-hovered="isHovered && isCompatible"
|
||||
:is-video="
|
||||
template.mediaType === 'video' ||
|
||||
template.mediaSubtype === 'webp'
|
||||
@@ -31,7 +35,7 @@
|
||||
:base-image-src="baseThumbnailSrc"
|
||||
:overlay-image-src="overlayThumbnailSrc"
|
||||
:alt="title"
|
||||
:is-hovered="isHovered"
|
||||
:is-hovered="isHovered && isCompatible"
|
||||
:is-video="
|
||||
template.mediaType === 'video' ||
|
||||
template.mediaSubtype === 'webp'
|
||||
@@ -42,7 +46,7 @@
|
||||
<DefaultThumbnail
|
||||
:src="baseThumbnailSrc"
|
||||
:alt="title"
|
||||
:is-hovered="isHovered"
|
||||
:is-hovered="isHovered && isCompatible"
|
||||
:is-video="
|
||||
template.mediaType === 'video' ||
|
||||
template.mediaSubtype === 'webp'
|
||||
@@ -59,6 +63,21 @@
|
||||
class="absolute inset-0 z-1 w-3/12 h-full"
|
||||
/>
|
||||
</div>
|
||||
<!-- Version incompatibility indicator -->
|
||||
<div
|
||||
v-if="!isCompatible"
|
||||
class="absolute top-2 right-2 bg-yellow-500 text-white text-xs px-2 py-1 rounded-full flex items-center gap-1 shadow-lg z-10"
|
||||
:title="
|
||||
$t('templateWorkflows.requiresUpgrade', {
|
||||
version: template.versionRequired
|
||||
})
|
||||
"
|
||||
>
|
||||
<i class="pi pi-exclamation-triangle text-xs"></i>
|
||||
<span class="text-xs font-medium"
|
||||
>v{{ template.versionRequired }}+</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
@@ -70,6 +89,21 @@
|
||||
<p class="line-clamp-2 text-sm text-muted grow" :title="description">
|
||||
{{ description }}
|
||||
</p>
|
||||
<!-- Upgrade prompt for incompatible templates -->
|
||||
<div
|
||||
v-if="!isCompatible"
|
||||
class="mt-2 text-xs text-yellow-600 dark:text-yellow-400 font-medium"
|
||||
>
|
||||
{{ $t('templateWorkflows.upgradeToUse') }}
|
||||
<br />
|
||||
<span class="text-muted">
|
||||
{{
|
||||
$t('templateWorkflows.currentVersion', {
|
||||
version: currentVersion
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -87,7 +121,9 @@ import CompareSliderThumbnail from '@/components/templates/thumbnails/CompareSli
|
||||
import DefaultThumbnail from '@/components/templates/thumbnails/DefaultThumbnail.vue'
|
||||
import HoverDissolveThumbnail from '@/components/templates/thumbnails/HoverDissolveThumbnail.vue'
|
||||
import { useTemplateWorkflows } from '@/composables/useTemplateWorkflows'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import { TemplateInfo } from '@/types/workflowTemplateTypes'
|
||||
import { compareVersions } from '@/utils/formatUtil'
|
||||
|
||||
const UPSCALE_ZOOM_SCALE = 16 // for upscale templates, exaggerate the hover zoom
|
||||
const DEFAULT_ZOOM_SCALE = 5
|
||||
@@ -101,6 +137,7 @@ const { sourceModule, loading, template } = defineProps<{
|
||||
|
||||
const cardRef = ref<HTMLElement | null>(null)
|
||||
const isHovered = useElementHover(cardRef)
|
||||
const systemStatsStore = useSystemStatsStore()
|
||||
|
||||
const { getTemplateThumbnailUrl, getTemplateTitle, getTemplateDescription } =
|
||||
useTemplateWorkflows()
|
||||
@@ -133,7 +170,31 @@ const title = computed(() =>
|
||||
getTemplateTitle(template, effectiveSourceModule.value)
|
||||
)
|
||||
|
||||
defineEmits<{
|
||||
// Version compatibility check
|
||||
const currentVersion = computed(() => {
|
||||
return systemStatsStore.systemStats?.system?.comfyui_version || ''
|
||||
})
|
||||
|
||||
const isCompatible = computed(() => {
|
||||
if (!template.versionRequired) {
|
||||
return true // No version requirement, always compatible
|
||||
}
|
||||
|
||||
if (!currentVersion.value) {
|
||||
return false // No current version available, assume incompatible
|
||||
}
|
||||
|
||||
// Return true if current version is >= required version
|
||||
return compareVersions(currentVersion.value, template.versionRequired) >= 0
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
loadWorkflow: [name: string]
|
||||
}>()
|
||||
|
||||
const handleCardClick = () => {
|
||||
if (isCompatible.value) {
|
||||
emit('loadWorkflow', template.name)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -56,12 +56,13 @@ import { useAsyncState } from '@vueuse/core'
|
||||
import Button from 'primevue/button'
|
||||
import Divider from 'primevue/divider'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import { watch } from 'vue'
|
||||
import { onMounted, watch } from 'vue'
|
||||
|
||||
import TemplateWorkflowView from '@/components/templates/TemplateWorkflowView.vue'
|
||||
import TemplateWorkflowsSideNav from '@/components/templates/TemplateWorkflowsSideNav.vue'
|
||||
import { useResponsiveCollapse } from '@/composables/element/useResponsiveCollapse'
|
||||
import { useTemplateWorkflows } from '@/composables/useTemplateWorkflows'
|
||||
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||
import type { WorkflowTemplates } from '@/types/workflowTemplateTypes'
|
||||
|
||||
const {
|
||||
@@ -82,6 +83,14 @@ const {
|
||||
} = useTemplateWorkflows()
|
||||
|
||||
const { isReady } = useAsyncState(loadTemplates, null)
|
||||
const systemStatsStore = useSystemStatsStore()
|
||||
|
||||
// Initialize system stats when component mounts
|
||||
onMounted(async () => {
|
||||
if (!systemStatsStore.systemStats) {
|
||||
await systemStatsStore.fetchSystemStats()
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
isReady,
|
||||
|
||||
@@ -501,6 +501,9 @@
|
||||
},
|
||||
"templateWorkflows": {
|
||||
"title": "Get Started with a Template",
|
||||
"requiresUpgrade": "Requires ComfyUI v{version} or higher",
|
||||
"upgradeToUse": "Upgrade to use this template",
|
||||
"currentVersion": "Current version: v{version}",
|
||||
"category": {
|
||||
"ComfyUI Examples": "ComfyUI Examples",
|
||||
"Custom Nodes": "Custom Nodes",
|
||||
|
||||
@@ -1152,6 +1152,8 @@
|
||||
"Video": "Video",
|
||||
"Video API": "API de Video"
|
||||
},
|
||||
"currentVersion": "Versión actual: v{version}",
|
||||
"requiresUpgrade": "Requiere ComfyUI v{version} o superior",
|
||||
"template": {
|
||||
"3D": {
|
||||
"hunyuan-3d-multiview-elf": "Hunyuan3D 2.0 MV",
|
||||
@@ -1354,7 +1356,8 @@
|
||||
"api_veo2_i2v": "Usa la API Google Veo2 para generar videos a partir de imágenes."
|
||||
}
|
||||
},
|
||||
"title": "Comienza con una Plantilla"
|
||||
"title": "Comienza con una Plantilla",
|
||||
"upgradeToUse": "Actualiza para usar esta plantilla"
|
||||
},
|
||||
"toastMessages": {
|
||||
"couldNotDetermineFileType": "No se pudo determinar el tipo de archivo",
|
||||
|
||||
@@ -1152,6 +1152,8 @@
|
||||
"Video": "Vidéo",
|
||||
"Video API": "API vidéo"
|
||||
},
|
||||
"currentVersion": "Version actuelle : v{version}",
|
||||
"requiresUpgrade": "Nécessite ComfyUI v{version} ou supérieur",
|
||||
"template": {
|
||||
"3D": {
|
||||
"hunyuan-3d-multiview-elf": "Hunyuan3D Multivue",
|
||||
@@ -1354,7 +1356,8 @@
|
||||
"api_veo2_i2v": "Utilisez l'API Google Veo2 pour générer des vidéos à partir d'images."
|
||||
}
|
||||
},
|
||||
"title": "Commencez avec un modèle"
|
||||
"title": "Commencez avec un modèle",
|
||||
"upgradeToUse": "Mettez à jour pour utiliser ce modèle"
|
||||
},
|
||||
"toastMessages": {
|
||||
"couldNotDetermineFileType": "Impossible de déterminer le type de fichier",
|
||||
|
||||
@@ -1152,6 +1152,8 @@
|
||||
"Video": "ビデオ",
|
||||
"Video API": "動画API"
|
||||
},
|
||||
"currentVersion": "現在のバージョン: v{version}",
|
||||
"requiresUpgrade": "ComfyUI v{version} 以上が必要です",
|
||||
"template": {
|
||||
"3D": {
|
||||
"hunyuan-3d-multiview-elf": "Hunyuan3D マルチビュー",
|
||||
@@ -1354,7 +1356,8 @@
|
||||
"api_veo2_i2v": "Google Veo2 APIで画像から動画を生成します。"
|
||||
}
|
||||
},
|
||||
"title": "テンプレートを利用して開始"
|
||||
"title": "テンプレートを利用して開始",
|
||||
"upgradeToUse": "このテンプレートを使用するにはアップグレードしてください"
|
||||
},
|
||||
"toastMessages": {
|
||||
"couldNotDetermineFileType": "ファイルタイプを判断できませんでした",
|
||||
|
||||
@@ -1152,6 +1152,8 @@
|
||||
"Video": "비디오",
|
||||
"Video API": "비디오 API"
|
||||
},
|
||||
"currentVersion": "현재 버전: v{version}",
|
||||
"requiresUpgrade": "ComfyUI v{version} 이상이 필요합니다",
|
||||
"template": {
|
||||
"3D": {
|
||||
"hunyuan-3d-multiview-elf": "Hunyuan3D 다중뷰",
|
||||
@@ -1354,7 +1356,8 @@
|
||||
"api_veo2_i2v": "Google Veo2 API로 이미지에서 비디오를 생성합니다."
|
||||
}
|
||||
},
|
||||
"title": "템플릿으로 시작하기"
|
||||
"title": "템플릿으로 시작하기",
|
||||
"upgradeToUse": "이 템플릿을 사용하려면 업그레이드하세요"
|
||||
},
|
||||
"toastMessages": {
|
||||
"couldNotDetermineFileType": "파일 유형을 결정할 수 없습니다",
|
||||
|
||||
@@ -1152,6 +1152,8 @@
|
||||
"Video": "Видео",
|
||||
"Video API": "Video API"
|
||||
},
|
||||
"currentVersion": "Текущая версия: v{version}",
|
||||
"requiresUpgrade": "Требуется ComfyUI версии {version} или выше",
|
||||
"template": {
|
||||
"3D": {
|
||||
"hunyuan-3d-multiview-elf": "Hunyuan3D Многовидовой",
|
||||
@@ -1354,7 +1356,8 @@
|
||||
"api_veo2_i2v": "Используйте Google Veo2 API для генерации видео из изображений."
|
||||
}
|
||||
},
|
||||
"title": "Начните с шаблона"
|
||||
"title": "Начните с шаблона",
|
||||
"upgradeToUse": "Обновите, чтобы использовать этот шаблон"
|
||||
},
|
||||
"toastMessages": {
|
||||
"couldNotDetermineFileType": "Не удалось определить тип файла",
|
||||
|
||||
@@ -1152,6 +1152,8 @@
|
||||
"Video": "视频",
|
||||
"Video API": "视频 API"
|
||||
},
|
||||
"currentVersion": "当前版本:v{version}",
|
||||
"requiresUpgrade": "需要 ComfyUI v{version} 或更高版本",
|
||||
"template": {
|
||||
"3D": {
|
||||
"hunyuan-3d-multiview-elf": "混元3D多视图",
|
||||
@@ -1354,7 +1356,8 @@
|
||||
"api_veo2_i2v": "使用 Google Veo2 API 通过图像生成视频。"
|
||||
}
|
||||
},
|
||||
"title": "从模板开始"
|
||||
"title": "从模板开始",
|
||||
"upgradeToUse": "升级以使用此模板"
|
||||
},
|
||||
"toastMessages": {
|
||||
"couldNotDetermineFileType": "无法确定文件类型",
|
||||
|
||||
@@ -12,6 +12,10 @@ export interface TemplateInfo {
|
||||
localizedTitle?: string
|
||||
localizedDescription?: string
|
||||
sourceModule?: string
|
||||
/**
|
||||
* Minimum version of ComfyUI required to use this template
|
||||
*/
|
||||
versionRequired?: string
|
||||
}
|
||||
|
||||
export interface WorkflowTemplates {
|
||||
|
||||
Reference in New Issue
Block a user