Add reroute migration toast (#3286)

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Chenlei Hu
2025-03-30 21:48:10 -04:00
committed by GitHub
parent 7e76665a22
commit 58dec5ea42
15 changed files with 90 additions and 22 deletions

View File

@@ -32,7 +32,7 @@ test.describe('Keybindings', () => {
}) })
await comfyPage.executeCommand('TestCommand') await comfyPage.executeCommand('TestCommand')
await expect(comfyPage.page.locator('.p-toast')).toBeVisible() expect(await comfyPage.getToastErrorCount()).toBe(1)
}) })
test('Should handle async command errors', async ({ comfyPage }) => { test('Should handle async command errors', async ({ comfyPage }) => {
@@ -45,6 +45,6 @@ test.describe('Keybindings', () => {
}) })
await comfyPage.executeCommand('TestCommand') await comfyPage.executeCommand('TestCommand')
await expect(comfyPage.page.locator('.p-toast')).toBeVisible() expect(await comfyPage.getToastErrorCount()).toBe(1)
}) })
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,46 @@
<template>
<Toast group="reroute-migration">
<template #message>
<div class="flex flex-col items-start flex-auto">
<div class="font-medium text-lg my-4">
{{ t('toastMessages.migrateToLitegraphReroute') }}
</div>
<Button
class="self-end"
size="small"
:label="t('g.migrate')"
severity="warn"
@click="migrateToLitegraphReroute"
/>
</div>
</template>
</Toast>
</template>
<script setup lang="ts">
import { useToast } from 'primevue'
import Button from 'primevue/button'
import Toast from 'primevue/toast'
import { useI18n } from 'vue-i18n'
import type { WorkflowJSON04 } from '@/schemas/comfyWorkflowSchema'
import { app } from '@/scripts/app'
import { useWorkflowStore } from '@/stores/workflowStore'
import { migrateLegacyRerouteNodes } from '@/utils/migration/migrateReroute'
const { t } = useI18n()
const toast = useToast()
const workflowStore = useWorkflowStore()
const migrateToLitegraphReroute = () => {
const workflowJSON = app.serializeGraph() as unknown as WorkflowJSON04
const migratedWorkflowJSON = migrateLegacyRerouteNodes(workflowJSON)
app.loadGraphData(
migratedWorkflowJSON,
false,
false,
workflowStore.activeWorkflow
)
toast.removeGroup('reroute-migration')
}
</script>

View File

@@ -104,7 +104,8 @@
"interrupted": "Interrupted", "interrupted": "Interrupted",
"enabling": "Enabling", "enabling": "Enabling",
"disabling": "Disabling", "disabling": "Disabling",
"updating": "Updating" "updating": "Updating",
"migrate": "Migrate"
}, },
"manager": { "manager": {
"title": "Custom Nodes Manager", "title": "Custom Nodes Manager",
@@ -1024,6 +1025,7 @@
"errorSaveSetting": "Error saving setting {id}: {err}", "errorSaveSetting": "Error saving setting {id}: {err}",
"errorCopyImage": "Error copying image: {error}", "errorCopyImage": "Error copying image: {error}",
"noTemplatesToExport": "No templates to export", "noTemplatesToExport": "No templates to export",
"failedToFetchLogs": "Failed to fetch server logs" "failedToFetchLogs": "Failed to fetch server logs",
"migrateToLitegraphReroute": "Reroute nodes will be removed in future versions. Click to migrate to litegraph-native reroute."
} }
} }

View File

