From c56533bb23474eebeaf75f30be3fdb8e2fcae339 Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Tue, 5 Nov 2024 11:03:27 -0500 Subject: [PATCH] Workflow Management Reworked (#1406) * Merge temp userfile Basic migration Remove deprecated isFavourite Rename nit nit Rework open/load Refactor save Refactor delete Remove workflow dep on manager WIP Change map to record Fix directory nit isActive Move nit Add unload Add close workflow Remove workflowManager.closeWorkflow nit Remove workflowManager.storePrompt move from commandStore move more from commandStore nit Use workflowservice nit nit implement setWorkflow nit Remove workflows.ts Fix strict errors nit nit Resolves circular dep nit nit Fix workflow switching Add openworkflowPaths Fix store Fix key Serialize by default Fix proxy nit Update path Proper sync Fix tabs WIP nit Resolve merge conflict Fix userfile store tests Update jest test Update tabs patch tests Fix changeTracker init Move insert to service nit Fix insert nit Handle bookmark rename Refactor tests Add delete workflow Add test on deleting workflow Add closeWorkflow tests nit * Fix path * Move load next/previous * Move logic from store to service * nit * nit * nit * nit * nit * Add ChangeTracker.initialState * ChangeTracker load/unload * Remove app.changeWorkflow * Hook to app.ts * Changetracker restore * nit * nit * nit * Add debug logs * Remove unnecessary checkState on graphLoad * nit * Fix strict * Fix temp workflow name * Track ismodified * Fix reactivity * nit * Fix graph equal * nit * update test * nit * nit * Fix modified state * nit * Fix modified state * Sidebar force close * tabs force close * Fix save * Add load remote workflow test * Force save * Add save test * nit * Correctly handle delete last opened workflow * nit * Fix workflow rename * Fix save * Fix tests * Fix strict * Update playwright tests * Fix filename conflict handling * nit * Merge temporary and persisted ref * Update playwright expectations * nit * nit * Fix saveAs * Add playwright test * nit --- browser_tests/actionbar.spec.ts | 4 +- browser_tests/browserTabTitle.spec.ts | 18 +- browser_tests/fixtures/components/Topbar.ts | 8 + browser_tests/menu.spec.ts | 19 +- src/components/BrowserTabTitle.vue | 4 +- src/components/graph/GraphCanvas.vue | 6 +- .../sidebar/tabs/WorkflowsSidebarTab.vue | 42 +- .../tabs/workflows/WorkflowTreeLeaf.vue | 3 +- src/components/topbar/WorkflowTabs.vue | 38 +- src/scripts/app.ts | 118 +++-- src/scripts/changeTracker.ts | 154 ++++--- src/scripts/defaultGraph.ts | 13 + src/scripts/pnginfo.ts | 1 + src/scripts/ui/components/asyncDialog.ts | 10 +- src/scripts/utils.ts | 2 +- src/scripts/workflows.ts | 430 ------------------ src/services/workflowService.ts | 241 +++++++++- src/stores/commandStore.ts | 39 +- src/stores/executionStore.ts | 2 +- src/stores/userFileStore.ts | 169 +++---- src/stores/workflowStore.ts | 363 +++++++++++++-- src/stores/workspaceStore.ts | 3 + src/utils/formatUtil.ts | 55 +++ src/utils/syncUtil.ts | 45 ++ src/views/GraphView.vue | 10 - .../tests/fast/store/userFileStore.test.ts | 29 +- .../tests/fast/store/workflowStore.test.ts | 358 +++++++++++++++ tests-ui/tests/globalSetup.ts | 9 +- 28 files changed, 1409 insertions(+), 784 deletions(-) delete mode 100644 src/scripts/workflows.ts create mode 100644 src/utils/syncUtil.ts create mode 100644 tests-ui/tests/fast/store/workflowStore.test.ts diff --git a/browser_tests/actionbar.spec.ts b/browser_tests/actionbar.spec.ts index 96a2d3a72..b85e284f8 100644 --- a/browser_tests/actionbar.spec.ts +++ b/browser_tests/actionbar.spec.ts @@ -52,7 +52,9 @@ test.describe('Actionbar', () => { (n) => n.type === 'EmptyLatentImage' ) node.widgets[0].value = value - window['app'].workflowManager.activeWorkflow.changeTracker.checkState() + window[ + 'app' + ].extensionManager.workflow.activeWorkflow.changeTracker.checkState() }, value) } diff --git a/browser_tests/browserTabTitle.spec.ts b/browser_tests/browserTabTitle.spec.ts index b5c02812a..43439bbd9 100644 --- a/browser_tests/browserTabTitle.spec.ts +++ b/browser_tests/browserTabTitle.spec.ts @@ -9,24 +9,22 @@ test.describe('Browser tab title', () => { test('Can display workflow name', async ({ comfyPage }) => { const workflowName = await comfyPage.page.evaluate(async () => { - return window['app'].workflowManager.activeWorkflow.name + return window['app'].extensionManager.workflow.activeWorkflow.filename }) - // Note: unsaved workflow name is always prepended with "*". - expect(await comfyPage.page.title()).toBe(`*${workflowName} - ComfyUI`) + expect(await comfyPage.page.title()).toBe(`${workflowName} - ComfyUI`) }) - // Broken by https://github.com/Comfy-Org/ComfyUI_frontend/pull/893 - // Release blocker for v1.3.0 + // Failing on CI + // Cannot reproduce locally test.skip('Can display workflow name with unsaved changes', async ({ comfyPage }) => { const workflowName = await comfyPage.page.evaluate(async () => { - return window['app'].workflowManager.activeWorkflow.name + return window['app'].extensionManager.workflow.activeWorkflow.filename }) - // Note: unsaved workflow name is always prepended with "*". - expect(await comfyPage.page.title()).toBe(`*${workflowName} - ComfyUI`) + expect(await comfyPage.page.title()).toBe(`${workflowName} - ComfyUI`) - await comfyPage.menu.saveWorkflow('test') + await comfyPage.menu.topbar.saveWorkflow('test') expect(await comfyPage.page.title()).toBe('test - ComfyUI') const textBox = comfyPage.widgetTextBox @@ -36,7 +34,7 @@ test.describe('Browser tab title', () => { // Delete the saved workflow for cleanup. await comfyPage.page.evaluate(async () => { - window['app'].workflowManager.activeWorkflow.delete() + return window['app'].extensionManager.workflow.activeWorkflow.delete() }) }) }) diff --git a/browser_tests/fixtures/components/Topbar.ts b/browser_tests/fixtures/components/Topbar.ts index fc0692b66..bc64fea1d 100644 --- a/browser_tests/fixtures/components/Topbar.ts +++ b/browser_tests/fixtures/components/Topbar.ts @@ -36,6 +36,14 @@ export class Topbar { await this.page.waitForTimeout(300) } + async saveWorkflowAs(workflowName: string) { + await this.triggerTopbarCommand(['Workflow', 'Save As']) + await this.page.locator('.p-dialog-content input').fill(workflowName) + await this.page.keyboard.press('Enter') + // Wait for the dialog to close. + await this.page.waitForTimeout(300) + } + async triggerTopbarCommand(path: string[]) { if (path.length < 2) { throw new Error('Path is too short') diff --git a/browser_tests/menu.spec.ts b/browser_tests/menu.spec.ts index 5b2f33dfa..adf509238 100644 --- a/browser_tests/menu.spec.ts +++ b/browser_tests/menu.spec.ts @@ -392,7 +392,7 @@ test.describe('Menu', () => { await tab.newBlankWorkflowButton.click() expect(await tab.getOpenedWorkflowNames()).toEqual([ '*Unsaved Workflow.json', - '*Unsaved Workflow (2).json' + 'Unsaved Workflow (2).json' ]) }) @@ -411,6 +411,19 @@ test.describe('Menu', () => { ) }) + test('Can save workflow as', async ({ comfyPage }) => { + await comfyPage.menu.workflowsTab.newBlankWorkflowButton.click() + await comfyPage.menu.topbar.saveWorkflowAs('workflow3.json') + expect( + await comfyPage.menu.workflowsTab.getOpenedWorkflowNames() + ).toEqual(['*Unsaved Workflow.json', 'workflow3.json']) + + await comfyPage.menu.topbar.saveWorkflowAs('workflow4.json') + expect( + await comfyPage.menu.workflowsTab.getOpenedWorkflowNames() + ).toEqual(['*Unsaved Workflow.json', 'workflow3.json', 'workflow4.json']) + }) + test('Does not report warning when switching between opened workflows', async ({ comfyPage }) => { @@ -441,7 +454,7 @@ test.describe('Menu', () => { await closeButton.click() expect( await comfyPage.menu.workflowsTab.getOpenedWorkflowNames() - ).toEqual(['*Unsaved Workflow (2).json']) + ).toEqual(['Unsaved Workflow.json']) }) }) @@ -466,7 +479,7 @@ test.describe('Menu', () => { expect(await comfyPage.menu.topbar.getTabNames()).toEqual([workflowName]) await comfyPage.menu.topbar.closeWorkflowTab(workflowName) expect(await comfyPage.menu.topbar.getTabNames()).toEqual([ - 'Unsaved Workflow (2)' + 'Unsaved Workflow' ]) }) }) diff --git a/src/components/BrowserTabTitle.vue b/src/components/BrowserTabTitle.vue index 34cd792a7..33647e2f6 100644 --- a/src/components/BrowserTabTitle.vue +++ b/src/components/BrowserTabTitle.vue @@ -26,10 +26,10 @@ const betaMenuEnabled = computed( const workflowStore = useWorkflowStore() const isUnsavedText = computed(() => - workflowStore.activeWorkflow?.unsaved ? ' *' : '' + workflowStore.activeWorkflow?.isModified ? ' *' : '' ) const workflowNameText = computed(() => { - const workflowName = workflowStore.activeWorkflow?.name + const workflowName = workflowStore.activeWorkflow?.filename return workflowName ? isUnsavedText.value + workflowName + TITLE_SUFFIX : DEFAULT_TITLE diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index 2e993e329..5465e4410 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -55,6 +55,7 @@ import GraphCanvasMenu from '@/components/graph/GraphCanvasMenu.vue' import { usePragmaticDroppable } from '@/hooks/dndHooks' import { useWorkflowStore } from '@/stores/workflowStore' import { setStorageValue } from '@/scripts/utils' +import { ChangeTracker } from '@/scripts/changeTracker' const emit = defineEmits(['ready']) const canvasRef = ref(null) @@ -147,7 +148,7 @@ const workflowStore = useWorkflowStore() watchEffect(() => { if (workflowStore.activeWorkflow) { const workflow = workflowStore.activeWorkflow - setStorageValue('Comfy.PreviousWorkflow', workflow.path ?? workflow.name) + setStorageValue('Comfy.PreviousWorkflow', workflow.key) } }) @@ -222,6 +223,9 @@ onMounted(async () => { comfyApp.vueAppReady = true workspaceStore.spinner = true + // ChangeTracker needs to be initialized before setup, as it will overwrite + // some listeners of litegraph canvas. + ChangeTracker.init(comfyApp) await comfyApp.setup(canvasRef.value) canvasStore.canvas = comfyApp.canvas workspaceStore.spinner = false diff --git a/src/components/sidebar/tabs/WorkflowsSidebarTab.vue b/src/components/sidebar/tabs/WorkflowsSidebarTab.vue index 5c1388689..df8c6e90f 100644 --- a/src/components/sidebar/tabs/WorkflowsSidebarTab.vue +++ b/src/components/sidebar/tabs/WorkflowsSidebarTab.vue @@ -43,20 +43,22 @@