mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 18:52:19 +00:00
[Refactor] Unify error dialog component (#3265)
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
@@ -323,7 +323,7 @@ test.describe('Error dialog', () => {
|
|||||||
|
|
||||||
await comfyPage.loadWorkflow('default')
|
await comfyPage.loadWorkflow('default')
|
||||||
|
|
||||||
const errorDialog = comfyPage.page.locator('.error-dialog-content')
|
const errorDialog = comfyPage.page.locator('.comfy-error-report')
|
||||||
await expect(errorDialog).toBeVisible()
|
await expect(errorDialog).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,80 +1,159 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="error-dialog-content flex flex-col gap-4">
|
<div class="comfy-error-report flex flex-col gap-4">
|
||||||
<NoResultsPlaceholder
|
<NoResultsPlaceholder
|
||||||
class="pb-0"
|
class="pb-0"
|
||||||
icon="pi pi-exclamation-circle"
|
icon="pi pi-exclamation-circle"
|
||||||
:title="title"
|
:title="title"
|
||||||
:message="errorMessage"
|
:message="error.exceptionMessage"
|
||||||
/>
|
/>
|
||||||
<pre
|
<template v-if="error.extensionFile">
|
||||||
class="stack-trace p-5 text-neutral-400 text-xs max-h-[50vh] overflow-auto bg-black/20"
|
|
||||||
>
|
|
||||||
{{ stackTrace }}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<template v-if="extensionFile">
|
|
||||||
<span>{{ t('errorDialog.extensionFileHint') }}:</span>
|
<span>{{ t('errorDialog.extensionFileHint') }}:</span>
|
||||||
<br />
|
<br />
|
||||||
<span class="font-bold">{{ extensionFile }}</span>
|
<span class="font-bold">{{ error.extensionFile }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<Button
|
<div class="flex gap-2 justify-center">
|
||||||
v-show="!sendReportOpen"
|
<Button
|
||||||
text
|
v-show="!reportOpen"
|
||||||
fluid
|
text
|
||||||
:label="$t('issueReport.helpFix')"
|
:label="$t('g.showReport')"
|
||||||
@click="showSendReport"
|
@click="showReport"
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
v-show="!sendReportOpen"
|
||||||
|
text
|
||||||
|
:label="$t('issueReport.helpFix')"
|
||||||
|
@click="showSendReport"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template v-if="reportOpen">
|
||||||
|
<Divider />
|
||||||
|
<ScrollPanel class="w-full h-[400px] max-w-[80vw]">
|
||||||
|
<pre class="whitespace-pre-wrap break-words">{{ reportContent }}</pre>
|
||||||
|
</ScrollPanel>
|
||||||
|
<Divider />
|
||||||
|
</template>
|
||||||
<ReportIssuePanel
|
<ReportIssuePanel
|
||||||
v-if="sendReportOpen"
|
v-if="sendReportOpen"
|
||||||
:error-type="errorType"
|
:title="$t('issueReport.submitErrorReport')"
|
||||||
:extra-fields="[
|
:error-type="error.reportType ?? 'unknownError'"
|
||||||
{
|
:extra-fields="[stackTraceField]"
|
||||||
label: t('issueReport.stackTrace'),
|
|
||||||
value: 'StackTrace',
|
|
||||||
optIn: true,
|
|
||||||
getData: () => stackTrace
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
:tags="{
|
:tags="{
|
||||||
exceptionMessage: errorMessage,
|
exceptionMessage: error.exceptionMessage,
|
||||||
extensionFile: extensionFile ?? 'UNKNOWN'
|
nodeType: error.nodeType ?? 'UNKNOWN'
|
||||||
}"
|
}"
|
||||||
:title="t('issueReport.submitErrorReport')"
|
|
||||||
/>
|
/>
|
||||||
|
<div class="flex gap-4 justify-end">
|
||||||
|
<FindIssueButton
|
||||||
|
:errorMessage="error.exceptionMessage"
|
||||||
|
:repoOwner="repoOwner"
|
||||||
|
:repoName="repoName"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
v-if="reportOpen"
|
||||||
|
:label="$t('g.copyToClipboard')"
|
||||||
|
icon="pi pi-copy"
|
||||||
|
@click="copyReportToClipboard"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import { computed, ref } from 'vue'
|
import Divider from 'primevue/divider'
|
||||||
|
import ScrollPanel from 'primevue/scrollpanel'
|
||||||
|
import { useToast } from 'primevue/usetoast'
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||||
|
import FindIssueButton from '@/components/dialog/content/error/FindIssueButton.vue'
|
||||||
|
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||||
|
import { api } from '@/scripts/api'
|
||||||
|
import { app } from '@/scripts/app'
|
||||||
|
import { useSystemStatsStore } from '@/stores/systemStatsStore'
|
||||||
|
import type { ReportField } from '@/types/issueReportTypes'
|
||||||
|
import {
|
||||||
|
type ErrorReportData,
|
||||||
|
generateErrorReport
|
||||||
|
} from '@/utils/errorReportUtil'
|
||||||
|
|
||||||
import ReportIssuePanel from './error/ReportIssuePanel.vue'
|
import ReportIssuePanel from './error/ReportIssuePanel.vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { error } = defineProps<{
|
||||||
const {
|
error: Omit<ErrorReportData, 'workflow' | 'systemStats' | 'serverLogs'> & {
|
||||||
title: _title,
|
/**
|
||||||
errorMessage,
|
* The type of error report to submit.
|
||||||
stackTrace: _stackTrace,
|
* @default 'unknownError'
|
||||||
extensionFile,
|
*/
|
||||||
errorType = 'frontendError'
|
reportType?: string
|
||||||
} = defineProps<{
|
/**
|
||||||
title?: string
|
* The file name of the extension that caused the error.
|
||||||
errorMessage: string
|
*/
|
||||||
stackTrace?: string
|
extensionFile?: string
|
||||||
extensionFile?: string
|
}
|
||||||
errorType?: string
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const title = computed(() => _title ?? t('errorDialog.defaultTitle'))
|
const repoOwner = 'comfyanonymous'
|
||||||
const stackTrace = computed(() => _stackTrace ?? t('errorDialog.noStackTrace'))
|
const repoName = 'ComfyUI'
|
||||||
|
const reportContent = ref('')
|
||||||
|
const reportOpen = ref(false)
|
||||||
|
const showReport = () => {
|
||||||
|
reportOpen.value = true
|
||||||
|
}
|
||||||
const sendReportOpen = ref(false)
|
const sendReportOpen = ref(false)
|
||||||
function showSendReport() {
|
const showSendReport = () => {
|
||||||
sendReportOpen.value = true
|
sendReportOpen.value = true
|
||||||
}
|
}
|
||||||
|
const toast = useToast()
|
||||||
|
const { t } = useI18n()
|
||||||
|
const systemStatsStore = useSystemStatsStore()
|
||||||
|
|
||||||
|
const title = computed<string>(
|
||||||
|
() => error.nodeType ?? error.exceptionType ?? t('errorDialog.defaultTitle')
|
||||||
|
)
|
||||||
|
|
||||||
|
const stackTraceField = computed<ReportField>(() => {
|
||||||
|
return {
|
||||||
|
label: t('issueReport.stackTrace'),
|
||||||
|
value: 'StackTrace',
|
||||||
|
optIn: true,
|
||||||
|
getData: () => error.traceback
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!systemStatsStore.systemStats) {
|
||||||
|
await systemStatsStore.fetchSystemStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [logs] = await Promise.all([api.getLogs()])
|
||||||
|
|
||||||
|
reportContent.value = generateErrorReport({
|
||||||
|
systemStats: systemStatsStore.systemStats!,
|
||||||
|
serverLogs: logs,
|
||||||
|
workflow: app.graph.serialize(),
|
||||||
|
exceptionType: error.exceptionType,
|
||||||
|
exceptionMessage: error.exceptionMessage,
|
||||||
|
traceback: error.traceback,
|
||||||
|
nodeId: error.nodeId,
|
||||||
|
nodeType: error.nodeType
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching logs:', error)
|
||||||
|
toast.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: t('g.error'),
|
||||||
|
detail: t('toastMessages.failedToFetchLogs'),
|
||||||
|
life: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { copyToClipboard } = useCopyToClipboard()
|
||||||
|
const copyReportToClipboard = async () => {
|
||||||
|
await copyToClipboard(reportContent.value)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
<template>
|
|
||||||
<NoResultsPlaceholder
|
|
||||||
icon="pi pi-exclamation-circle"
|
|
||||||
:title="error.node_type"
|
|
||||||
:message="error.exception_message"
|
|
||||||
/>
|
|
||||||
<div class="comfy-error-report">
|
|
||||||
<div class="flex gap-2 justify-center">
|
|
||||||
<Button
|
|
||||||
v-show="!reportOpen"
|
|
||||||
text
|
|
||||||
:label="$t('g.showReport')"
|
|
||||||
@click="showReport"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
v-show="!sendReportOpen"
|
|
||||||
text
|
|
||||||
:label="$t('issueReport.helpFix')"
|
|
||||||
@click="showSendReport"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<template v-if="reportOpen">
|
|
||||||
<Divider />
|
|
||||||
<ScrollPanel style="width: 100%; height: 400px; max-width: 80vw">
|
|
||||||
<pre class="wrapper-pre">{{ reportContent }}</pre>
|
|
||||||
</ScrollPanel>
|
|
||||||
<Divider />
|
|
||||||
</template>
|
|
||||||
<ReportIssuePanel
|
|
||||||
v-if="sendReportOpen"
|
|
||||||
:title="$t('issueReport.submitErrorReport')"
|
|
||||||
error-type="graphExecutionError"
|
|
||||||
:extra-fields="[stackTraceField]"
|
|
||||||
:tags="{
|
|
||||||
exceptionMessage: error.exception_message,
|
|
||||||
nodeType: error.node_type
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
<div class="action-container">
|
|
||||||
<FindIssueButton
|
|
||||||
:errorMessage="error.exception_message"
|
|
||||||
:repoOwner="repoOwner"
|
|
||||||
:repoName="repoName"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
v-if="reportOpen"
|
|
||||||
:label="$t('g.copyToClipboard')"
|
|
||||||
icon="pi pi-copy"
|
|
||||||
@click="copyReportToClipboard"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import Button from 'primevue/button'
|
|
||||||
import Divider from 'primevue/divider'
|
|
||||||
import ScrollPanel from 'primevue/scrollpanel'
|
|
||||||
import { useToast } from 'primevue/usetoast'
|
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
|
||||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
|
||||||
import FindIssueButton from '@/components/dialog/content/error/FindIssueButton.vue'
|
|
||||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
|
||||||
import type { ExecutionErrorWsMessage, SystemStats } from '@/schemas/apiSchema'
|
|
||||||
import { api } from '@/scripts/api'
|
|
||||||
import { app } from '@/scripts/app'
|
|
||||||
import type { ReportField } from '@/types/issueReportTypes'
|
|
||||||
import { generateErrorReport } from '@/utils/errorReportUtil'
|
|
||||||
|
|
||||||
import ReportIssuePanel from './error/ReportIssuePanel.vue'
|
|
||||||
|
|
||||||
const { error } = defineProps<{
|
|
||||||
error: ExecutionErrorWsMessage
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const repoOwner = 'comfyanonymous'
|
|
||||||
const repoName = 'ComfyUI'
|
|
||||||
const reportContent = ref('')
|
|
||||||
const reportOpen = ref(false)
|
|
||||||
const showReport = () => {
|
|
||||||
reportOpen.value = true
|
|
||||||
}
|
|
||||||
const sendReportOpen = ref(false)
|
|
||||||
const showSendReport = () => {
|
|
||||||
sendReportOpen.value = true
|
|
||||||
}
|
|
||||||
const toast = useToast()
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const stackTraceField = computed<ReportField>(() => {
|
|
||||||
return {
|
|
||||||
label: t('issueReport.stackTrace'),
|
|
||||||
value: 'StackTrace',
|
|
||||||
optIn: true,
|
|
||||||
getData: () => error.traceback?.join('\n')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
try {
|
|
||||||
const [systemStats, logs] = await Promise.all([
|
|
||||||
api.getSystemStats(),
|
|
||||||
api.getLogs()
|
|
||||||
])
|
|
||||||
generateReport(systemStats, logs)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching system stats or logs:', error)
|
|
||||||
toast.add({
|
|
||||||
severity: 'error',
|
|
||||||
summary: 'Error',
|
|
||||||
detail: 'Failed to fetch system information',
|
|
||||||
life: 5000
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const generateReport = (systemStats: SystemStats, logs: string) => {
|
|
||||||
reportContent.value = generateErrorReport({
|
|
||||||
systemStats,
|
|
||||||
serverLogs: logs,
|
|
||||||
workflow: app.graph.serialize(),
|
|
||||||
exception_type: error.exception_type,
|
|
||||||
exception_message: error.exception_message,
|
|
||||||
traceback: error.traceback,
|
|
||||||
node_id: error.node_id,
|
|
||||||
node_type: error.node_type
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const { copyToClipboard } = useCopyToClipboard()
|
|
||||||
const copyReportToClipboard = async () => {
|
|
||||||
await copyToClipboard(reportContent.value)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.comfy-error-report {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-container {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper-pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1022,6 +1022,7 @@
|
|||||||
"nodeDefinitionsUpdated": "Node definitions updated",
|
"nodeDefinitionsUpdated": "Node definitions updated",
|
||||||
"errorSaveSetting": "Error saving setting {id}: {err}",
|
"errorSaveSetting": "Error saving setting {id}: {err}",
|
||||||
"errorCopyImage": "Error copying image: {error}",
|
"errorCopyImage": "Error copying image: {error}",
|
||||||
"noTemplatesToExport": "No templates to export"
|
"noTemplatesToExport": "No templates to export",
|
||||||
|
"failedToFetchLogs": "Failed to fetch server logs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -995,6 +995,7 @@
|
|||||||
"failedToApplyTexture": "Error al aplicar textura",
|
"failedToApplyTexture": "Error al aplicar textura",
|
||||||
"failedToDownloadFile": "Error al descargar el archivo",
|
"failedToDownloadFile": "Error al descargar el archivo",
|
||||||
"failedToExportModel": "Error al exportar modelo como {format}",
|
"failedToExportModel": "Error al exportar modelo como {format}",
|
||||||
|
"failedToFetchLogs": "Error al obtener los registros del servidor",
|
||||||
"fileLoadError": "No se puede encontrar el flujo de trabajo en {fileName}",
|
"fileLoadError": "No se puede encontrar el flujo de trabajo en {fileName}",
|
||||||
"fileUploadFailed": "Error al subir el archivo",
|
"fileUploadFailed": "Error al subir el archivo",
|
||||||
"interrupted": "La ejecución ha sido interrumpida",
|
"interrupted": "La ejecución ha sido interrumpida",
|
||||||
|
|||||||
@@ -995,6 +995,7 @@
|
|||||||
"failedToApplyTexture": "Échec de l'application de la texture",
|
"failedToApplyTexture": "Échec de l'application de la texture",
|
||||||
"failedToDownloadFile": "Échec du téléchargement du fichier",
|
"failedToDownloadFile": "Échec du téléchargement du fichier",
|
||||||
"failedToExportModel": "Échec de l'exportation du modèle en {format}",
|
"failedToExportModel": "Échec de l'exportation du modèle en {format}",
|
||||||
|
"failedToFetchLogs": "Échec de la récupération des journaux du serveur",
|
||||||
"fileLoadError": "Impossible de trouver le flux de travail dans {fileName}",
|
"fileLoadError": "Impossible de trouver le flux de travail dans {fileName}",
|
||||||
"fileUploadFailed": "Échec du téléchargement du fichier",
|
"fileUploadFailed": "Échec du téléchargement du fichier",
|
||||||
"interrupted": "L'exécution a été interrompue",
|
"interrupted": "L'exécution a été interrompue",
|
||||||
|
|||||||
@@ -995,6 +995,7 @@
|
|||||||
"failedToApplyTexture": "テクスチャの適用に失敗しました",
|
"failedToApplyTexture": "テクスチャの適用に失敗しました",
|
||||||
"failedToDownloadFile": "ファイルのダウンロードに失敗しました",
|
"failedToDownloadFile": "ファイルのダウンロードに失敗しました",
|
||||||
"failedToExportModel": "{format}としてモデルのエクスポートに失敗しました",
|
"failedToExportModel": "{format}としてモデルのエクスポートに失敗しました",
|
||||||
|
"failedToFetchLogs": "サーバーログの取得に失敗しました",
|
||||||
"fileLoadError": "{fileName}でワークフローが見つかりません",
|
"fileLoadError": "{fileName}でワークフローが見つかりません",
|
||||||
"fileUploadFailed": "ファイルのアップロードに失敗しました",
|
"fileUploadFailed": "ファイルのアップロードに失敗しました",
|
||||||
"interrupted": "実行が中断されました",
|
"interrupted": "実行が中断されました",
|
||||||
|
|||||||
@@ -995,6 +995,7 @@
|
|||||||
"failedToApplyTexture": "텍스처 적용에 실패했습니다",
|
"failedToApplyTexture": "텍스처 적용에 실패했습니다",
|
||||||
"failedToDownloadFile": "파일 다운로드에 실패했습니다",
|
"failedToDownloadFile": "파일 다운로드에 실패했습니다",
|
||||||
"failedToExportModel": "{format} 형식으로 모델 내보내기에 실패했습니다",
|
"failedToExportModel": "{format} 형식으로 모델 내보내기에 실패했습니다",
|
||||||
|
"failedToFetchLogs": "서버 로그를 가져오는 데 실패했습니다",
|
||||||
"fileLoadError": "{fileName}에서 워크플로우를 찾을 수 없습니다",
|
"fileLoadError": "{fileName}에서 워크플로우를 찾을 수 없습니다",
|
||||||
"fileUploadFailed": "파일 업로드에 실패했습니다",
|
"fileUploadFailed": "파일 업로드에 실패했습니다",
|
||||||
"interrupted": "실행이 중단되었습니다",
|
"interrupted": "실행이 중단되었습니다",
|
||||||
|
|||||||
@@ -995,6 +995,7 @@
|
|||||||
"failedToApplyTexture": "Не удалось применить текстуру",
|
"failedToApplyTexture": "Не удалось применить текстуру",
|
||||||
"failedToDownloadFile": "Не удалось скачать файл",
|
"failedToDownloadFile": "Не удалось скачать файл",
|
||||||
"failedToExportModel": "Не удалось экспортировать модель как {format}",
|
"failedToExportModel": "Не удалось экспортировать модель как {format}",
|
||||||
|
"failedToFetchLogs": "Не удалось получить серверные логи",
|
||||||
"fileLoadError": "Не удалось найти рабочий процесс в {fileName}",
|
"fileLoadError": "Не удалось найти рабочий процесс в {fileName}",
|
||||||
"fileUploadFailed": "Не удалось загрузить файл",
|
"fileUploadFailed": "Не удалось загрузить файл",
|
||||||
"interrupted": "Выполнение было прервано",
|
"interrupted": "Выполнение было прервано",
|
||||||
|
|||||||
@@ -995,6 +995,7 @@
|
|||||||
"failedToApplyTexture": "应用纹理失败",
|
"failedToApplyTexture": "应用纹理失败",
|
||||||
"failedToDownloadFile": "文件下载失败",
|
"failedToDownloadFile": "文件下载失败",
|
||||||
"failedToExportModel": "无法将模型导出为 {format}",
|
"failedToExportModel": "无法将模型导出为 {format}",
|
||||||
|
"failedToFetchLogs": "无法获取服务器日志",
|
||||||
"fileLoadError": "无法在 {fileName} 中找到工作流",
|
"fileLoadError": "无法在 {fileName} 中找到工作流",
|
||||||
"fileUploadFailed": "文件上传失败",
|
"fileUploadFailed": "文件上传失败",
|
||||||
"interrupted": "执行已被中断",
|
"interrupted": "执行已被中断",
|
||||||
|
|||||||
@@ -741,7 +741,7 @@ export class ComfyApp {
|
|||||||
|
|
||||||
api.addEventListener('execution_error', ({ detail }) => {
|
api.addEventListener('execution_error', ({ detail }) => {
|
||||||
this.lastExecutionError = detail
|
this.lastExecutionError = detail
|
||||||
useDialogService().showExecutionErrorDialog({ error: detail })
|
useDialogService().showExecutionErrorDialog(detail)
|
||||||
this.canvas.draw(true, true)
|
this.canvas.draw(true, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1129,7 +1129,7 @@ export class ComfyApp {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
useDialogService().showErrorDialog(error, {
|
useDialogService().showErrorDialog(error, {
|
||||||
title: t('errorDialog.loadWorkflowTitle'),
|
title: t('errorDialog.loadWorkflowTitle'),
|
||||||
errorType: 'loadWorkflowError'
|
reportType: 'loadWorkflowError'
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
|
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
|
||||||
import ErrorDialogContent from '@/components/dialog/content/ErrorDialogContent.vue'
|
import ErrorDialogContent from '@/components/dialog/content/ErrorDialogContent.vue'
|
||||||
import ExecutionErrorDialogContent from '@/components/dialog/content/ExecutionErrorDialogContent.vue'
|
|
||||||
import IssueReportDialogContent from '@/components/dialog/content/IssueReportDialogContent.vue'
|
import IssueReportDialogContent from '@/components/dialog/content/IssueReportDialogContent.vue'
|
||||||
import LoadWorkflowWarning from '@/components/dialog/content/LoadWorkflowWarning.vue'
|
import LoadWorkflowWarning from '@/components/dialog/content/LoadWorkflowWarning.vue'
|
||||||
import ManagerProgressDialogContent from '@/components/dialog/content/ManagerProgressDialogContent.vue'
|
import ManagerProgressDialogContent from '@/components/dialog/content/ManagerProgressDialogContent.vue'
|
||||||
@@ -15,6 +14,7 @@ import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.
|
|||||||
import TemplateWorkflowsContent from '@/components/templates/TemplateWorkflowsContent.vue'
|
import TemplateWorkflowsContent from '@/components/templates/TemplateWorkflowsContent.vue'
|
||||||
import TemplateWorkflowsDialogHeader from '@/components/templates/TemplateWorkflowsDialogHeader.vue'
|
import TemplateWorkflowsDialogHeader from '@/components/templates/TemplateWorkflowsDialogHeader.vue'
|
||||||
import { t } from '@/i18n'
|
import { t } from '@/i18n'
|
||||||
|
import type { ExecutionErrorWsMessage } from '@/schemas/apiSchema'
|
||||||
import { type ShowDialogOptions, useDialogStore } from '@/stores/dialogStore'
|
import { type ShowDialogOptions, useDialogStore } from '@/stores/dialogStore'
|
||||||
|
|
||||||
export type ConfirmationDialogType =
|
export type ConfirmationDialogType =
|
||||||
@@ -70,12 +70,21 @@ export const useDialogService = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function showExecutionErrorDialog(
|
function showExecutionErrorDialog(executionError: ExecutionErrorWsMessage) {
|
||||||
props: InstanceType<typeof ExecutionErrorDialogContent>['$props']
|
const props: InstanceType<typeof ErrorDialogContent>['$props'] = {
|
||||||
) {
|
error: {
|
||||||
|
exceptionType: executionError.exception_type,
|
||||||
|
exceptionMessage: executionError.exception_message,
|
||||||
|
nodeId: executionError.node_id,
|
||||||
|
nodeType: executionError.node_type,
|
||||||
|
traceback: executionError.traceback.join('\n'),
|
||||||
|
reportType: 'graphExecutionError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dialogStore.showDialog({
|
dialogStore.showDialog({
|
||||||
key: 'global-execution-error',
|
key: 'global-execution-error',
|
||||||
component: ExecutionErrorDialogContent,
|
component: ErrorDialogContent,
|
||||||
props
|
props
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -174,23 +183,33 @@ export const useDialogService = () => {
|
|||||||
error: unknown,
|
error: unknown,
|
||||||
options: {
|
options: {
|
||||||
title?: string
|
title?: string
|
||||||
errorType?: string
|
reportType?: string
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
const props =
|
const errorProps: {
|
||||||
|
errorMessage: string
|
||||||
|
stackTrace?: string
|
||||||
|
extensionFile?: string
|
||||||
|
} =
|
||||||
error instanceof Error
|
error instanceof Error
|
||||||
? parseError(error)
|
? parseError(error)
|
||||||
: {
|
: {
|
||||||
errorMessage: String(error)
|
errorMessage: String(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const props: InstanceType<typeof ErrorDialogContent>['$props'] = {
|
||||||
|
error: {
|
||||||
|
exceptionType: options.title ?? 'Unknown Error',
|
||||||
|
exceptionMessage: errorProps.errorMessage,
|
||||||
|
traceback: errorProps.stackTrace ?? t('errorDialog.noStackTrace'),
|
||||||
|
reportType: options.reportType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dialogStore.showDialog({
|
dialogStore.showDialog({
|
||||||
key: 'global-error',
|
key: 'global-error',
|
||||||
component: ErrorDialogContent,
|
component: ErrorDialogContent,
|
||||||
props: {
|
props
|
||||||
...props,
|
|
||||||
...options
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import type { SystemStats } from '@/schemas/apiSchema'
|
|||||||
import type { NodeId } from '@/schemas/comfyWorkflowSchema'
|
import type { NodeId } from '@/schemas/comfyWorkflowSchema'
|
||||||
|
|
||||||
export interface ErrorReportData {
|
export interface ErrorReportData {
|
||||||
exception_type: string
|
exceptionType: string
|
||||||
exception_message: string
|
exceptionMessage: string
|
||||||
systemStats: SystemStats
|
systemStats: SystemStats
|
||||||
serverLogs: string
|
serverLogs: string
|
||||||
workflow: ISerialisedGraph
|
workflow: ISerialisedGraph
|
||||||
|
|
||||||
traceback?: string[]
|
traceback?: string
|
||||||
node_id?: NodeId
|
nodeId?: NodeId
|
||||||
node_type?: string
|
nodeType?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,13 +35,13 @@ export function generateErrorReport(error: ErrorReportData): string {
|
|||||||
${
|
${
|
||||||
error
|
error
|
||||||
? `## Error Details
|
? `## Error Details
|
||||||
- **Node ID:** ${error.node_id || 'N/A'}
|
- **Node ID:** ${error.nodeId || 'N/A'}
|
||||||
- **Node Type:** ${error.node_type || 'N/A'}
|
- **Node Type:** ${error.nodeType || 'N/A'}
|
||||||
- **Exception Type:** ${error.exception_type || 'N/A'}
|
- **Exception Type:** ${error.exceptionType || 'N/A'}
|
||||||
- **Exception Message:** ${error.exception_message || 'N/A'}
|
- **Exception Message:** ${error.exceptionMessage || 'N/A'}
|
||||||
## Stack Trace
|
## Stack Trace
|
||||||
\`\`\`
|
\`\`\`
|
||||||
${error.traceback ? error.traceback.join('\n') : 'No stack trace available'}
|
${error.traceback || 'No stack trace available'}
|
||||||
\`\`\``
|
\`\`\``
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user