@@ -181,6 +181,7 @@
"loadWorkflow": "Cargar flujo de trabajo", "loadWorkflow": "Cargar flujo de trabajo",
"loading": "Cargando", "loading": "Cargando",
"logs": "Registros", "logs": "Registros",
"migrate": "Migrar",
"missing": "Faltante", "missing": "Faltante",
"name": "Nombre", "name": "Nombre",
"newFolder": "Nueva carpeta", "newFolder": "Nueva carpeta",
@@ -1000,6 +1001,7 @@
"fileLoadError": "No se puede encontrar el flujo de trabajo en {fileName}", "fileLoadError": "No se puede encontrar el flujo de trabajo en {fileName}",
"fileUploadFailed": "Error al subir el archivo", "fileUploadFailed": "Error al subir el archivo",
"interrupted": "La ejecución ha sido interrumpida", "interrupted": "La ejecución ha sido interrumpida",
"migrateToLitegraphReroute": "Los nodos de reroute se eliminarán en futuras versiones. Haz clic para migrar a reroute nativo de litegraph.",
"no3dScene": "No hay escena 3D para aplicar textura", "no3dScene": "No hay escena 3D para aplicar textura",
"no3dSceneToExport": "No hay escena 3D para exportar", "no3dSceneToExport": "No hay escena 3D para exportar",
"noTemplatesToExport": "No hay plantillas para exportar", "noTemplatesToExport": "No hay plantillas para exportar",

View File

@@ -181,6 +181,7 @@
"loadWorkflow": "Charger le flux de travail", "loadWorkflow": "Charger le flux de travail",
"loading": "Chargement", "loading": "Chargement",
"logs": "Journaux", "logs": "Journaux",
"migrate": "Migrer",
"missing": "Manquant", "missing": "Manquant",
"name": "Nom", "name": "Nom",
"newFolder": "Nouveau dossier", "newFolder": "Nouveau dossier",
@@ -1000,6 +1001,7 @@
"fileLoadError": "Impossible de trouver le flux de travail dans {fileName}", "fileLoadError": "Impossible de trouver le flux de travail dans {fileName}",
"fileUploadFailed": "Échec du téléchargement du fichier", "fileUploadFailed": "Échec du téléchargement du fichier",
"interrupted": "L'exécution a été interrompue", "interrupted": "L'exécution a été interrompue",
"migrateToLitegraphReroute": "Les nœuds de reroute seront supprimés dans les futures versions. Cliquez pour migrer vers le reroute natif de litegraph.",
"no3dScene": "Aucune scène 3D pour appliquer la texture", "no3dScene": "Aucune scène 3D pour appliquer la texture",
"no3dSceneToExport": "Aucune scène 3D à exporter", "no3dSceneToExport": "Aucune scène 3D à exporter",
"noTemplatesToExport": "Aucun modèle à exporter", "noTemplatesToExport": "Aucun modèle à exporter",

View File

@@ -181,6 +181,7 @@
"loadWorkflow": "ワークフローを読み込む", "loadWorkflow": "ワークフローを読み込む",
"loading": "読み込み中", "loading": "読み込み中",
"logs": "ログ", "logs": "ログ",
"migrate": "移行する",
"missing": "不足している", "missing": "不足している",
"name": "名前", "name": "名前",
"newFolder": "新しいフォルダー", "newFolder": "新しいフォルダー",
@@ -1000,6 +1001,7 @@
"fileLoadError": "{fileName}でワークフローが見つかりません", "fileLoadError": "{fileName}でワークフローが見つかりません",
"fileUploadFailed": "ファイルのアップロードに失敗しました", "fileUploadFailed": "ファイルのアップロードに失敗しました",
"interrupted": "実行が中断されました", "interrupted": "実行が中断されました",
"migrateToLitegraphReroute": "将来のバージョンではRerouteードが削除されます。litegraph-native rerouteに移行するにはクリックしてください。",
"no3dScene": "テクスチャを適用する3Dシーンがありません", "no3dScene": "テクスチャを適用する3Dシーンがありません",
"no3dSceneToExport": "エクスポートする3Dシーンがありません", "no3dSceneToExport": "エクスポートする3Dシーンがありません",
"noTemplatesToExport": "エクスポートするテンプレートがありません", "noTemplatesToExport": "エクスポートするテンプレートがありません",

