From e0ca4741a0d194a529e88cbe0eb4709363ab6232 Mon Sep 17 00:00:00 2001 From: bymyself Date: Sat, 11 Oct 2025 11:23:24 -0700 Subject: [PATCH 1/2] fix vue node border states - executing, error, selected --- packages/design-system/src/css/style.css | 18 ++++++++++++++++-- .../vueNodes/components/LGraphNode.vue | 7 +++---- .../execution/useNodeExecutionState.ts | 7 +++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/design-system/src/css/style.css b/packages/design-system/src/css/style.css index df630d2265..5208d4bbb6 100644 --- a/packages/design-system/src/css/style.css +++ b/packages/design-system/src/css/style.css @@ -129,6 +129,7 @@ /* --- */ + --accent-primary: var(--color-charcoal-700); --backdrop: var(--color-white); --dialog-surface: var(--color-neutral-200); --node-component-border: var(--color-gray-400); @@ -154,13 +155,20 @@ from var(--color-zinc-500) r g b / 10% ); --node-component-widget-skeleton-surface: var(--color-zinc-300); - --node-stroke: var(--color-stone-100); + --node-stroke: var(--color-gray-400); + --node-stroke-selected: var(--color-accent-primary); + --node-stroke-error: var(--color-error); + --node-stroke-executing: var(--color-blue-100); } .dark-theme { + --accent-primary: var(--color-pure-white); --backdrop: var(--color-neutral-900); --dialog-surface: var(--color-neutral-700); --node-component-border: var(--color-stone-200); + --node-component-border-error: var(--color-danger-100); + --node-component-border-executing: var(--color-blue-500); + --node-component-border-selected: var(--color-charcoal-200); --node-component-header-icon: var(--color-slate-300); --node-component-header-surface: var(--color-charcoal-800); --node-component-outline: var(--color-white); @@ -176,7 +184,10 @@ --node-component-tooltip-border: var(--color-slate-300); --node-component-tooltip-surface: var(--color-charcoal-800); --node-component-widget-skeleton-surface: var(--color-zinc-800); - --node-stroke: var(--color-slate-100); + --node-stroke: var(--color-stone-200); + --node-stroke-selected: var(--color-pure-white); + --node-stroke-error: var(--color-error); + --node-stroke-executing: var(--color-blue-100); } @theme inline { @@ -214,6 +225,9 @@ --node-component-widget-skeleton-surface ); --color-node-stroke: var(--node-stroke); + --color-node-stroke-selected: var(--node-stroke-selected); + --color-node-stroke-error: var(--node-stroke-error); + --color-node-stroke-executing: var(--node-stroke-executing); } @custom-variant dark-theme { diff --git a/src/renderer/extensions/vueNodes/components/LGraphNode.vue b/src/renderer/extensions/vueNodes/components/LGraphNode.vue index 8054a7415e..e29601ef09 100644 --- a/src/renderer/extensions/vueNodes/components/LGraphNode.vue +++ b/src/renderer/extensions/vueNodes/components/LGraphNode.vue @@ -319,10 +319,9 @@ const { latestPreviewUrl, shouldShowPreviewImg } = useNodePreviewState( ) const borderClass = computed(() => { - return ( - (hasAnyError.value && 'border-error') || - (executing.value && 'border-node-executing') - ) + if (hasAnyError.value) return 'border-node-stroke-error' + if (executing.value) return 'border-node-stroke-executing' + return 'border-node-stroke' }) const outlineClass = computed(() => { diff --git a/src/renderer/extensions/vueNodes/execution/useNodeExecutionState.ts b/src/renderer/extensions/vueNodes/execution/useNodeExecutionState.ts index 10905624ca..136f268121 100644 --- a/src/renderer/extensions/vueNodes/execution/useNodeExecutionState.ts +++ b/src/renderer/extensions/vueNodes/execution/useNodeExecutionState.ts @@ -17,14 +17,17 @@ export const useNodeExecutionState = ( nodeLocatorIdMaybe: MaybeRefOrGetter ) => { const locatorId = computed(() => toValue(nodeLocatorIdMaybe) ?? '') - const { nodeLocationProgressStates } = storeToRefs(useExecutionStore()) + const { nodeLocationProgressStates, isIdle } = + storeToRefs(useExecutionStore()) const progressState = computed(() => { const id = locatorId.value return id ? nodeLocationProgressStates.value[id] : undefined }) - const executing = computed(() => progressState.value?.state === 'running') + const executing = computed( + () => !isIdle.value && progressState.value?.state === 'running' + ) const progress = computed(() => { const state = progressState.value From a1c0925cd7fa816df382890ea0a038158e323ee9 Mon Sep 17 00:00:00 2001 From: bymyself Date: Sat, 11 Oct 2025 11:57:17 -0700 Subject: [PATCH 2/2] add playwright test for error state --- browser_tests/fixtures/ComfyPage.ts | 15 ++++++--------- .../tests/vueNodes/nodeStates/error.spec.ts | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/browser_tests/fixtures/ComfyPage.ts b/browser_tests/fixtures/ComfyPage.ts index 19796f4c4c..d46b31a98a 100644 --- a/browser_tests/fixtures/ComfyPage.ts +++ b/browser_tests/fixtures/ComfyPage.ts @@ -1,6 +1,5 @@ import type { APIRequestContext, Locator, Page } from '@playwright/test' -import { expect } from '@playwright/test' -import { test as base } from '@playwright/test' +import { test as base, expect } from '@playwright/test' import dotenv from 'dotenv' import * as fs from 'fs' @@ -130,7 +129,8 @@ export class ComfyPage { // Buttons public readonly resetViewButton: Locator - public readonly queueButton: Locator + public readonly queueButton: Locator // Run button in Legacy UI + public readonly runButton: Locator // Run button (renamed "Queue" -> "Run") // Inputs public readonly workflowUploadInput: Locator @@ -165,6 +165,9 @@ export class ComfyPage { this.widgetTextBox = page.getByPlaceholder('text').nth(1) this.resetViewButton = page.getByRole('button', { name: 'Reset View' }) this.queueButton = page.getByRole('button', { name: 'Queue Prompt' }) + this.runButton = page + .getByTestId('queue-button') + .getByRole('button', { name: 'Run' }) this.workflowUploadInput = page.locator('#comfy-file-input') this.visibleToasts = page.locator('.p-toast-message:visible') @@ -1086,12 +1089,6 @@ export class ComfyPage { const targetPosition = await targetSlot.getPosition() - // Debug: Log the positions we're trying to use - console.log('Drag positions:', { - source: sourcePosition, - target: targetPosition - }) - await this.dragAndDrop(sourcePosition, targetPosition) await this.nextFrame() } diff --git a/browser_tests/tests/vueNodes/nodeStates/error.spec.ts b/browser_tests/tests/vueNodes/nodeStates/error.spec.ts index f4f8e10fe0..b8c718239c 100644 --- a/browser_tests/tests/vueNodes/nodeStates/error.spec.ts +++ b/browser_tests/tests/vueNodes/nodeStates/error.spec.ts @@ -3,7 +3,7 @@ import { comfyPageFixture as test } from '../../../fixtures/ComfyPage' -const ERROR_CLASS = /border-error/ +const ERROR_CLASS = /border-node-stroke-error/ test.describe('Vue Node Error', () => { test.beforeEach(async ({ comfyPage }) => { @@ -17,16 +17,21 @@ test.describe('Vue Node Error', () => { await comfyPage.setup() await comfyPage.loadWorkflow('missing/missing_nodes') - // Close missing nodes warning dialog - await comfyPage.page.getByRole('button', { name: 'Close' }).click() - await comfyPage.page.waitForSelector('.comfy-missing-nodes', { - state: 'hidden' - }) - // Expect error state on missing unknown node const unknownNode = comfyPage.page.locator('[data-node-id]').filter({ hasText: 'UNKNOWN NODE' }) await expect(unknownNode).toHaveClass(ERROR_CLASS) }) + + test('should display error state when node causes execution error', async ({ + comfyPage + }) => { + await comfyPage.setup() + await comfyPage.loadWorkflow('nodes/execution_error') + await comfyPage.runButton.click() + + const raiseErrorNode = comfyPage.vueNodes.getNodeByTitle('Raise Error') + await expect(raiseErrorNode).toHaveClass(ERROR_CLASS) + }) })