From c0875d066aacfa72162ddd18e47662b5ad0f76bd Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Sat, 27 Jul 2024 21:28:48 -0400 Subject: [PATCH] Node library side bar tab (#237) * Basic tree * Add node filter * Fix key issue * Add icons * Node count * nit * Add node preview basics * Node preview * Make comfy node in node lib draggable * Set drop target * Add node on drop * Drop on dynamic location * nit * nit * Fix hover preview issue * nit * More visual diff between node and folder * Add playwright test * Add dep * Get rid of screenshot test --- browser_tests/ComfyPage.ts | 46 ++++++++ browser_tests/menu.spec.ts | 25 +++++ package-lock.json | 28 ++++- package.json | 1 + src/App.vue | 33 +++++- src/components/primevueOverride/TreePlus.vue | 106 ++++++++++++++++++ src/components/sidebar/SideToolBar.vue | 1 + .../sidebar/tabs/NodeLibrarySideBarTab.vue | 99 ++++++++++++++++ src/i18n.ts | 6 +- src/scripts/app.ts | 9 ++ src/stores/nodeDefStore.ts | 28 +++++ 11 files changed, 376 insertions(+), 6 deletions(-) create mode 100644 src/components/primevueOverride/TreePlus.vue create mode 100644 src/components/sidebar/tabs/NodeLibrarySideBarTab.vue diff --git a/browser_tests/ComfyPage.ts b/browser_tests/ComfyPage.ts index 593aef8e5..38404fab9 100644 --- a/browser_tests/ComfyPage.ts +++ b/browser_tests/ComfyPage.ts @@ -37,6 +37,42 @@ class ComfyNodeSearchBox { } } +class NodeLibrarySideBarTab { + public readonly tabId: string = 'node-library' + constructor(public readonly page: Page) {} + + get tabButton() { + return this.page.locator(`.${this.tabId}-tab-button`) + } + + get selectedTabButton() { + return this.page.locator( + `.${this.tabId}-tab-button.side-bar-button-selected` + ) + } + + get nodeLibraryTree() { + return this.page.locator('.node-lib-tree') + } + + get nodePreview() { + return this.page.locator('.node-lib-node-preview') + } + + async open() { + if (await this.selectedTabButton.isVisible()) { + return + } + + await this.tabButton.click() + await this.nodeLibraryTree.waitFor({ state: 'visible' }) + } + + async toggleFirstFolder() { + await this.page.locator('.p-tree-node-toggle-button').nth(0).click() + } +} + class ComfyMenu { public readonly sideToolBar: Locator public readonly themeToggleButton: Locator @@ -46,6 +82,10 @@ class ComfyMenu { this.themeToggleButton = page.locator('.comfy-vue-theme-toggle') } + get nodeLibraryTab() { + return new NodeLibrarySideBarTab(this.page) + } + async toggleTheme() { await this.themeToggleButton.click() await this.page.evaluate(() => { @@ -96,6 +136,12 @@ export class ComfyPage { this.menu = new ComfyMenu(page) } + async getGraphNodesCount(): Promise { + return await this.page.evaluate(() => { + return window['app']?.graph?._nodes?.length || 0 + }) + } + async setup() { await this.goto() // Unify font for consistent screenshots. diff --git a/browser_tests/menu.spec.ts b/browser_tests/menu.spec.ts index 369cc27c4..0ab6cefa2 100644 --- a/browser_tests/menu.spec.ts +++ b/browser_tests/menu.spec.ts @@ -67,4 +67,29 @@ test.describe('Menu', () => { ) expect(newChildrenCount).toBe(initialChildrenCount + 1) }) + + test('Sidebar node preview and drag to canvas', async ({ comfyPage }) => { + // Open the sidebar + const tab = comfyPage.menu.nodeLibraryTab + await tab.open() + await tab.toggleFirstFolder() + + // Hover over a node to display the preview + const nodeSelector = '.p-tree-node-leaf' + await comfyPage.page.hover(nodeSelector) + + // Verify the preview is displayed + const previewVisible = await comfyPage.page.isVisible( + '.node-lib-node-preview' + ) + expect(previewVisible).toBe(true) + + const count = await comfyPage.getGraphNodesCount() + // Drag the node onto the canvas + const canvasSelector = '#graph-canvas' + await comfyPage.page.dragAndDrop(nodeSelector, canvasSelector) + + // Verify the node is added to the canvas + expect(await comfyPage.getGraphNodesCount()).toBe(count + 1) + }) }) diff --git a/package-lock.json b/package-lock.json index 2b59213cf..2bc4bf884 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "comfyui-frontend", "version": "1.2.2", "dependencies": { + "@atlaskit/pragmatic-drag-and-drop": "^1.2.1", "@comfyorg/litegraph": "^0.7.29", "@primevue/themes": "^4.0.0-rc.2", "@vitejs/plugin-vue": "^5.0.5", @@ -78,6 +79,17 @@ "node": ">=6.0.0" } }, + "node_modules/@atlaskit/pragmatic-drag-and-drop": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@atlaskit/pragmatic-drag-and-drop/-/pragmatic-drag-and-drop-1.2.1.tgz", + "integrity": "sha512-gW2wJblFAeg94YXITHg0YdhFM2nmFAdDmX0LKYBIm79yEbIrOiuHHukgSjII07M4U5JpJ0Ff/4BaADjN23ix+A==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.0.0", + "bind-event-listener": "^3.0.0", + "raf-schd": "^4.0.3" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", @@ -1755,7 +1767,6 @@ "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -4055,6 +4066,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bind-event-listener": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bind-event-listener/-/bind-event-listener-3.0.0.tgz", + "integrity": "sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8573,6 +8590,12 @@ } ] }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==", + "license": "MIT" + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -8641,8 +8664,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", diff --git a/package.json b/package.json index 5777f8b1d..f53088908 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "zip-dir": "^2.0.0" }, "dependencies": { + "@atlaskit/pragmatic-drag-and-drop": "^1.2.1", "@comfyorg/litegraph": "^0.7.29", "@primevue/themes": "^4.0.0-rc.2", "@vitejs/plugin-vue": "^5.0.5", diff --git a/src/App.vue b/src/App.vue index 691dbdfc1..ad967f23e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -13,7 +13,7 @@