diff --git a/apps/ai-game-creator-shell/src/features/app-shell/homeCreationFailure.ts b/apps/ai-game-creator-shell/src/features/app-shell/homeCreationFailure.ts index 2ac9e3fbe..f8e7bca7a 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/homeCreationFailure.ts +++ b/apps/ai-game-creator-shell/src/features/app-shell/homeCreationFailure.ts @@ -11,6 +11,8 @@ * 落回本模块的专用兜底,并按「工作区是否已经建好」分成两条,避免用户重做一遍。 */ +import { visibleClientErrorMessage } from '../../services/errorReporting'; + /** 未分类失败的专用兜底文案。 */ export const HOME_CREATION_UNCLASSIFIED_FAILURE = '创建未完成,请重试'; @@ -64,6 +66,19 @@ function isHomeCreationTransportText(text: string): boolean { ); } +function safeHomeCreationDetail(error: unknown, code?: string) { + const detail = visibleClientErrorMessage(error, '').trim(); + if (!detail) return ''; + if (!code) return detail; + const withoutCode = detail + .replace(code, '') + .replace(/^\s*[::;;,,-]\s*/u, '') + .trim(); + return withoutCode === code || /[::;;,,-]\s*$/u.test(withoutCode) + ? '' + : withoutCode; +} + /** * 由宿主错误算出用户可见文案。 * @@ -76,20 +91,25 @@ export function homeCreationFailureMessage( ): string { const projectCreated = options.projectCreated === true; const text = homeCreationDiagnosticText(error); + const safeDetail = safeHomeCreationDetail(error); if (isHomeCreationTransportText(text)) { - return projectCreated + const base = projectCreated ? HOME_CREATION_AFTER_PROJECT_IPC_FAILURE : HOME_CREATION_IPC_FAILURE; + return safeDetail ? `${base}(${safeDetail})` : base; } const code = HOME_CREATION_HOST_CODE.exec(text)?.[0]; if (code) { - return projectCreated + const base = projectCreated ? `工作区已创建;未能进入项目开发(${code});请从「已创建的工作区」打开后重试。` : `创建未完成(${code});请重试。`; + const detail = safeHomeCreationDetail(error, code); + return detail ? `${base}(${detail})` : base; } - return projectCreated + const base = projectCreated ? HOME_CREATION_AFTER_PROJECT_FAILURE : HOME_CREATION_UNCLASSIFIED_FAILURE; + return safeDetail ? `${base}(${safeDetail})` : base; } /** 包装成带证据的创建失败;控制器负责把 `diagnostic` 写进日志后再抛出。 */ diff --git a/apps/ai-game-creator-shell/tests/homeCreationFailure.test.ts b/apps/ai-game-creator-shell/tests/homeCreationFailure.test.ts index 11c1db813..5f45ba17d 100644 --- a/apps/ai-game-creator-shell/tests/homeCreationFailure.test.ts +++ b/apps/ai-game-creator-shell/tests/homeCreationFailure.test.ts @@ -28,7 +28,7 @@ it('宿主机器码原样显示,不落回通用文案', () => { it('调用通道失联使用专用 IPC 文案,不冒充业务原因', () => { expect(homeCreationFailureMessage(new Error('Failed to fetch'))).toBe( - HOME_CREATION_IPC_FAILURE, + `${HOME_CREATION_IPC_FAILURE}(Failed to fetch)`, ); expect( homeCreationFailureMessage( @@ -37,12 +37,14 @@ it('调用通道失联使用专用 IPC 文案,不冒充业务原因', () => { ), { projectCreated: true }, ), - ).toBe(HOME_CREATION_AFTER_PROJECT_IPC_FAILURE); + ).toBe( + `${HOME_CREATION_AFTER_PROJECT_IPC_FAILURE}(IPC custom protocol failed, Tauri will now use the postMessage interface instead TypeError: Failed to fetch)`, + ); }); it('拿不到任何机器码时使用专用兜底,并按是否已建好工作区分文案', () => { expect(homeCreationFailureMessage(new Error('自动创建测试结束'))).toBe( - HOME_CREATION_UNCLASSIFIED_FAILURE, + `${HOME_CREATION_UNCLASSIFIED_FAILURE}(自动创建测试结束)`, ); expect(homeCreationFailureMessage(undefined)).toBe( HOME_CREATION_UNCLASSIFIED_FAILURE, @@ -51,7 +53,9 @@ it('拿不到任何机器码时使用专用兜底,并按是否已建好工作 homeCreationFailureMessage(new Error('design runtime unavailable'), { projectCreated: true, }), - ).toBe(HOME_CREATION_AFTER_PROJECT_FAILURE); + ).toBe( + `${HOME_CREATION_AFTER_PROJECT_FAILURE}(design runtime unavailable)`, + ); }); it('宿主原始文本只进诊断,不进用户文案', () => { @@ -60,7 +64,9 @@ it('宿主原始文本只进诊断,不进用户文案', () => { { projectCreated: false }, ); expect(failure).toBeInstanceOf(HomeCreationFailure); - expect(failure.userMessage).toBe(HOME_CREATION_UNCLASSIFIED_FAILURE); + expect(failure.userMessage).toBe( + `${HOME_CREATION_UNCLASSIFIED_FAILURE}(Provider 返回 500:)`, + ); expect(failure.diagnostic).toBe( 'Provider 返回 500:https://provider.example/private', );