diff --git a/.github/workflows/test-browser-exp.yaml b/.github/workflows/test-browser-exp.yaml index 80a7239e..4e5481b9 100644 --- a/.github/workflows/test-browser-exp.yaml +++ b/.github/workflows/test-browser-exp.yaml @@ -5,7 +5,6 @@ name: Update Playwright Expectations on: pull_request: types: [ labeled ] - branches: [ main, master ] jobs: test: diff --git a/browser_tests/ComfyPage.ts b/browser_tests/ComfyPage.ts index d95bc97b..53d7bc70 100644 --- a/browser_tests/ComfyPage.ts +++ b/browser_tests/ComfyPage.ts @@ -1,5 +1,6 @@ import type { Page, Locator } from '@playwright/test' import { test as base } from '@playwright/test' +import { expect } from '@playwright/test' import dotenv from 'dotenv' dotenv.config() import * as fs from 'fs' @@ -138,6 +139,15 @@ class NodeLibrarySidebarTab { await this.nodeLibraryTree.waitFor({ state: 'visible' }) } + async close() { + if (!this.tabButton.isVisible()) { + return + } + + await this.tabButton.click() + await this.nodeLibraryTree.waitFor({ state: 'hidden' }) + } + folderSelector(folderName: string) { return `.p-tree-node-content:has(> .tree-explorer-node-label:has(.tree-folder .node-label:has-text("${folderName}")))` } @@ -705,8 +715,8 @@ export class ComfyPage { await dialog.accept(groupNodeName) }) await this.canvas.press('Control+a') - await this.rightClickEmptyLatentNode() - await this.page.getByText('Convert to Group Node').click() + const node = await this.getFirstNodeRef() + await node!.clickContextMenuOption('Convert to Group Node') await this.nextFrame() } async convertOffsetToCanvas(pos: [number, number]) { @@ -721,13 +731,20 @@ export class ComfyPage { return Promise.all( ( await this.page.evaluate((type) => { - return window['app'].graph._nodes + return window['app'].graph.nodes .filter((n) => n.type === type) .map((n) => n.id) }, type) ).map((id: NodeId) => this.getNodeRefById(id)) ) } + async getFirstNodeRef(): Promise { + const id = await this.page.evaluate(() => { + return window['app'].graph.nodes[0]?.id + }) + if (!id) return null + return this.getNodeRefById(id) + } } class NodeSlotReference { constructor( @@ -867,7 +884,7 @@ class NodeReference { await this.clickContextMenuOption('Convert to Group Node') await this.comfyPage.nextFrame() const nodes = await this.comfyPage.getNodeRefsByType( - `workflow/${groupNodeName}` + `workflow>${groupNodeName}` ) if (nodes.length !== 1) { throw new Error(`Did not find single group node (found=${nodes.length})`) diff --git a/browser_tests/assets/every_node_color.json b/browser_tests/assets/every_node_color.json new file mode 100644 index 00000000..79c06738 --- /dev/null +++ b/browser_tests/assets/every_node_color.json @@ -0,0 +1,504 @@ +{ + "last_node_id": 13, + "last_link_id": 9, + "nodes": [ + { + "id": 3, + "type": "KSampler", + "pos": { + "0": 863, + "1": 186 + }, + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 1 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 4 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 6 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 2 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 7 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 156680208700286, + "randomize", + 20, + 8, + "euler", + "normal", + 1 + ], + "color": "#432", + "bgcolor": "#653" + }, + { + "id": 4, + "type": "CheckpointLoaderSimple", + "pos": { + "0": 36, + "1": 172 + }, + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 1 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 3, + 5 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 8 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "Stable-diffusion/v1-5-pruned-emaonly.safetensors" + ], + "color": "#322", + "bgcolor": "#533" + }, + { + "id": 5, + "type": "EmptyLatentImage", + "pos": { + "0": 473, + "1": 609 + }, + "size": { + "0": 315, + "1": 106 + }, + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 2 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ], + "color": "#323", + "bgcolor": "#535" + }, + { + "id": 6, + "type": "CLIPTextEncode", + "pos": { + "0": 415, + "1": 186 + }, + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 3 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 4 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "beautiful scenery nature glass bottle landscape, , purple galaxy bottle," + ], + "color": "#233", + "bgcolor": "#355" + }, + { + "id": 7, + "type": "CLIPTextEncode", + "pos": { + "0": 413, + "1": 389 + }, + "size": { + "0": 425.27801513671875, + "1": 180.6060791015625 + }, + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 5 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 6 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "text, watermark" + ], + "color": "#323", + "bgcolor": "#535" + }, + { + "id": 8, + "type": "VAEDecode", + "pos": { + "0": 866, + "1": 502 + }, + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 7 + }, + { + "name": "vae", + "type": "VAE", + "link": 8 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 9 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + }, + "widgets_values": [], + "color": "#222", + "bgcolor": "#000" + }, + { + "id": 9, + "type": "SaveImage", + "pos": { + "0": 857, + "1": 611 + }, + "size": [ + 214.2000732421875, + 59.4000244140625 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 9 + } + ], + "outputs": [], + "properties": {}, + "widgets_values": [ + "ComfyUI" + ] + }, + { + "id": 10, + "type": "CheckpointLoaderSimple", + "pos": { + "0": 42, + "1": 329 + }, + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": null + }, + { + "name": "CLIP", + "type": "CLIP", + "links": null + }, + { + "name": "VAE", + "type": "VAE", + "links": null + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "Stable-diffusion/v1-5-pruned-emaonly.safetensors" + ], + "color": "#332922", + "bgcolor": "#593930" + }, + { + "id": 11, + "type": "CheckpointLoaderSimple", + "pos": { + "0": 40, + "1": 494 + }, + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": null + }, + { + "name": "CLIP", + "type": "CLIP", + "links": null + }, + { + "name": "VAE", + "type": "VAE", + "links": null + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "Stable-diffusion/v1-5-pruned-emaonly.safetensors" + ], + "color": "#223", + "bgcolor": "#335" + }, + { + "id": 13, + "type": "ImageScale", + "pos": { + "0": 42, + "1": 650 + }, + "size": { + "0": 315, + "1": 130 + }, + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": null + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": null + } + ], + "properties": { + "Node name for S&R": "ImageScale" + }, + "widgets_values": [ + "nearest-exact", + 512, + 512, + "disabled" + ], + "color": "#2a363b", + "bgcolor": "#3f5159" + } + ], + "links": [ + [ + 1, + 4, + 0, + 3, + 0, + "MODEL" + ], + [ + 2, + 5, + 0, + 3, + 3, + "LATENT" + ], + [ + 3, + 4, + 1, + 6, + 0, + "CLIP" + ], + [ + 4, + 6, + 0, + 3, + 1, + "CONDITIONING" + ], + [ + 5, + 4, + 1, + 7, + 0, + "CLIP" + ], + [ + 6, + 7, + 0, + 3, + 2, + "CONDITIONING" + ], + [ + 7, + 3, + 0, + 8, + 0, + "LATENT" + ], + [ + 8, + 4, + 2, + 8, + 1, + "VAE" + ], + [ + 9, + 8, + 0, + 9, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file diff --git a/browser_tests/assets/force_input.json b/browser_tests/assets/force_input.json new file mode 100644 index 00000000..d4c203b8 --- /dev/null +++ b/browser_tests/assets/force_input.json @@ -0,0 +1,62 @@ +{ + "last_node_id": 5, + "last_link_id": 0, + "nodes": [ + { + "id": 5, + "type": "DevToolsNodeWithForceInput", + "pos": { + "0": 9, + "1": 39 + }, + "size": { + "0": 315, + "1": 106 + }, + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [ + { + "name": "int_input", + "type": "INT", + "link": null, + "widget": { + "name": "int_input" + } + }, + { + "name": "float_input", + "type": "FLOAT", + "link": null, + "widget": { + "name": "float_input" + }, + "shape": 7 + } + ], + "outputs": [], + "properties": { + "Node name for S&R": "DevToolsNodeWithForceInput" + }, + "widgets_values": [ + 0, + 1, + 0 + ] + } + ], + "links": [], + "groups": [], + "config": {}, + "extra": { + "ds": { + "scale": 1, + "offset": [ + 0, + 0 + ] + } + }, + "version": 0.4 +} \ No newline at end of file diff --git a/browser_tests/colorPalette.spec.ts b/browser_tests/colorPalette.spec.ts index 8caca660..26065c54 100644 --- a/browser_tests/colorPalette.spec.ts +++ b/browser_tests/colorPalette.spec.ts @@ -150,8 +150,21 @@ test.describe('Color Palette', () => { await comfyPage.nextFrame() await expect(comfyPage.canvas).toHaveScreenshot('default-color-palette.png') }) +}) + +test.describe('Node Color Adjustments', () => { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.loadWorkflow('every_node_color') + }) - test('Can change node opacity setting', async ({ comfyPage }) => { + test.afterEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.Node.Opacity', 1.0) + await comfyPage.setSetting('Comfy.ColorPalette', 'dark') + }) + + test('should adjust opacity via node opacity setting', async ({ + comfyPage + }) => { await comfyPage.setSetting('Comfy.Node.Opacity', 0.5) await comfyPage.page.waitForTimeout(128) @@ -166,4 +179,71 @@ test.describe('Color Palette', () => { await comfyPage.page.mouse.move(8, 8) await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-1.png') }) + + test('should persist color adjustments when changing themes', async ({ + comfyPage + }) => { + await comfyPage.setSetting('Comfy.Node.Opacity', 0.2) + await comfyPage.setSetting('Comfy.ColorPalette', 'arc') + await comfyPage.nextFrame() + await comfyPage.page.mouse.move(0, 0) + await expect(comfyPage.canvas).toHaveScreenshot( + 'node-opacity-0.2-arc-theme.png' + ) + }) + + test('should not serialize color adjustments in workflow', async ({ + comfyPage + }) => { + await comfyPage.setSetting('Comfy.Node.Opacity', 0.5) + await comfyPage.setSetting('Comfy.ColorPalette', 'light') + const saveWorkflowInterval = 1000 + await comfyPage.page.waitForTimeout(saveWorkflowInterval) + const workflow = await comfyPage.page.evaluate(() => { + return localStorage.getItem('workflow') + }) + for (const node of JSON.parse(workflow).nodes) { + if (node.bgcolor) expect(node.bgcolor).not.toMatch(/hsla/) + if (node.color) expect(node.color).not.toMatch(/hsla/) + } + }) + + test('should lighten node colors when switching to light theme', async ({ + comfyPage + }) => { + await comfyPage.setSetting('Comfy.ColorPalette', 'light') + await comfyPage.nextFrame() + await expect(comfyPage.canvas).toHaveScreenshot('node-lightened-colors.png') + }) + + test.describe('Context menu color adjustments', () => { + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.ColorPalette', 'light') + await comfyPage.setSetting('Comfy.Node.Opacity', 0.3) + const node = await comfyPage.getFirstNodeRef() + await node.clickContextMenuOption('Colors') + }) + + test('should persist color adjustments when changing custom node colors', async ({ + comfyPage + }) => { + await comfyPage.page + .locator('.litemenu-entry.submenu span:has-text("red")') + .click() + await expect(comfyPage.canvas).toHaveScreenshot( + 'node-opacity-0.3-color-changed.png' + ) + }) + + test('should persist color adjustments when removing custom node color', async ({ + comfyPage + }) => { + await comfyPage.page + .locator('.litemenu-entry.submenu span:has-text("No color")') + .click() + await expect(comfyPage.canvas).toHaveScreenshot( + 'node-opacity-0.3-color-removed.png' + ) + }) + }) }) diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-lightened-colors-chromium-2x-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-lightened-colors-chromium-2x-linux.png new file mode 100644 index 00000000..b40738a6 Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-lightened-colors-chromium-2x-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-lightened-colors-chromium-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-lightened-colors-chromium-linux.png new file mode 100644 index 00000000..f06dc5bd Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-lightened-colors-chromium-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-2-arc-theme-chromium-2x-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-2-arc-theme-chromium-2x-linux.png new file mode 100644 index 00000000..eee9c766 Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-2-arc-theme-chromium-2x-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-2-arc-theme-chromium-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-2-arc-theme-chromium-linux.png new file mode 100644 index 00000000..d5600023 Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-2-arc-theme-chromium-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-changed-chromium-2x-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-changed-chromium-2x-linux.png new file mode 100644 index 00000000..50df1175 Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-changed-chromium-2x-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-changed-chromium-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-changed-chromium-linux.png new file mode 100644 index 00000000..df216e25 Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-changed-chromium-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-removed-chromium-2x-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-removed-chromium-2x-linux.png new file mode 100644 index 00000000..7f8a35e5 Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-removed-chromium-2x-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-removed-chromium-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-removed-chromium-linux.png new file mode 100644 index 00000000..15b5b478 Binary files /dev/null and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-3-color-removed-chromium-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-2x-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-2x-linux.png index 22d25252..a3da195e 100644 Binary files a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-2x-linux.png and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-2x-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-linux.png index 03bd2fba..1810439d 100644 Binary files a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-linux.png and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-0-5-chromium-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-2x-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-2x-linux.png index 242929e5..ecf612d3 100644 Binary files a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-2x-linux.png and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-2x-linux.png differ diff --git a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-linux.png b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-linux.png index f6d7b354..8f30b77a 100644 Binary files a/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-linux.png and b/browser_tests/colorPalette.spec.ts-snapshots/node-opacity-1-chromium-linux.png differ diff --git a/browser_tests/dialog.spec.ts b/browser_tests/dialog.spec.ts index 66bc89bd..a74264f1 100644 --- a/browser_tests/dialog.spec.ts +++ b/browser_tests/dialog.spec.ts @@ -81,7 +81,8 @@ test.describe('Missing models warning', () => { ]) }) - test('Should display a warning when missing models are found', async ({ + // Regressed by https://github.com/comfyanonymous/ComfyUI/pull/4981 + test.skip('Should display a warning when missing models are found', async ({ comfyPage }) => { await comfyPage.setSetting('Comfy.Workflow.ShowMissingModelsWarning', true) diff --git a/browser_tests/groupNode.spec.ts b/browser_tests/groupNode.spec.ts index 97bdecd8..4a726da2 100644 --- a/browser_tests/groupNode.spec.ts +++ b/browser_tests/groupNode.spec.ts @@ -6,33 +6,84 @@ test.describe('Group Node', () => { await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled') }) - test('Is added to node library sidebar', async ({ comfyPage }) => { - await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') + test.describe('Node library sidebar', () => { const groupNodeName = 'DefautWorkflowGroupNode' - await comfyPage.convertAllNodesToGroupNode(groupNodeName) - const tab = comfyPage.menu.nodeLibraryTab - await tab.open() - expect(await tab.getFolder('group nodes').count()).toBe(1) - }) + const groupNodeCategory = 'group nodes>workflow' + const groupNodeBookmarkName = `workflow>${groupNodeName}` + let libraryTab - test('Can be added to canvas using node library sidebar', async ({ - comfyPage - }) => { - await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') - const groupNodeName = 'DefautWorkflowGroupNode' - await comfyPage.convertAllNodesToGroupNode(groupNodeName) - const initialNodeCount = await comfyPage.getGraphNodesCount() - - // Add group node from node library sidebar - const tab = comfyPage.menu.nodeLibraryTab - await tab.open() - await tab.getFolder('group nodes').click() - await tab.getFolder('workflow').click() - await tab.getFolder('workflow').last().click() - await tab.getNode(groupNodeName).click() - - // Verify the node is added to the canvas - expect(await comfyPage.getGraphNodesCount()).toBe(initialNodeCount + 1) + test.beforeEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.UseNewMenu', 'Top') + libraryTab = comfyPage.menu.nodeLibraryTab + await comfyPage.convertAllNodesToGroupNode(groupNodeName) + await libraryTab.open() + }) + + test.afterEach(async ({ comfyPage }) => { + await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', []) + await libraryTab.close() + }) + + test('Is added to node library sidebar', async ({ comfyPage }) => { + expect(await libraryTab.getFolder('group nodes').count()).toBe(1) + }) + + test('Can be added to canvas using node library sidebar', async ({ + comfyPage + }) => { + const initialNodeCount = await comfyPage.getGraphNodesCount() + + // Add group node from node library sidebar + await libraryTab.getFolder(groupNodeCategory).click() + await libraryTab.getNode(groupNodeName).click() + + // Verify the node is added to the canvas + expect(await comfyPage.getGraphNodesCount()).toBe(initialNodeCount + 1) + }) + + test('Can be bookmarked and unbookmarked', async ({ comfyPage }) => { + await libraryTab.getFolder(groupNodeCategory).click() + await libraryTab + .getNode(groupNodeName) + .locator('.bookmark-button') + .click() + + // Verify the node is added to the bookmarks tab + expect( + await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2') + ).toEqual([groupNodeBookmarkName]) + // Verify the bookmark node with the same name is added to the tree + expect(await libraryTab.getNode(groupNodeName).count()).not.toBe(0) + + // Unbookmark the node + await libraryTab + .getNode(groupNodeName) + .locator('.bookmark-button') + .first() + .click() + + // Verify the node is removed from the bookmarks tab + expect( + await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2') + ).toHaveLength(0) + }) + + test('Displays preview on bookmark hover', async ({ comfyPage }) => { + await libraryTab.getFolder(groupNodeCategory).click() + await libraryTab + .getNode(groupNodeName) + .locator('.bookmark-button') + .click() + await comfyPage.page.hover('.p-tree-node-label.tree-explorer-node-label') + expect(await comfyPage.page.isVisible('.node-lib-node-preview')).toBe( + true + ) + await libraryTab + .getNode(groupNodeName) + .locator('.bookmark-button') + .first() + .click() + }) }) test('Can be added to canvas using search', async ({ comfyPage }) => { diff --git a/browser_tests/nodeDisplay.spec.ts b/browser_tests/nodeDisplay.spec.ts index d17b0cb1..3b872722 100644 --- a/browser_tests/nodeDisplay.spec.ts +++ b/browser_tests/nodeDisplay.spec.ts @@ -18,4 +18,9 @@ test.describe('Optional input', () => { await comfyPage.loadWorkflow('optional_input_correct_shape') await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png') }) + + test('Force input', async ({ comfyPage }) => { + await comfyPage.loadWorkflow('force_input') + await expect(comfyPage.canvas).toHaveScreenshot('force_input.png') + }) }) diff --git a/browser_tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-2x-linux.png b/browser_tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-2x-linux.png new file mode 100644 index 00000000..a313aaa1 Binary files /dev/null and b/browser_tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-2x-linux.png differ diff --git a/browser_tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-linux.png b/browser_tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-linux.png new file mode 100644 index 00000000..dcdf7345 Binary files /dev/null and b/browser_tests/nodeDisplay.spec.ts-snapshots/force-input-chromium-linux.png differ diff --git a/package-lock.json b/package-lock.json index 3d04e00e..8e1920ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "comfyui-frontend", - "version": "1.2.61", + "version": "1.2.63", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "comfyui-frontend", - "version": "1.2.61", + "version": "1.2.63", "dependencies": { "@atlaskit/pragmatic-drag-and-drop": "^1.2.1", "@comfyorg/litegraph": "^0.7.77", @@ -3504,9 +3504,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.0.tgz", - "integrity": "sha512-/IZQvg6ZR0tAkEi4tdXOraQoWeJy9gbQ/cx4I7k9dJaCk9qrXEcdouxRVz5kZXt5C2bQ9pILoAA+KB4C/d3pfw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", "cpu": [ "arm" ], @@ -3516,9 +3516,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.0.tgz", - "integrity": "sha512-ETHi4bxrYnvOtXeM7d4V4kZWixib2jddFacJjsOjwbgYSRsyXYtZHC4ht134OsslPIcnkqT+TKV4eU8rNBKyyQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", "cpu": [ "arm64" ], @@ -3528,9 +3528,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.0.tgz", - "integrity": "sha512-ZWgARzhSKE+gVUX7QWaECoRQsPwaD8ZR0Oxb3aUpzdErTvlEadfQpORPXkKSdKbFci9v8MJfkTtoEHnnW9Ulng==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", "cpu": [ "arm64" ], @@ -3540,9 +3540,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.0.tgz", - "integrity": "sha512-h0ZAtOfHyio8Az6cwIGS+nHUfRMWBDO5jXB8PQCARVF6Na/G6XS2SFxDl8Oem+S5ZsHQgtsI7RT4JQnI1qrlaw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", "cpu": [ "x64" ], @@ -3552,9 +3552,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.0.tgz", - "integrity": "sha512-9pxQJSPwFsVi0ttOmqLY4JJ9pg9t1gKhK0JDbV1yUEETSx55fdyCjt39eBQ54OQCzAF0nVGO6LfEH1KnCPvelA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", "cpu": [ "arm" ], @@ -3564,9 +3564,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.0.tgz", - "integrity": "sha512-YJ5Ku5BmNJZb58A4qSEo3JlIG4d3G2lWyBi13ABlXzO41SsdnUKi3HQHe83VpwBVG4jHFTW65jOQb8qyoR+qzg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", "cpu": [ "arm" ], @@ -3576,9 +3576,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.0.tgz", - "integrity": "sha512-U4G4u7f+QCqHlVg1Nlx+qapZy+QoG+NV6ux+upo/T7arNGwKvKP2kmGM4W5QTbdewWFgudQxi3kDNST9GT1/mg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", "cpu": [ "arm64" ], @@ -3588,9 +3588,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.0.tgz", - "integrity": "sha512-aQpNlKmx3amwkA3a5J6nlXSahE1ijl0L9KuIjVOUhfOh7uw2S4piR3mtpxpRtbnK809SBtyPsM9q15CPTsY7HQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", "cpu": [ "arm64" ], @@ -3600,9 +3600,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.0.tgz", - "integrity": "sha512-9fx6Zj/7vve/Fp4iexUFRKb5+RjLCff6YTRQl4CoDhdMfDoobWmhAxQWV3NfShMzQk1Q/iCnageFyGfqnsmeqQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", "cpu": [ "ppc64" ], @@ -3612,9 +3612,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.0.tgz", - "integrity": "sha512-VWQiCcN7zBgZYLjndIEh5tamtnKg5TGxyZPWcN9zBtXBwfcGSZ5cHSdQZfQH/GB4uRxk0D3VYbOEe/chJhPGLQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", "cpu": [ "riscv64" ], @@ -3624,9 +3624,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.0.tgz", - "integrity": "sha512-EHmPnPWvyYqncObwqrosb/CpH3GOjE76vWVs0g4hWsDRUVhg61hBmlVg5TPXqF+g+PvIbqkC7i3h8wbn4Gp2Fg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", "cpu": [ "s390x" ], @@ -3636,9 +3636,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.0.tgz", - "integrity": "sha512-tsSWy3YQzmpjDKnQ1Vcpy3p9Z+kMFbSIesCdMNgLizDWFhrLZIoN21JSq01g+MZMDFF+Y1+4zxgrlqPjid5ohg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", "cpu": [ "x64" ], @@ -3648,9 +3648,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.0.tgz", - "integrity": "sha512-anr1Y11uPOQrpuU8XOikY5lH4Qu94oS6j0xrulHk3NkLDq19MlX8Ng/pVipjxBJ9a2l3+F39REZYyWQFkZ4/fw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", "cpu": [ "x64" ], @@ -3660,9 +3660,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.0.tgz", - "integrity": "sha512-7LB+Bh+Ut7cfmO0m244/asvtIGQr5pG5Rvjz/l1Rnz1kDzM02pSX9jPaS0p+90H5I1x4d1FkCew+B7MOnoatNw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", "cpu": [ "arm64" ], @@ -3672,9 +3672,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.0.tgz", - "integrity": "sha512-+3qZ4rer7t/QsC5JwMpcvCVPRcJt1cJrYS/TMJZzXIJbxWFQEVhrIc26IhB+5Z9fT9umfVc+Es2mOZgl+7jdJQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", "cpu": [ "ia32" ], @@ -3684,9 +3684,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.0.tgz", - "integrity": "sha512-YdicNOSJONVx/vuPkgPTyRoAPx3GbknBZRCOUkK84FJ/YTfs/F0vl/YsMscrB6Y177d+yDRcj+JWMPMCgshwrA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", "cpu": [ "x64" ], @@ -10768,9 +10768,9 @@ "dev": true }, "node_modules/rollup": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.0.tgz", - "integrity": "sha512-W21MUIFPZ4+O2Je/EU+GP3iz7PH4pVPUXSbEZdatQnxo29+3rsUjgrJmzuAZU24z7yRAnFN6ukxeAhZh/c7hzg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "dependencies": { "@types/estree": "1.0.5" }, @@ -10782,22 +10782,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.0", - "@rollup/rollup-android-arm64": "4.22.0", - "@rollup/rollup-darwin-arm64": "4.22.0", - "@rollup/rollup-darwin-x64": "4.22.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.0", - "@rollup/rollup-linux-arm-musleabihf": "4.22.0", - "@rollup/rollup-linux-arm64-gnu": "4.22.0", - "@rollup/rollup-linux-arm64-musl": "4.22.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.0", - "@rollup/rollup-linux-riscv64-gnu": "4.22.0", - "@rollup/rollup-linux-s390x-gnu": "4.22.0", - "@rollup/rollup-linux-x64-gnu": "4.22.0", - "@rollup/rollup-linux-x64-musl": "4.22.0", - "@rollup/rollup-win32-arm64-msvc": "4.22.0", - "@rollup/rollup-win32-ia32-msvc": "4.22.0", - "@rollup/rollup-win32-x64-msvc": "4.22.0", + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", "fsevents": "~2.3.2" } }, diff --git a/package.json b/package.json index 6e0a4249..f155a10a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "comfyui-frontend", "private": true, - "version": "1.2.61", + "version": "1.2.63", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/common/DeviceInfo.vue b/src/components/common/DeviceInfo.vue index d89e8b9c..10d25b81 100644 --- a/src/components/common/DeviceInfo.vue +++ b/src/components/common/DeviceInfo.vue @@ -9,6 +9,7 @@ diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index cac9d934..6cacc803 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -42,9 +42,6 @@ import { import type { RenderedTreeExplorerNode } from '@/types/treeExplorerTypes' import { useNodeBookmarkStore } from '@/stores/nodeBookmarkStore' import { useCanvasStore } from '@/stores/graphStore' -import { applyOpacity } from '@/utils/colorUtil' -import { getColorPalette } from '@/extensions/core/colorPalette' -import { debounce } from 'lodash' const emit = defineEmits(['ready']) const canvasRef = ref(null) @@ -93,24 +90,6 @@ watchEffect(() => { }) }) -const updateNodeOpacity = (nodeOpacity: number) => { - const colorPalette = getColorPalette() - - if (!canvasStore.canvas) return - - const nodeBgColor = colorPalette?.colors?.litegraph_base?.NODE_DEFAULT_BGCOLOR - if (nodeBgColor) { - LiteGraph.NODE_DEFAULT_BGCOLOR = applyOpacity(nodeBgColor, nodeOpacity) - } -} - -const debouncedUpdateNodeOpacity = debounce(updateNodeOpacity, 128) - -watchEffect(() => { - const nodeOpacity = settingStore.get('Comfy.Node.Opacity') - debouncedUpdateNodeOpacity(nodeOpacity) -}) - let dropTargetCleanup = () => {} onMounted(async () => { diff --git a/src/extensions/core/colorPalette.ts b/src/extensions/core/colorPalette.ts index 52bdc90a..fa67fca8 100644 --- a/src/extensions/core/colorPalette.ts +++ b/src/extensions/core/colorPalette.ts @@ -2,8 +2,6 @@ import { app } from '../../scripts/app' import { $el } from '../../scripts/ui' import type { ColorPalettes, Palette } from '@/types/colorPalette' import { LGraphCanvas, LiteGraph } from '@comfyorg/litegraph' -import { applyOpacity } from '@/utils/colorUtil' -import { useSettingStore } from '@/stores/settingStore' // Manage color palettes @@ -684,13 +682,7 @@ app.registerExtension({ colorPalette.colors.litegraph_base.hasOwnProperty(key) && LiteGraph.hasOwnProperty(key) ) { - LiteGraph[key] = - key === 'NODE_DEFAULT_BGCOLOR' - ? applyOpacity( - colorPalette.colors.litegraph_base[key], - useSettingStore().get('Comfy.Node.Opacity') - ) - : colorPalette.colors.litegraph_base[key] + LiteGraph[key] = colorPalette.colors.litegraph_base[key] } } } diff --git a/src/extensions/core/groupNode.ts b/src/extensions/core/groupNode.ts index 34e743dd..31d662ab 100644 --- a/src/extensions/core/groupNode.ts +++ b/src/extensions/core/groupNode.ts @@ -15,7 +15,7 @@ const Workflow = { InWorkflow: 2 }, isInUseGroupNode(name) { - const id = `workflow/${name}` + const id = `workflow>${name}` // Check if lready registered/in use in this workflow if (app.graph.extra?.groupNodes?.[name]) { if (app.graph.nodes.find((n) => n.type === id)) { @@ -191,9 +191,9 @@ export class GroupNodeConfig { output_name: [], output_is_list: [], output_is_hidden: [], - name: source + '/' + this.name, + name: source + '>' + this.name, display_name: this.name, - category: 'group nodes' + ('/' + source), + category: 'group nodes' + ('>' + source), input: { required: {} }, description: `Group node combining ${this.nodeData.nodes .map((n) => n.type) @@ -216,7 +216,7 @@ export class GroupNodeConfig { p() } this.#convertedToProcess = null - await app.registerNodeDef('workflow/' + this.name, this.nodeDef) + await app.registerNodeDef('workflow>' + this.name, this.nodeDef) useNodeDefStore().addNodeDef(this.nodeDef) } @@ -1380,7 +1380,7 @@ export class GroupNodeHandler { const config = new GroupNodeConfig(name, nodeData) await config.registerType() - const groupNode = LiteGraph.createNode(`workflow/${name}`) + const groupNode = LiteGraph.createNode(`workflow>${name}`) // Reuse the existing nodes for this instance groupNode.setInnerNodes(builder.nodes) groupNode[GROUP].populateWidgets() diff --git a/src/extensions/core/groupNodeManage.ts b/src/extensions/core/groupNodeManage.ts index 933353fe..cf68be0b 100644 --- a/src/extensions/core/groupNodeManage.ts +++ b/src/extensions/core/groupNodeManage.ts @@ -102,7 +102,7 @@ export class ManageGroupDialog extends ComfyDialog { getGroupData() { this.groupNodeType = - LiteGraph.registered_node_types['workflow/' + this.selectedGroup] + LiteGraph.registered_node_types['workflow>' + this.selectedGroup] this.groupNodeDef = this.groupNodeType.nodeData this.groupData = GroupNodeHandler.getGroupData(this.groupNodeType) } @@ -367,7 +367,7 @@ export class ManageGroupDialog extends ComfyDialog { groupNodes.map((g) => $el('option', { textContent: g, - selected: 'workflow/' + g === type, + selected: 'workflow>' + g === type, value: g }) ) @@ -389,7 +389,7 @@ export class ManageGroupDialog extends ComfyDialog { { onclick: (e) => { const node = app.graph.nodes.find( - (n) => n.type === 'workflow/' + this.selectedGroup + (n) => n.type === 'workflow>' + this.selectedGroup ) if (node) { alert( @@ -403,7 +403,7 @@ export class ManageGroupDialog extends ComfyDialog { ) ) { delete app.graph.extra.groupNodes[this.selectedGroup] - LiteGraph.unregisterNodeType('workflow/' + this.selectedGroup) + LiteGraph.unregisterNodeType('workflow>' + this.selectedGroup) } this.show() } @@ -476,7 +476,7 @@ export class ManageGroupDialog extends ComfyDialog { }, {}) } - const nodes = nodesByType['workflow/' + g] + const nodes = nodesByType['workflow>' + g] if (nodes) recreateNodes.push(...nodes) } @@ -503,7 +503,7 @@ export class ManageGroupDialog extends ComfyDialog { this.element.replaceChildren(outer) this.changeGroup( - type ? groupNodes.find((g) => 'workflow/' + g === type) : groupNodes[0] + type ? groupNodes.find((g) => 'workflow>' + g === type) : groupNodes[0] ) this.element.showModal() diff --git a/src/extensions/core/widgetInputs.ts b/src/extensions/core/widgetInputs.ts index da3e111e..25567376 100644 --- a/src/extensions/core/widgetInputs.ts +++ b/src/extensions/core/widgetInputs.ts @@ -3,6 +3,7 @@ import { app } from '../../scripts/app' import { applyTextReplacements } from '../../scripts/utils' import { LiteGraph, LGraphNode } from '@comfyorg/litegraph' import type { INodeInputSlot, IWidget } from '@comfyorg/litegraph' +import type { InputSpec } from '@/types/apiTypes' const CONVERTED_TYPE = 'converted-widget' const VALID_TYPES = ['STRING', 'combo', 'number', 'toggle', 'BOOLEAN'] @@ -447,15 +448,19 @@ function showWidget(widget) { } } -function convertToInput(node, widget, config) { +function convertToInput(node: LGraphNode, widget: IWidget, config: InputSpec) { hideWidget(node, widget) const { type } = getWidgetType(config) // Add input and store widget config for creating on primitive node const sz = node.size + const inputIsOptional = !!widget.options?.inputIsOptional node.addInput(widget.name, type, { - widget: { name: widget.name, [GET_CONFIG]: () => config } + // @ts-expect-error GET_CONFIG is not defined in LiteGraph + widget: { name: widget.name, [GET_CONFIG]: () => config }, + // @ts-expect-error LiteGraph.SlotShape is not typed. + ...(inputIsOptional ? { shape: LiteGraph.SlotShape.HollowCircle } : {}) }) for (const widget of node.widgets) { @@ -479,7 +484,7 @@ function convertToWidget(node, widget) { node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]) } -function getWidgetType(config) { +function getWidgetType(config: InputSpec) { // Special handling for COMBO so we restrict links based on the entries let type = config[0] if (type instanceof Array) { diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 450cc23e..6c5b73f9 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -21,7 +21,7 @@ import { validateComfyWorkflow } from '../types/comfyWorkflow' import { ComfyNodeDef, StatusWsMessageStatus } from '@/types/apiTypes' -import { lightenColor } from '@/utils/colorUtil' +import { adjustColor, ColorAdjustOptions } from '@/utils/colorUtil' import { ComfyAppMenu } from './ui/menu/index' import { getStorageValue } from './utils' import { ComfyWorkflowManager, ComfyWorkflow } from './workflows' @@ -1568,14 +1568,25 @@ export class ComfyApp { this.editor_alpha = 0.2 } - const adjustColor = (color?: string) => { - return color ? lightenColor(color, 0.5) : color - } - if (app.ui.settings.getSettingValue('Comfy.ColorPalette') === 'light') { - node.bgcolor = adjustColor(node.bgcolor) - node.color = adjustColor(node.color) + const adjustments: ColorAdjustOptions = {} + + const opacity = useSettingStore().get('Comfy.Node.Opacity') + if (opacity) adjustments.opacity = opacity + + if (useSettingStore().get('Comfy.ColorPalette') === 'light') { + adjustments.lightness = 0.5 + + // Lighten title bar of colored nodes on light theme + if (old_color) { + node.color = adjustColor(old_color, { lightness: 0.5 }) + } } + node.bgcolor = adjustColor( + old_bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR, + adjustments + ) + const res = origDrawNode.apply(this, arguments) this.editor_alpha = editor_alpha @@ -2012,6 +2023,7 @@ export class ComfyApp { for (const inputName in inputs) { const inputData = inputs[inputName] const type = inputData[0] + const inputIsRequired = inputName in requiredInputs let widgetCreated = true const widgetType = self.getWidgetType(inputData, inputName) @@ -2029,7 +2041,6 @@ export class ComfyApp { } } else { // Node connection inputs - const inputIsRequired = inputName in requiredInputs const inputOptions = inputIsRequired ? {} : // @ts-expect-error LiteGraph.SlotShape is not typed. @@ -2037,6 +2048,15 @@ export class ComfyApp { this.addInput(inputName, type, inputOptions) widgetCreated = false } + + // @ts-expect-error + if (widgetCreated && !inputIsRequired && config?.widget) { + // @ts-expect-error + if (!config.widget.options) config.widget.options = {} + // @ts-expect-error + config.widget.options.inputIsOptional = true + } + // @ts-expect-error if (widgetCreated && inputData[1]?.forceInput && config?.widget) { // @ts-expect-error diff --git a/src/types/apiTypes.ts b/src/types/apiTypes.ts index 20c4a4ed..420b9029 100644 --- a/src/types/apiTypes.ts +++ b/src/types/apiTypes.ts @@ -410,7 +410,9 @@ export const zSystemStats = z.object({ embedded_python: z.boolean(), comfyui_version: z.string(), pytorch_version: z.string(), - argv: z.array(z.string()) + argv: z.array(z.string()), + ram_total: z.number(), + ram_free: z.number() }), devices: z.array(zDeviceStats) }) @@ -486,7 +488,7 @@ const zSettings = z.record(z.any()).and( 'Comfy.PreviewFormat': z.string(), 'Comfy.PromptFilename': z.boolean(), 'Comfy.Sidebar.Location': z.enum(['left', 'right']), - 'Comfy.Sidebar.Size': z.number(), + 'Comfy.Sidebar.Size': z.enum(['small', 'normal']), 'Comfy.SwitchUser': z.any(), 'Comfy.SnapToGrid.GridSize': z.number(), 'Comfy.TextareaWidget.FontSize': z.number(), diff --git a/src/utils/colorUtil.ts b/src/utils/colorUtil.ts index 12d0b188..d5586103 100644 --- a/src/utils/colorUtil.ts +++ b/src/utils/colorUtil.ts @@ -1,7 +1,15 @@ +import { memoize } from 'lodash' + type RGB = { r: number; g: number; b: number } type HSL = { h: number; s: number; l: number } +type HSLA = { h: number; s: number; l: number; a: number } type ColorFormat = 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla' +export interface ColorAdjustOptions { + lightness?: number + opacity?: number +} + function rgbToHsl({ r, g, b }: RGB): HSL { r /= 255 g /= 255 @@ -33,35 +41,6 @@ function rgbToHsl({ r, g, b }: RGB): HSL { return { h, s, l } } -function hslToRgb({ h, s, l }: HSL): RGB { - let r: number, g: number, b: number - - if (s === 0) { - r = g = b = l // achromatic - } else { - const hue2rgb = (p: number, q: number, t: number) => { - if (t < 0) t += 1 - if (t > 1) t -= 1 - if (t < 1 / 6) return p + (q - p) * 6 * t - if (t < 1 / 2) return q - if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6 - return p - } - - const q = l < 0.5 ? l * (1 + s) : l + s - l * s - const p = 2 * l - q - r = hue2rgb(p, q, h + 1 / 3) - g = hue2rgb(p, q, h) - b = hue2rgb(p, q, h - 1 / 3) - } - - return { - r: Math.round(r * 255), - g: Math.round(g * 255), - b: Math.round(b * 255) - } -} - function hexToRgb(hex: string): RGB { let r = 0, g = 0, @@ -81,75 +60,105 @@ function hexToRgb(hex: string): RGB { return { r, g, b } } -function rgbToHex({ r, g, b }: RGB): string { - return ( - '#' + - [r, g, b] - .map((x) => { - const hex = x.toString(16) - return hex.length === 1 ? '0' + hex : hex - }) - .join('') - ) -} - -function identifyColorFormat(color: string): ColorFormat | null { +const identifyColorFormat = (color: string): ColorFormat | null => { if (!color) return null - if (color.startsWith('#')) return 'hex' - if (/^rgba?\(\d+,\s*\d+,\s*\d+/.test(color)) + if (color.startsWith('#') && (color.length === 4 || color.length === 7)) + return 'hex' + if (/rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*/.test(color)) return color.includes('rgba') ? 'rgba' : 'rgb' - if (/^hsla?\(\d+(\.\d+)?,\s*\d+(\.\d+)?%,\s*\d+(\.\d+)?%/.test(color)) + if (/hsla?\(\s*\d+(\.\d+)?\s*,\s*\d+(\.\d+)?%\s*,\s*\d+(\.\d+)?%/.test(color)) return color.includes('hsla') ? 'hsla' : 'hsl' return null } -export function lightenColor(hex: string, amount: number): string { - let rgb = hexToRgb(hex) - const hsl = rgbToHsl(rgb) - hsl.l = Math.min(1, hsl.l + amount) - rgb = hslToRgb(hsl) - return rgbToHex(rgb) -} - -export function applyOpacity(color: string, opacity: number): string { - const colorFormat = identifyColorFormat(color) +const isHSLA = (color: unknown): color is HSLA => { + if (typeof color !== 'object' || color === null) return false - if (!colorFormat) { - console.warn( - `Unsupported color format in user color palette for color: ${color}` - ) - return color - } + return ['h', 's', 'l', 'a'].every( + (key) => + typeof (color as Record)[key] === 'number' && + !isNaN((color as Record)[key]) + ) +} - const clampedOpacity = Math.max(0, Math.min(1, opacity)) +function parseToHSLA(color: string, format: ColorFormat): HSLA | null { + let match: RegExpMatchArray | null - switch (colorFormat) { + switch (format) { case 'hex': { - const { r, g, b } = hexToRgb(color) - if (isNaN(r) || isNaN(g) || isNaN(b)) { - return color + const hsl = rgbToHsl(hexToRgb(color)) + return { + h: Math.round(hsl.h * 360), + s: +(hsl.s * 100).toFixed(1), + l: +(hsl.l * 100).toFixed(1), + a: 1 } - return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})` - } - case 'rgb': { - return color.replace('rgb', 'rgba').replace(')', `, ${clampedOpacity})`) } + + case 'rgb': case 'rgba': { - return color.replace( - /rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,[^)]+\)/, - `rgba($1, $2, $3, ${clampedOpacity})` - ) - } - case 'hsl': { - return color.replace('hsl', 'hsla').replace(')', `, ${clampedOpacity})`) + match = color.match(/\d+(\.\d+)?/g) + if (!match || match.length < 3) return null + const [r, g, b] = match.map(Number) + const hsl = rgbToHsl({ r, g, b }) + + const a = format === 'rgba' && match[3] ? parseFloat(match[3]) : 1 + + return { + h: Math.round(hsl.h * 360), + s: +(hsl.s * 100).toFixed(1), + l: +(hsl.l * 100).toFixed(1), + a + } } + + case 'hsl': case 'hsla': { - return color.replace( - /hsla\(\s*(\d+)\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*,[^)]+\)/, - `hsla($1, $2%, $3%, ${clampedOpacity})` - ) + match = color.match(/\d+(\.\d+)?/g) + if (!match || match.length < 3) return null + const [h, s, l] = match.map(Number) + const a = format === 'hsla' && match[3] ? parseFloat(match[3]) : 1 + return { h, s, l, a } } default: - return color + return null } } + +const applyColorAdjustments = ( + color: string, + options: ColorAdjustOptions +): string => { + if (!Object.keys(options).length) return color + + const format = identifyColorFormat(color) + if (!format) { + console.warn(`Unsupported color format in color palette: ${color}`) + return color + } + + const hsla = parseToHSLA(color, format) + if (!isHSLA(hsla)) { + console.warn(`Invalid color values in color palette: ${color}`) + return color + } + + if (options.lightness) { + hsla.l = Math.max(0, Math.min(100, hsla.l + options.lightness * 100.0)) + } + + if (options.opacity) { + hsla.a = Math.max(0, Math.min(1, options.opacity)) + } + + return `hsla(${hsla.h}, ${hsla.s}%, ${hsla.l}%, ${hsla.a})` +} + +export const adjustColor: ( + color: string, + options: ColorAdjustOptions +) => string = memoize( + applyColorAdjustments, + (color: string, options: ColorAdjustOptions): string => + `${color}-${JSON.stringify(options)}` +) diff --git a/src/utils/formatUtil.ts b/src/utils/formatUtil.ts index 2d702b76..bbddf8f4 100644 --- a/src/utils/formatUtil.ts +++ b/src/utils/formatUtil.ts @@ -59,3 +59,15 @@ export function formatNumberWithSuffix( return `${formattedNum}${suffixes[exp]}` } + +export function formatMemory(value?: number) { + if (value === null || value === undefined) { + return '-' + } + + const mb = Math.round(value / (1024 * 1024)) + if (mb >= 1024) { + return `${(mb / 1024).toFixed(2)} GB` + } + return `${mb} MB` +} diff --git a/tests-ui/tests/colorUtil.test.ts b/tests-ui/tests/colorUtil.test.ts index 5c8fd552..00de93ea 100644 --- a/tests-ui/tests/colorUtil.test.ts +++ b/tests-ui/tests/colorUtil.test.ts @@ -1,50 +1,170 @@ -import { applyOpacity } from '@/utils/colorUtil' +import { adjustColor } from '@/utils/colorUtil' -describe('colorUtil - applyOpacity', () => { - // Same color in various formats - const solarized = { +interface ColorTestCase { + hex: string + rgb: string + rgba: string + hsl: string + hsla: string + lightExpected: string + transparentExpected: string + lightTransparentExpected: string +} + +type ColorFormat = 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla' + +jest.mock('lodash', () => ({ + memoize: (fn: any) => fn +})) + +const targetOpacity = 0.5 +const targetLightness = 0.5 + +const assertColorVariationsMatch = (variations: string[], adjustment: any) => { + for (let i = 0; i < variations.length - 1; i++) { + expect(adjustColor(variations[i], adjustment)).toBe( + adjustColor(variations[i + 1], adjustment) + ) + } +} + +const colors: Record = { + green: { hex: '#073642', rgb: 'rgb(7, 54, 66)', rgba: 'rgba(7, 54, 66, 1)', - hsl: 'hsl(192, 80.80%, 14.30%)', - hsla: 'hsla(192, 80.80%, 14.30%, 1)' + hsl: 'hsl(192, 80.8%, 14.3%)', + hsla: 'hsla(192, 80.8%, 14.3%, 1)', + lightExpected: 'hsla(192, 80.8%, 64.3%, 1)', + transparentExpected: 'hsla(192, 80.8%, 14.3%, 0.5)', + lightTransparentExpected: 'hsla(192, 80.8%, 64.3%, 0.5)' + }, + blue: { + hex: '#00008B', + rgb: 'rgb(0,0,139)', + rgba: 'rgba(0,0,139,1)', + hsl: 'hsl(240,100%,27.3%)', + hsla: 'hsl(240,100%,27.3%,1)', + lightExpected: 'hsla(240, 100%, 77.3%, 1)', + transparentExpected: 'hsla(240, 100%, 27.3%, 0.5)', + lightTransparentExpected: 'hsla(240, 100%, 77.3%, 0.5)' } +} - const opacity = 0.5 +const formats: ColorFormat[] = ['hex', 'rgb', 'rgba', 'hsl', 'hsla'] - it('applies opacity consistently to hex, rgb, and rgba formats', () => { - const hexResult = applyOpacity(solarized.hex, opacity) - const rgbResult = applyOpacity(solarized.rgb, opacity) - const rgbaResult = applyOpacity(solarized.rgba, opacity) +describe('colorUtil - adjustColor', () => { + const runAdjustColorTests = ( + color: ColorTestCase, + format: ColorFormat + ): void => { + it('converts lightness', () => { + const result = adjustColor(color[format], { lightness: targetLightness }) + expect(result).toBe(color.lightExpected) + }) - expect(hexResult).toBe(rgbResult) - expect(rgbResult).toBe(rgbaResult) - }) + it('applies opacity', () => { + const result = adjustColor(color[format], { opacity: targetOpacity }) + expect(result).toBe(color.transparentExpected) + }) - it('applies opacity consistently to hsl and hsla formats', () => { - const hslResult = applyOpacity(solarized.hsl, opacity) - const hslaResult = applyOpacity(solarized.hsla, opacity) + it('applies lightness and opacity jointly', () => { + const result = adjustColor(color[format], { + lightness: targetLightness, + opacity: targetOpacity + }) + expect(result).toBe(color.lightTransparentExpected) + }) + } - expect(hslResult).toBe(hslaResult) + describe.each(Object.entries(colors))('%s color', (colorName, color) => { + describe.each(formats)('%s format', (format) => { + runAdjustColorTests(color, format as ColorFormat) + }) }) it('returns the original value for invalid color formats', () => { const invalidColors = [ - '#GGGGGG', // Invalid hex code (non-hex characters) - 'rgb(300, -10, 256)', // Invalid RGB values (out of range) - 'xyz(255, 255, 255)', // Unsupported format - 'rgba(255, 255, 255)', // Missing alpha in RGBA - 'hsl(100, 50, 50%)' // Missing percentage sign for saturation + 'cmky(100, 50, 50, 0.5)', + 'rgb(300, -10, 256)', + 'xyz(255, 255, 255)', + 'hsl(100, 50, 50%)', + 'hsl(100, 50%, 50)', + '#GGGGGG', + '#3333' ] invalidColors.forEach((color) => { - const result = applyOpacity(color, opacity) + const result = adjustColor(color, { + lightness: targetLightness, + opacity: targetOpacity + }) expect(result).toBe(color) }) }) it('returns the original value for null or undefined inputs', () => { - expect(applyOpacity(null, opacity)).toBe(null) - expect(applyOpacity(undefined, opacity)).toBe(undefined) + expect(adjustColor(null, { opacity: targetOpacity })).toBe(null) + expect(adjustColor(undefined, { opacity: targetOpacity })).toBe(undefined) + }) + + describe('handles input variations', () => { + it('handles spaces in rgb input', () => { + const variations = [ + 'rgb(0, 0, 0)', + 'rgb(0,0,0)', + 'rgb(0, 0,0)', + 'rgb(0,0, 0)' + ] + assertColorVariationsMatch(variations, { lightness: 0.5 }) + }) + + it('handles spaces in hsl input', () => { + const variations = [ + 'hsl(0, 0%, 0%)', + 'hsl(0,0%,0%)', + 'hsl(0, 0%,0%)', + 'hsl(0,0%, 0%)' + ] + assertColorVariationsMatch(variations, { lightness: 0.5 }) + }) + + it('handles different decimal places in rgba input', () => { + const variations = [ + 'rgba(0, 0, 0, 0.5)', + 'rgba(0, 0, 0, 0.50)', + 'rgba(0, 0, 0, 0.500)' + ] + assertColorVariationsMatch(variations, { opacity: 0.5 }) + }) + + it('handles different decimal places in hsla input', () => { + const variations = [ + 'hsla(0, 0%, 0%, 0.5)', + 'hsla(0, 0%, 0%, 0.50)', + 'hsla(0, 0%, 0%, 0.500)' + ] + assertColorVariationsMatch(variations, { opacity: 0.5 }) + }) + }) + + describe('clamps values correctly', () => { + it('clamps lightness to 0 and 100', () => { + expect(adjustColor('hsl(0, 100%, 50%)', { lightness: -1 })).toBe( + 'hsla(0, 100%, 0%, 1)' + ) + expect(adjustColor('hsl(0, 100%, 50%)', { lightness: 1.5 })).toBe( + 'hsla(0, 100%, 100%, 1)' + ) + }) + + it('clamps opacity to 0 and 1', () => { + expect(adjustColor('rgba(0, 0, 0, 0.5)', { opacity: -0.5 })).toBe( + 'hsla(0, 0%, 0%, 0)' + ) + expect(adjustColor('rgba(0, 0, 0, 0.5)', { opacity: 1.5 })).toBe( + 'hsla(0, 0%, 0%, 1)' + ) + }) }) }) diff --git a/tests-ui/tests/exampleWorkflows.test.ts b/tests-ui/tests/exampleWorkflows.test.ts index 2ca60372..0d1d6a70 100644 --- a/tests-ui/tests/exampleWorkflows.test.ts +++ b/tests-ui/tests/exampleWorkflows.test.ts @@ -56,6 +56,11 @@ describe('example workflows', () => { skip = !!Object.keys(parsedWorkflow?.extra?.groupNodes ?? {}).length } catch (error) {} + // https://github.com/comfyanonymous/ComfyUI_examples/issues/40 + if (file === 'audio_stable_audio_example.flac.json') { + skip = true + } + return { file, workflow, prompt, parsedWorkflow, skip } }) diff --git a/tests-ui/tests/groupNode.test.ts b/tests-ui/tests/groupNode.test.ts index a206cc8d..894954bb 100644 --- a/tests-ui/tests/groupNode.test.ts +++ b/tests-ui/tests/groupNode.test.ts @@ -46,7 +46,7 @@ describe('group node', () => { expect(n.isRemoved).toBeTruthy() } - expect(groupNode.type).toEqual('workflow/' + name) + expect(groupNode.type).toEqual('workflow>' + name) return graph.find(groupNode) } @@ -520,7 +520,7 @@ describe('group node', () => { group1.menu.Clone.call() expect(app.graph.nodes).toHaveLength(4) const group2 = graph.find(app.graph.nodes[3]) - expect(group2.node.type).toEqual('workflow/test') + expect(group2.node.type).toEqual('workflow>test') expect(group2.id).not.toEqual(group1.id) group1.outputs.VAE.connectTo(group2.inputs.VAE) @@ -681,7 +681,7 @@ describe('group node', () => { group1.menu.Clone.call() expect(app.graph.nodes).toHaveLength(3) const group2 = graph.find(app.graph.nodes[2]) - expect(group2.node.type).toEqual('workflow/test') + expect(group2.node.type).toEqual('workflow>test') expect(group2.id).not.toEqual(group1.id) // Reconnect ckpt @@ -741,7 +741,7 @@ describe('group node', () => { resetEnv: true })) // Ensure the node isnt registered - expect(() => ez['workflow/test']).toThrow() + expect(() => ez['workflow>test']).toThrow() // Reload the workflow await app.loadGraphData(JSON.parse(workflow)) @@ -768,7 +768,7 @@ describe('group node', () => { nodes: [ { id: 3, - type: 'workflow/testerror' + type: 'workflow>testerror' } ], links: [], @@ -796,7 +796,7 @@ describe('group node', () => { expect(call).toContain('the following node types were not found') expect(call).toContain('NotKSampler') expect(call).toContain('NotVAEDecode') - expect(call).toContain('workflow/testerror') + expect(call).toContain('workflow>testerror') }) test('maintains widget inputs on conversion back to nodes', async () => { const { ez, graph, app } = await start()