mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 09:13:14 +00:00
fix: contain markdown render crashes with an error boundary (#1586)
Streamdown bundles Mermaid and renders ```mermaid blocks itself; a diagram it can't parse throws during render and, with no boundary, white-screens the whole review page. Wrap the markdown render so any crash falls back to the raw text instead. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
parent
f54fc154ca
commit
609e5551c4
1 changed files with 54 additions and 12 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { memo, useMemo } from "react";
|
||||
import { Component, memo, useMemo } from "react";
|
||||
import { Streamdown } from "streamdown";
|
||||
import type { ReactNode } from "react";
|
||||
import "streamdown/styles.css";
|
||||
|
|
@ -76,6 +76,46 @@ const STREAMDOWN_COMPONENTS = {
|
|||
|
||||
const SHIKI_THEME: ["github-light", "github-dark"] = ["github-light", "github-dark"];
|
||||
|
||||
interface BoundaryProps {
|
||||
content: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
interface BoundaryState {
|
||||
failed: boolean;
|
||||
key: string;
|
||||
}
|
||||
|
||||
// Streamdown bundles Mermaid and renders ```mermaid blocks itself; a diagram it
|
||||
// can't parse throws during render and, with no boundary, white-screens the
|
||||
// whole page. Contain it and fall back to the raw markdown text.
|
||||
class MarkdownErrorBoundary extends Component<BoundaryProps, BoundaryState> {
|
||||
state: BoundaryState = { failed: false, key: this.props.content };
|
||||
|
||||
static getDerivedStateFromError(): Partial<BoundaryState> {
|
||||
return { failed: true };
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(
|
||||
props: BoundaryProps,
|
||||
state: BoundaryState
|
||||
): Partial<BoundaryState> | null {
|
||||
if (props.content !== state.key) return { failed: false, key: props.content };
|
||||
return null;
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
if (this.state.failed) {
|
||||
return (
|
||||
<pre className="whitespace-pre-wrap break-words [overflow-wrap:anywhere] font-sans text-[color:var(--ui-text)]">
|
||||
{this.props.content}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export const Markdown = memo(function Markdown({
|
||||
content,
|
||||
isLive = false,
|
||||
|
|
@ -99,17 +139,19 @@ export const Markdown = memo(function Markdown({
|
|||
|
||||
return (
|
||||
<div className="min-w-0 max-w-full text-[13px] leading-6 break-words [overflow-wrap:anywhere] [&_.streamdown]:text-[color:var(--ui-text)]">
|
||||
<Streamdown
|
||||
mode={isLive ? "streaming" : "static"}
|
||||
parseIncompleteMarkdown={isLive}
|
||||
isAnimating={isLive}
|
||||
animated={isLive ? STREAMDOWN_ANIMATED : false}
|
||||
shikiTheme={SHIKI_THEME}
|
||||
className="streamdown-agent min-w-0 max-w-full"
|
||||
components={components}
|
||||
>
|
||||
{content}
|
||||
</Streamdown>
|
||||
<MarkdownErrorBoundary content={content}>
|
||||
<Streamdown
|
||||
mode={isLive ? "streaming" : "static"}
|
||||
parseIncompleteMarkdown={isLive}
|
||||
isAnimating={isLive}
|
||||
animated={isLive ? STREAMDOWN_ANIMATED : false}
|
||||
shikiTheme={SHIKI_THEME}
|
||||
className="streamdown-agent min-w-0 max-w-full"
|
||||
components={components}
|
||||
>
|
||||
{content}
|
||||
</Streamdown>
|
||||
</MarkdownErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue