mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-07 08:14:42 +00:00
## Summary Simplifies the error overlay so it presents one clear title, one clear message, and one stable details action instead of rendering a list of per-error messages. ## Changes - **What**: Extracts the error overlay view model into `useErrorOverlayState`, adds focused unit coverage for the overlay copy resolution rules, and updates the overlay E2E coverage to match the new behavior. - **Breaking**: None. - **Dependencies**: None. ### Behavior changes - The overlay body no longer renders a `<ul>` of individual error messages. It now always renders a single paragraph message. - Single-error overlays now prefer toast-specific copy when it exists. For execution errors, the overlay resolves the message in this order: `toastMessage`, `displayMessage`, raw `message`, group `displayMessage`, then group `displayTitle`. The title resolves from `toastTitle`, then `displayTitle`, then the group title. - Single non-execution groups use group-level toast/display copy. This lets grouped error types supply overlay-friendly copy without the overlay needing to understand each card implementation. - Multiple-error overlays now ignore individual error item copy in the overlay itself. The header becomes the pluralized count title, for example `7 errors found`, and the body becomes the fixed guidance message: `Resolve them before running the workflow.` - The overlay is hidden if the store reports an error count but no resolved overlay message exists. This avoids rendering a visible shell with an empty body. - The action button no longer varies by error type in normal app mode. Missing nodes, missing models, missing media, swap nodes, validation errors, and runtime errors all use `View details` instead of labels like `Show missing nodes`, `Show missing models`, `Show missing inputs`, or `See Errors`. - App mode keeps its existing `Show errors in graph` action label. - The overlay width now keeps the previous width as its minimum and allows a wider maximum, reducing avoidable wrapping in longer error headers. - The live region was softened from an assertive alert-style announcement to `role="status"` with `aria-live="polite"` so updates such as count changes are less disruptive. ### Tests - Adds component coverage for the rendered overlay shape and app-mode action label. - Adds composable coverage for single execution errors, runtime errors, grouped missing media errors, multiple-error aggregate copy, hidden empty-message state, and display-copy fallback behavior. - Updates `errorOverlay.spec.ts` so the E2E suite checks the new single-message overlay, the stable `View details` action, and the fixed multiple-error body guidance. - Removes the old type-specific button-label E2E expectations because that branch no longer exists in product behavior. ### Follow-up PR A follow-up PR is stacked on top of this one: `jaeone/fe-816-missing-resource-error-messaging`. That follow-up will wire missing resource error resolvers into the copy model consumed here. It covers missing node packs, missing models, missing media, and swap-node groups, including the group-level `toastTitle`, `toastMessage`, `displayMessage`, `displayDetails`, and item label copy those cards need. This PR intentionally keeps the overlay behavior separate so it can merge first without depending on the missing-resource resolver copy. ## Review Focus - Please check the single-error versus multiple-error overlay behavior, especially the fallback order for execution error copy. - Please check that the `View details` action is now intentionally error-type agnostic in normal app mode while app mode keeps `Show errors in graph`. - Please check the empty-message guard and the requirement that a single-error overlay only resolves a single group when the total error count and group list agree. - Please check the E2E reduction: the old type-specific action-label assertions were removed because the UI branch they tested was removed. ## Screenshots (if applicable) N/A
104 lines
2.6 KiB
TypeScript
104 lines
2.6 KiB
TypeScript
import { computed } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { storeToRefs } from 'pinia'
|
|
|
|
import { useExecutionErrorStore } from '@/stores/executionErrorStore'
|
|
import { useErrorGroups } from '@/components/rightSidePanel/errors/useErrorGroups'
|
|
import type { ErrorGroup } from '@/components/rightSidePanel/errors/types'
|
|
|
|
function resolveSingleOverlayCopy(
|
|
group: ErrorGroup
|
|
): { title?: string; message: string } | undefined {
|
|
if (group.type === 'execution') {
|
|
const [card] = group.cards
|
|
const [error] = card?.errors ?? []
|
|
const message =
|
|
error?.toastMessage ??
|
|
error?.displayMessage ??
|
|
error?.message ??
|
|
group.displayMessage ??
|
|
group.displayTitle
|
|
|
|
if (!message) return undefined
|
|
|
|
return {
|
|
title: error?.toastTitle ?? error?.displayTitle ?? group.displayTitle,
|
|
message
|
|
}
|
|
}
|
|
|
|
const message =
|
|
group.toastMessage ?? group.displayMessage ?? group.displayTitle
|
|
if (!message) return undefined
|
|
|
|
return {
|
|
title: group.toastTitle ?? group.displayTitle,
|
|
message
|
|
}
|
|
}
|
|
|
|
export function useErrorOverlayState() {
|
|
const { t } = useI18n()
|
|
const executionErrorStore = useExecutionErrorStore()
|
|
const { totalErrorCount, isErrorOverlayOpen } =
|
|
storeToRefs(executionErrorStore)
|
|
const { allErrorGroups } = useErrorGroups('')
|
|
|
|
const hasExactlyOneError = computed(() => totalErrorCount.value === 1)
|
|
const hasMultipleErrors = computed(() => totalErrorCount.value > 1)
|
|
const singleErrorGroup = computed(() =>
|
|
hasExactlyOneError.value && allErrorGroups.value.length === 1
|
|
? allErrorGroups.value[0]
|
|
: undefined
|
|
)
|
|
|
|
const errorCountLabel = computed(() =>
|
|
t(
|
|
'errorOverlay.errorCount',
|
|
{ count: totalErrorCount.value },
|
|
totalErrorCount.value
|
|
)
|
|
)
|
|
|
|
const multipleErrorCountLabel = computed(() =>
|
|
t(
|
|
'errorOverlay.multipleErrorCount',
|
|
{ count: totalErrorCount.value },
|
|
totalErrorCount.value
|
|
)
|
|
)
|
|
|
|
const singleOverlayCopy = computed(() =>
|
|
singleErrorGroup.value
|
|
? resolveSingleOverlayCopy(singleErrorGroup.value)
|
|
: undefined
|
|
)
|
|
|
|
const overlayMessage = computed(() => {
|
|
if (hasMultipleErrors.value) {
|
|
return t('errorOverlay.multipleErrorsMessage')
|
|
}
|
|
|
|
return singleOverlayCopy.value?.message ?? ''
|
|
})
|
|
|
|
const overlayTitle = computed(() =>
|
|
hasMultipleErrors.value
|
|
? multipleErrorCountLabel.value
|
|
: (singleOverlayCopy.value?.title ?? errorCountLabel.value)
|
|
)
|
|
|
|
const isVisible = computed(
|
|
() =>
|
|
isErrorOverlayOpen.value &&
|
|
totalErrorCount.value > 0 &&
|
|
overlayMessage.value.trim().length > 0
|
|
)
|
|
|
|
return {
|
|
isVisible,
|
|
overlayMessage,
|
|
overlayTitle
|
|
}
|
|
}
|