View File

@@ -181,6 +181,7 @@
"loadWorkflow": "워크플로 로드", "loadWorkflow": "워크플로 로드",
"loading": "로딩 중", "loading": "로딩 중",
"logs": "로그", "logs": "로그",
"migrate": "마이그레이트",
"missing": "누락됨", "missing": "누락됨",
"name": "이름", "name": "이름",
"newFolder": "새 폴더", "newFolder": "새 폴더",
@@ -1000,6 +1001,7 @@
"fileLoadError": "{fileName}에서 워크플로우를 찾을 수 없습니다", "fileLoadError": "{fileName}에서 워크플로우를 찾을 수 없습니다",
"fileUploadFailed": "파일 업로드에 실패했습니다", "fileUploadFailed": "파일 업로드에 실패했습니다",
"interrupted": "실행이 중단되었습니다", "interrupted": "실행이 중단되었습니다",
"migrateToLitegraphReroute": "미래 버전에서는 Reroute 노드가 제거됩니다. litegraph-native reroute로 마이그레이트하려면 클릭하세요.",
"no3dScene": "텍스처를 적용할 3D 장면이 없습니다", "no3dScene": "텍스처를 적용할 3D 장면이 없습니다",
"no3dSceneToExport": "내보낼 3D 장면이 없습니다", "no3dSceneToExport": "내보낼 3D 장면이 없습니다",
"noTemplatesToExport": "내보낼 템플릿이 없습니다", "noTemplatesToExport": "내보낼 템플릿이 없습니다",

View File

@@ -181,6 +181,7 @@
"loadWorkflow": "Загрузить рабочий процесс", "loadWorkflow": "Загрузить рабочий процесс",
"loading": "Загрузка", "loading": "Загрузка",
"logs": "Логи", "logs": "Логи",
"migrate": "Мигрировать",
"missing": "Отсутствует", "missing": "Отсутствует",
"name": "Имя", "name": "Имя",
"newFolder": "Новая папка", "newFolder": "Новая папка",
@@ -1000,6 +1001,7 @@
"fileLoadError": "Не удалось найти рабочий процесс в {fileName}", "fileLoadError": "Не удалось найти рабочий процесс в {fileName}",
"fileUploadFailed": "Не удалось загрузить файл", "fileUploadFailed": "Не удалось загрузить файл",
"interrupted": "Выполнение было прервано", "interrupted": "Выполнение было прервано",
"migrateToLitegraphReroute": "Узлы перенаправления будут удалены в будущих версиях. Нажмите, чтобы перейти на litegraph-native reroute.",
"no3dScene": "Нет 3D сцены для применения текстуры", "no3dScene": "Нет 3D сцены для применения текстуры",
"no3dSceneToExport": "Нет 3D сцены для экспорта", "no3dSceneToExport": "Нет 3D сцены для экспорта",
"noTemplatesToExport": "Нет шаблонов для экспорта", "noTemplatesToExport": "Нет шаблонов для экспорта",

View File

@@ -181,6 +181,7 @@
"loadWorkflow": "加载工作流", "loadWorkflow": "加载工作流",
"loading": "加载中", "loading": "加载中",
"logs": "日志", "logs": "日志",
"migrate": "迁移",
"missing": "缺失", "missing": "缺失",
"name": "名称", "name": "名称",
"newFolder": "新文件夹", "newFolder": "新文件夹",
@@ -1000,6 +1001,7 @@
"fileLoadError": "无法在 {fileName} 中找到工作流", "fileLoadError": "无法在 {fileName} 中找到工作流",
"fileUploadFailed": "文件上传失败", "fileUploadFailed": "文件上传失败",
"interrupted": "执行已被中断", "interrupted": "执行已被中断",
"migrateToLitegraphReroute": "将来的版本中将删除重定向节点。点击以迁移到litegraph-native重定向。",
"no3dScene": "没有3D场景可以应用纹理", "no3dScene": "没有3D场景可以应用纹理",
"no3dSceneToExport": "没有3D场景可以导出", "no3dSceneToExport": "没有3D场景可以导出",
"noTemplatesToExport": "没有模板可以导出", "noTemplatesToExport": "没有模板可以导出",

