mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-18 09:48:09 +00:00
## Summary - Brand `NodeExecutionId` and `NodeLocatorId` as distinct required string types. - Route execution/locator ID construction through existing helper functions instead of minting raw strings at call sites. - Update tests and boundary parsing to use branded IDs without conflating them with local `NodeId` values. ## Validation - `pnpm typecheck` - `pnpm test:unit src/types/nodeIdentification.test.ts src/stores/executionStore.test.ts src/renderer/extensions/vueNodes/components/NodeSlots.test.ts src/composables/graph/useErrorClearingHooks.test.ts src/platform/nodeReplacement/missingNodeScan.test.ts -- --runInBand` - `pnpm exec eslint src/types/nodeIdentification.ts src/utils/graphTraversalUtil.ts src/platform/workflow/management/stores/workflowStore.ts src/renderer/extensions/minimap/data/LayoutStoreDataSource.ts src/renderer/extensions/vueNodes/execution/useNodeExecutionState.ts src/stores/workspace/favoritedWidgetsStore.ts src/stores/nodeOutputStore.ts src/utils/__tests__/executionErrorTestUtils.ts src/platform/nodeReplacement/missingNodeScan.test.ts src/stores/executionStore.test.ts --cache` Note: full `pnpm lint` timed out after 5 minutes while still in stylelint startup, so targeted lint was run on changed files. ## Open Question - Should root-level node IDs like `1` be considered valid `NodeExecutionId` values, or should `isNodeExecutionId()` require a colon and callers use a separate type/helper for root execution IDs?
134 lines
3.0 KiB
TypeScript
134 lines
3.0 KiB
TypeScript
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
import ErrorNodeCard from './ErrorNodeCard.vue'
|
|
import type { ErrorCardData } from './types'
|
|
import { createNodeExecutionId } from '@/types/nodeIdentification'
|
|
|
|
const meta: Meta<typeof ErrorNodeCard> = {
|
|
title: 'RightSidePanel/Errors/ErrorNodeCard',
|
|
component: ErrorNodeCard,
|
|
parameters: {
|
|
layout: 'centered'
|
|
},
|
|
decorators: [
|
|
(story) => ({
|
|
components: { story },
|
|
template:
|
|
'<div class="w-[330px] bg-base-surface border border-interface-stroke rounded-lg p-4"><story /></div>'
|
|
})
|
|
]
|
|
}
|
|
|
|
export default meta
|
|
type Story = StoryObj<typeof meta>
|
|
|
|
const singleErrorCard: ErrorCardData = {
|
|
id: 'node-10',
|
|
title: 'CLIPTextEncode',
|
|
nodeId: createNodeExecutionId([10]),
|
|
nodeTitle: 'CLIP Text Encode (Prompt)',
|
|
isSubgraphNode: false,
|
|
errors: [
|
|
{
|
|
message: 'Required input "text" is missing.',
|
|
details: 'Input: text\nExpected: STRING'
|
|
}
|
|
]
|
|
}
|
|
|
|
const multipleErrorsCard: ErrorCardData = {
|
|
id: 'node-24',
|
|
title: 'VAEDecode',
|
|
nodeId: createNodeExecutionId([24]),
|
|
nodeTitle: 'VAE Decode',
|
|
isSubgraphNode: false,
|
|
errors: [
|
|
{
|
|
message: 'Required input "samples" is missing.',
|
|
details: ''
|
|
},
|
|
{
|
|
message: 'Value "NaN" is not a valid number for "strength".',
|
|
details: 'Expected: FLOAT [0.0 .. 1.0]'
|
|
}
|
|
]
|
|
}
|
|
|
|
const runtimeErrorCard: ErrorCardData = {
|
|
id: 'exec-45',
|
|
title: 'KSampler',
|
|
nodeId: createNodeExecutionId([45]),
|
|
nodeTitle: 'KSampler',
|
|
isSubgraphNode: false,
|
|
errors: [
|
|
{
|
|
message: 'OutOfMemoryError: CUDA out of memory. Tried to allocate 1.2GB.',
|
|
details: [
|
|
'Traceback (most recent call last):',
|
|
' File "ksampler.py", line 142, in sample',
|
|
' samples = model.apply(latent)',
|
|
'RuntimeError: CUDA out of memory.'
|
|
].join('\n'),
|
|
isRuntimeError: true
|
|
}
|
|
]
|
|
}
|
|
|
|
const subgraphErrorCard: ErrorCardData = {
|
|
id: 'node-3:15',
|
|
title: 'KSampler',
|
|
nodeId: createNodeExecutionId([3, 15]),
|
|
nodeTitle: 'Nested KSampler',
|
|
isSubgraphNode: true,
|
|
errors: [
|
|
{
|
|
message: 'Latent input is required.',
|
|
details: ''
|
|
}
|
|
]
|
|
}
|
|
|
|
const promptOnlyCard: ErrorCardData = {
|
|
id: '__prompt__',
|
|
title: 'Prompt has no outputs.',
|
|
errors: [
|
|
{
|
|
message:
|
|
'The workflow does not contain any output nodes (e.g. Save Image, Preview Image) to produce a result.'
|
|
}
|
|
]
|
|
}
|
|
|
|
export const SingleValidationError: Story = {
|
|
args: {
|
|
card: singleErrorCard
|
|
}
|
|
}
|
|
|
|
/** Subgraph node error — shows "Enter subgraph" button */
|
|
export const WithEnterSubgraphButton: Story = {
|
|
args: {
|
|
card: subgraphErrorCard
|
|
}
|
|
}
|
|
|
|
/** Multiple validation errors on one node */
|
|
export const MultipleErrors: Story = {
|
|
args: {
|
|
card: multipleErrorsCard
|
|
}
|
|
}
|
|
|
|
/** Runtime execution error with full traceback */
|
|
export const RuntimeError: Story = {
|
|
args: {
|
|
card: runtimeErrorCard
|
|
}
|
|
}
|
|
|
|
/** Prompt-level error (no node header) */
|
|
export const PromptError: Story = {
|
|
args: {
|
|
card: promptOnlyCard
|
|
}
|
|
}
|