mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-04 07:00:23 +00:00
Workflow templates (#938)
* Add template gallery * Add simple test * Add examples * Enable floating menu in test
This commit is contained in:
committed by
Chenlei Hu
parent
2aaee5c331
commit
bf7652227a
@@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<SidebarTabTemplate :title="$t('sideToolbar.workflows')">
|
||||
<template #tool-buttons>
|
||||
<Button
|
||||
class="browse-templates-button"
|
||||
icon="pi pi-th-large"
|
||||
v-tooltip="$t('sideToolbar.browseTemplates')"
|
||||
text
|
||||
@click="browseTemplates"
|
||||
/>
|
||||
<Button
|
||||
class="browse-workflows-button"
|
||||
icon="pi pi-folder-open"
|
||||
@@ -112,6 +119,7 @@ import { TreeExplorerNode } from '@/types/treeExplorerTypes'
|
||||
import { ComfyWorkflow } from '@/scripts/workflows'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useTreeExpansion } from '@/hooks/treeHooks'
|
||||
import { showTemplateWorkflowsDialog } from '@/services/dialogService'
|
||||
|
||||
const searchQuery = ref('')
|
||||
const isSearching = computed(() => searchQuery.value.length > 0)
|
||||
@@ -145,6 +153,10 @@ const browse = () => {
|
||||
app.ui.loadFile()
|
||||
}
|
||||
|
||||
const browseTemplates = () => {
|
||||
showTemplateWorkflowsDialog()
|
||||
}
|
||||
|
||||
const createBlank = () => {
|
||||
app.workflowManager.setWorkflow(null)
|
||||
app.clean()
|
||||
|
||||
81
src/components/templates/TemplateWorkflowsContent.vue
Normal file
81
src/components/templates/TemplateWorkflowsContent.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-wrap content-around justify-around gap-4"
|
||||
data-testid="template-workflows-content"
|
||||
>
|
||||
<div
|
||||
v-for="template in templates"
|
||||
:key="template"
|
||||
:data-testid="`template-workflow-${template}`"
|
||||
>
|
||||
<Card>
|
||||
<template #header>
|
||||
<div
|
||||
class="relative overflow-hidden rounded-lg cursor-pointer"
|
||||
@click="loadWorkflow(template)"
|
||||
>
|
||||
<img
|
||||
:src="`/templates/${template}.png`"
|
||||
class="w-64 h-64 rounded-lg object-cover"
|
||||
/>
|
||||
<a>
|
||||
<div
|
||||
class="absolute top-0 left-0 w-64 h-64 overflow-hidden opacity-0 transition duration-300 ease-in-out hover:opacity-100 bg-opacity-50 bg-black flex items-center justify-center"
|
||||
>
|
||||
<i class="pi pi-play-circle"></i>
|
||||
</div>
|
||||
</a>
|
||||
<ProgressSpinner
|
||||
v-if="loading === template"
|
||||
class="absolute inset-0 z-1 w-3/12 h-full"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #subtitle>{{
|
||||
$t(`templateWorkflows.template.${template}`)
|
||||
}}</template>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import Card from 'primevue/card'
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
import { ref } from 'vue'
|
||||
import { app } from '@/scripts/app'
|
||||
import { api } from '@/scripts/api'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
const templates = ['default', 'image2image', 'upscale', 'flux_schnell']
|
||||
const loading = ref<string | null>(null)
|
||||
|
||||
const loadWorkflow = async (id: string) => {
|
||||
loading.value = id
|
||||
const json = await fetch(api.fileURL(`templates/${id}.json`)).then((r) =>
|
||||
r.json()
|
||||
)
|
||||
useDialogStore().closeDialog()
|
||||
await app.loadGraphData(
|
||||
json,
|
||||
true,
|
||||
true,
|
||||
t(`templateWorkflows.template.${id}`)
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.p-card {
|
||||
--p-card-body-padding: 10px 0 0 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.p-card-subtitle) {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
10
src/i18n.ts
10
src/i18n.ts
@@ -50,6 +50,7 @@ const messages = {
|
||||
queue: 'Queue',
|
||||
nodeLibrary: 'Node Library',
|
||||
workflows: 'Workflows',
|
||||
browseTemplates: 'Browse example templates',
|
||||
nodeLibraryTab: {
|
||||
sortOrder: 'Sort Order'
|
||||
},
|
||||
@@ -80,6 +81,15 @@ const messages = {
|
||||
clipspace: 'Open Clipspace',
|
||||
resetView: 'Reset canvas view',
|
||||
clear: 'Clear workflow'
|
||||
},
|
||||
templateWorkflows: {
|
||||
title: 'Get Started with a Template',
|
||||
template: {
|
||||
default: 'Image Generation',
|
||||
image2image: 'Image to Image',
|
||||
upscale: '2 Pass Upscale',
|
||||
flux_schnell: 'Flux Schnell'
|
||||
}
|
||||
}
|
||||
},
|
||||
zh: {
|
||||
|
||||
@@ -8,6 +8,8 @@ import SettingDialogContent from '@/components/dialog/content/SettingDialogConte
|
||||
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
|
||||
import type { ExecutionErrorWsMessage } from '@/types/apiTypes'
|
||||
import ExecutionErrorDialogContent from '@/components/dialog/content/ExecutionErrorDialogContent.vue'
|
||||
import TemplateWorkflowsContent from '@/components/templates/TemplateWorkflowsContent.vue'
|
||||
import { i18n } from '@/i18n'
|
||||
|
||||
export function showLoadWorkflowWarning(props: {
|
||||
missingNodeTypes: any[]
|
||||
@@ -47,3 +49,10 @@ export function showExecutionErrorDialog(error: ExecutionErrorWsMessage) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function showTemplateWorkflowsDialog() {
|
||||
useDialogStore().showDialog({
|
||||
title: i18n.global.t('templateWorkflows.title'),
|
||||
component: TemplateWorkflowsContent
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user