diff --git a/browser_tests/tests/dialog.spec.ts b/browser_tests/tests/dialog.spec.ts index 558dff2068..bd8daec179 100644 --- a/browser_tests/tests/dialog.spec.ts +++ b/browser_tests/tests/dialog.spec.ts @@ -309,3 +309,21 @@ test.describe('Feedback dialog', () => { await expect(feedbackHeader).not.toBeVisible() }) }) + +test.describe('Error dialog', () => { + test('Should display an error dialog when graph configure fails', async ({ + comfyPage + }) => { + await comfyPage.page.evaluate(() => { + const graph = window['graph'] + graph.configure = () => { + throw new Error('Error on configure!') + } + }) + + await comfyPage.loadWorkflow('default') + + const errorDialog = comfyPage.page.locator('.error-dialog-content') + await expect(errorDialog).toBeVisible() + }) +}) diff --git a/src/components/dialog/content/ErrorDialogContent.vue b/src/components/dialog/content/ErrorDialogContent.vue new file mode 100644 index 0000000000..a1cb4113f3 --- /dev/null +++ b/src/components/dialog/content/ErrorDialogContent.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/locales/en/main.json b/src/locales/en/main.json index fd3acf1472..3c8a26d6be 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -951,6 +951,12 @@ "missingModels": "Missing Models", "missingModelsMessage": "When loading the graph, the following models were not found" }, + "errorDialog": { + "defaultTitle": "An error occurred", + "loadWorkflowTitle": "Loading aborted due to error reloading workflow data", + "noStackTrace": "No stacktrace available", + "extensionFileHint": "This may be due to the following script" + }, "desktopUpdate": { "title": "Updating ComfyUI Desktop", "description": "ComfyUI Desktop is installing new dependencies. This may take a few minutes.", diff --git a/src/locales/fr/main.json b/src/locales/fr/main.json index b94b9c3322..a50efa80c1 100644 --- a/src/locales/fr/main.json +++ b/src/locales/fr/main.json @@ -115,6 +115,12 @@ "paused": "En pause", "resume": "Reprendre le téléchargement" }, + "errorDialog": { + "defaultTitle": "Une erreur est survenue", + "extensionFileHint": "Cela peut être dû au script suivant", + "loadWorkflowTitle": "Chargement interrompu en raison d'une erreur de rechargement des données de workflow", + "noStackTrace": "Aucune trace de pile disponible" + }, "g": { "about": "À propos", "add": "Ajouter", diff --git a/src/locales/ja/main.json b/src/locales/ja/main.json index 2b0b0ff83c..8a22093f1a 100644 --- a/src/locales/ja/main.json +++ b/src/locales/ja/main.json @@ -115,6 +115,12 @@ "paused": "一時停止", "resume": "ダウンロードを再開" }, + "errorDialog": { + "defaultTitle": "エラーが発生しました", + "extensionFileHint": "これは次のスクリプトが原因かもしれません", + "loadWorkflowTitle": "ワークフローデータの再読み込みエラーにより、読み込みが中止されました", + "noStackTrace": "スタックトレースは利用できません" + }, "g": { "about": "情報", "add": "追加", diff --git a/src/locales/ko/main.json b/src/locales/ko/main.json index ff588b77a0..3abee4320c 100644 --- a/src/locales/ko/main.json +++ b/src/locales/ko/main.json @@ -115,6 +115,12 @@ "paused": "일시 중지됨", "resume": "다운로드 재개" }, + "errorDialog": { + "defaultTitle": "오류가 발생했습니다", + "extensionFileHint": "다음 스크립트 때문일 수 있습니다", + "loadWorkflowTitle": "워크플로우 데이터를 다시 로드하는 중 오류로 인해 로드가 중단되었습니다", + "noStackTrace": "스택 추적이 사용할 수 없습니다" + }, "g": { "about": "정보", "add": "추가", diff --git a/src/locales/ru/main.json b/src/locales/ru/main.json index c80e915c8f..802cdd2807 100644 --- a/src/locales/ru/main.json +++ b/src/locales/ru/main.json @@ -115,6 +115,12 @@ "paused": "Приостановлено", "resume": "Возобновить загрузку" }, + "errorDialog": { + "defaultTitle": "Произошла ошибка", + "extensionFileHint": "Это может быть связано со следующим скриптом", + "loadWorkflowTitle": "Загрузка прервана из-за ошибки при перезагрузке данных рабочего процесса", + "noStackTrace": "Стек вызовов недоступен" + }, "g": { "about": "О программе", "add": "Добавить", diff --git a/src/locales/zh/main.json b/src/locales/zh/main.json index b6c5bbeec1..df3667fd6e 100644 --- a/src/locales/zh/main.json +++ b/src/locales/zh/main.json @@ -115,6 +115,12 @@ "paused": "已暂停", "resume": "恢复下载" }, + "errorDialog": { + "defaultTitle": "发生错误", + "extensionFileHint": "这可能是由于以下脚本", + "loadWorkflowTitle": "由于重新加载工作流数据出错,加载被中止", + "noStackTrace": "无可用堆栈跟踪" + }, "g": { "about": "关于", "add": "添加", diff --git a/src/scripts/app.ts b/src/scripts/app.ts index d185ce5b35..aceef39fc0 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -11,7 +11,7 @@ import _ from 'lodash' import type { ToastMessageOptions } from 'primevue/toast' import { reactive } from 'vue' -import { st } from '@/i18n' +import { st, t } from '@/i18n' import type { ResultItem } from '@/schemas/apiSchema' import { type ComfyWorkflowJSON, @@ -1144,56 +1144,10 @@ export class ComfyApp { this.canvas.ds.scale = graphData.extra.ds.scale } } catch (error) { - let errorHint = [] - // Try extracting filename to see if it was caused by an extension script - const filename = - // @ts-expect-error fixme ts strict error - error.fileName || - // @ts-expect-error fixme ts strict error - (error.stack || '').match(/(\/extensions\/.*\.js)/)?.[1] - const pos = (filename || '').indexOf('/extensions/') - if (pos > -1) { - errorHint.push( - $el('span', { - textContent: 'This may be due to the following script:' - }), - $el('br'), - $el('span', { - style: { - fontWeight: 'bold' - }, - textContent: filename.substring(pos) - }) - ) - } - - // Show dialog to let the user know something went wrong loading the data - this.ui.dialog.show( - $el('div', [ - $el('p', { - textContent: 'Loading aborted due to error reloading workflow data' - }), - $el('pre', { - style: { padding: '5px', backgroundColor: 'rgba(255,0,0,0.2)' }, - // @ts-expect-error fixme ts strict error - textContent: error.toString() - }), - $el('pre', { - style: { - padding: '5px', - color: '#ccc', - fontSize: '10px', - maxHeight: '50vh', - overflow: 'auto', - backgroundColor: 'rgba(0,0,0,0.2)' - }, - // @ts-expect-error fixme ts strict error - textContent: error.stack || 'No stacktrace available' - }), - ...errorHint - ]).outerHTML - ) - + useDialogService().showErrorDialog(error, { + title: t('errorDialog.loadWorkflowTitle'), + errorType: 'loadWorkflowError' + }) return } for (const node of this.graph.nodes) { @@ -1384,8 +1338,7 @@ export class ComfyApp { return !this.lastNodeErrors } - // @ts-expect-error fixme ts strict error - showErrorOnFileLoad(file) { + showErrorOnFileLoad(file: File) { this.ui.dialog.show( $el('div', [ $el('p', { textContent: `Unable to find workflow in ${file.name}` }) diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts index b3aa83f6af..d9a5d37324 100644 --- a/src/services/dialogService.ts +++ b/src/services/dialogService.ts @@ -1,4 +1,5 @@ import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.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 LoadWorkflowWarning from '@/components/dialog/content/LoadWorkflowWarning.vue' @@ -147,6 +148,52 @@ export const useDialogService = () => { }) } + function parseError(error: Error) { + const filename = + 'fileName' in error + ? (error.fileName as string) + : error.stack?.match(/(\/extensions\/.*\.js)/)?.[1] + + const extensionFile = filename + ? filename.substring(filename.indexOf('/extensions/')) + : undefined + + return { + errorMessage: error.toString(), + stackTrace: error.stack, + extensionFile + } + } + + /** + * Show a error dialog to the user when an error occurs. + * @param error The error to show + * @param options The options for the dialog + */ + function showErrorDialog( + error: unknown, + options: { + title?: string + errorType?: string + } = {} + ) { + const props = + error instanceof Error + ? parseError(error) + : { + errorMessage: String(error) + } + + dialogStore.showDialog({ + key: 'global-error', + component: ErrorDialogContent, + props: { + ...props, + ...options + } + }) + } + async function prompt({ title, message, @@ -230,6 +277,7 @@ export const useDialogService = () => { showIssueReportDialog, showManagerDialog, showManagerProgressDialog, + showErrorDialog, prompt, confirm }