View File

@@ -47,7 +47,7 @@ import { ExtensionManager } from '@/types/extensionTypes'
import { ColorAdjustOptions, adjustColor } from '@/utils/colorUtil' import { ColorAdjustOptions, adjustColor } from '@/utils/colorUtil'
import { graphToPrompt } from '@/utils/executionUtil' import { graphToPrompt } from '@/utils/executionUtil'
import { executeWidgetsCallback, isImageNode } from '@/utils/litegraphUtil' import { executeWidgetsCallback, isImageNode } from '@/utils/litegraphUtil'
import { migrateLegacyRerouteNodes } from '@/utils/migration/migrateReroute' import { findLegacyRerouteNodes } from '@/utils/migration/migrateReroute'
import { deserialiseAndCreate } from '@/utils/vintageClipboard' import { deserialiseAndCreate } from '@/utils/vintageClipboard'
import { type ComfyApi, PromptExecutionError, api } from './api' import { type ComfyApi, PromptExecutionError, api } from './api'
@@ -945,7 +945,11 @@ export class ComfyApp {
clean: boolean = true, clean: boolean = true,
restore_view: boolean = true, restore_view: boolean = true,
workflow: string | null | ComfyWorkflow = null, workflow: string | null | ComfyWorkflow = null,
{ showMissingNodesDialog = true, showMissingModelsDialog = true } = {} {
showMissingNodesDialog = true,
showMissingModelsDialog = true,
checkForRerouteMigration = true
} = {}
) { ) {
if (clean !== false) { if (clean !== false) {
this.clean() this.clean()
@@ -972,11 +976,16 @@ export class ComfyApp {
graphData = validatedGraphData ?? graphData graphData = validatedGraphData ?? graphData
} }
// Migrate legacy reroute nodes to the new format if (
if (graphData.version === 0.4) { checkForRerouteMigration &&
graphData = migrateLegacyRerouteNodes(graphData) graphData.version === 0.4 &&
findLegacyRerouteNodes(graphData).length
) {
useToastStore().add({
group: 'reroute-migration',
severity: 'warn'
})
} }
useWorkflowService().beforeLoadNewGraph() useWorkflowService().beforeLoadNewGraph()
const missingNodeTypes: MissingNodeType[] = [] const missingNodeTypes: MissingNodeType[] = []

View File

@@ -135,7 +135,8 @@ export class ChangeTracker {
try { try {
await this.app.loadGraphData(prevState, false, false, this.workflow, { await this.app.loadGraphData(prevState, false, false, this.workflow, {
showMissingModelsDialog: false, showMissingModelsDialog: false,
showMissingNodesDialog: false showMissingNodesDialog: false,
checkForRerouteMigration: false
}) })
this.activeState = prevState this.activeState = prevState
this.updateModified() this.updateModified()

View File

@@ -3,10 +3,7 @@ import type { SerialisableGraph, Vector2 } from '@comfyorg/litegraph'
import { toRaw } from 'vue' import { toRaw } from 'vue'
import { t } from '@/i18n' import { t } from '@/i18n'
import { import { ComfyWorkflowJSON } from '@/schemas/comfyWorkflowSchema'
ComfyWorkflowJSON,
WorkflowJSON04
} from '@/schemas/comfyWorkflowSchema'
import { app } from '@/scripts/app' import { app } from '@/scripts/app'
import { blankGraph, defaultGraph } from '@/scripts/defaultGraph' import { blankGraph, defaultGraph } from '@/scripts/defaultGraph'
import { downloadBlob } from '@/scripts/utils' import { downloadBlob } from '@/scripts/utils'
@@ -15,7 +12,6 @@ import { useToastStore } from '@/stores/toastStore'
import { ComfyWorkflow, useWorkflowStore } from '@/stores/workflowStore' import { ComfyWorkflow, useWorkflowStore } from '@/stores/workflowStore'
import { useWorkspaceStore } from '@/stores/workspaceStore' import { useWorkspaceStore } from '@/stores/workspaceStore'
import { appendJsonExt } from '@/utils/formatUtil' import { appendJsonExt } from '@/utils/formatUtil'
import { migrateLegacyRerouteNodes } from '@/utils/migration/migrateReroute'
import { useDialogService } from './dialogService' import { useDialogService } from './dialogService'
@@ -167,7 +163,8 @@ export const useWorkflowService = () => {
workflow, workflow,
{ {
showMissingModelsDialog: loadFromRemote, showMissingModelsDialog: loadFromRemote,
showMissingNodesDialog: loadFromRemote showMissingNodesDialog: loadFromRemote,
checkForRerouteMigration: loadFromRemote
} }
) )
} }
@@ -328,10 +325,7 @@ export const useWorkflowService = () => {
) => { ) => {
const loadedWorkflow = await workflow.load() const loadedWorkflow = await workflow.load()
const data = loadedWorkflow.initialState const data = loadedWorkflow.initialState
const workflowJSON = const workflowJSON = data
data.version === 0.4
? migrateLegacyRerouteNodes(data as WorkflowJSON04)
: data
const old = localStorage.getItem('litegrapheditor_clipboard') const old = localStorage.getItem('litegrapheditor_clipboard')
// unknown conversion: ComfyWorkflowJSON is stricter than LiteGraph's // unknown conversion: ComfyWorkflowJSON is stricter than LiteGraph's
// serialisation schema. // serialisation schema.

View File

@@ -20,7 +20,9 @@ type LinkExtension = {
/** /**
* Identifies all legacy Reroute nodes in a workflow * Identifies all legacy Reroute nodes in a workflow
*/ */
function findLegacyRerouteNodes(workflow: WorkflowJSON04): RerouteNode[] { export function findLegacyRerouteNodes(
workflow: WorkflowJSON04
): RerouteNode[] {
return workflow.nodes.filter( return workflow.nodes.filter(
(node) => node.type === 'Reroute' (node) => node.type === 'Reroute'
) as RerouteNode[] ) as RerouteNode[]

View File

@@ -14,6 +14,7 @@
</div> </div>
<GlobalToast /> <GlobalToast />
<RerouteMigrationToast />
<UnloadWindowConfirmDialog v-if="!isElectron()" /> <UnloadWindowConfirmDialog v-if="!isElectron()" />
<BrowserTabTitle /> <BrowserTabTitle />
<MenuHamburger /> <MenuHamburger />
@@ -31,6 +32,7 @@ import MenuHamburger from '@/components/MenuHamburger.vue'
import UnloadWindowConfirmDialog from '@/components/dialog/UnloadWindowConfirmDialog.vue' import UnloadWindowConfirmDialog from '@/components/dialog/UnloadWindowConfirmDialog.vue'
import GraphCanvas from '@/components/graph/GraphCanvas.vue' import GraphCanvas from '@/components/graph/GraphCanvas.vue'
import GlobalToast from '@/components/toast/GlobalToast.vue' import GlobalToast from '@/components/toast/GlobalToast.vue'
import RerouteMigrationToast from '@/components/toast/RerouteMigrationToast.vue'
import TopMenubar from '@/components/topbar/TopMenubar.vue' import TopMenubar from '@/components/topbar/TopMenubar.vue'
import { useCoreCommands } from '@/composables/useCoreCommands' import { useCoreCommands } from '@/composables/useCoreCommands'
import { useErrorHandling } from '@/composables/useErrorHandling' import { useErrorHandling } from '@/composables/useErrorHandling'