diff --git a/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx
index 3578d5801..ab7df6394 100644
--- a/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx
+++ b/apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx
@@ -18,7 +18,7 @@ type MarkdownErrorBoundaryState = {
hasError: boolean;
};
-class MarkdownErrorBoundary extends Component<
+export class MarkdownErrorBoundary extends Component<
MarkdownErrorBoundaryProps,
MarkdownErrorBoundaryState
> {
@@ -32,6 +32,15 @@ class MarkdownErrorBoundary extends Component<
// Keep the original message visible without logging its potentially sensitive content.
}
+ componentDidUpdate(prevProps: MarkdownErrorBoundaryProps) {
+ if (
+ this.state.hasError &&
+ prevProps.fallbackText !== this.props.fallbackText
+ ) {
+ this.setState({ hasError: false });
+ }
+ }
+
render() {
if (this.state.hasError) {
return (
diff --git a/apps/ai-game-creator-shell/tests/ChatMarkdownMessage.test.tsx b/apps/ai-game-creator-shell/tests/ChatMarkdownMessage.test.tsx
index 82aec9c8e..149b4d25c 100644
--- a/apps/ai-game-creator-shell/tests/ChatMarkdownMessage.test.tsx
+++ b/apps/ai-game-creator-shell/tests/ChatMarkdownMessage.test.tsx
@@ -1,9 +1,19 @@
// @vitest-environment jsdom
import { render } from '@testing-library/react';
-import { describe, expect, it } from 'vitest';
+import { describe, expect, it, vi } from 'vitest';
-import { ChatMarkdownMessage } from '../src/components/ChatMarkdownMessage';
+import {
+ ChatMarkdownMessage,
+ MarkdownErrorBoundary,
+} from '../src/components/ChatMarkdownMessage';
+
+function FailingChild({ shouldThrow }: { shouldThrow: boolean }) {
+ if (shouldThrow) {
+ throw new Error('transient render failure');
+ }
+ return markdown recovered;
+}
describe('ChatMarkdownMessage', () => {
it('渲染 assistant 的 GFM 内容与代码块', () => {
@@ -95,6 +105,31 @@ describe('ChatMarkdownMessage', () => {
expect(code?.className).not.toContain('rounded');
});
+ it('累计文本变化后可从 Markdown 错误回退中恢复', () => {
+ const consoleError = vi
+ .spyOn(console, 'error')
+ .mockImplementation(() => {});
+ try {
+ const { container, rerender } = render(
+
+
+ ,
+ );
+
+ expect(container.textContent).toBe('partial');
+
+ rerender(
+
+
+ ,
+ );
+
+ expect(container.textContent).toBe('markdown recovered');
+ } finally {
+ consoleError.mockRestore();
+ }
+ });
+
it('保留流式 assistant 文本并标记 streaming 状态', () => {
const { container } = render(