Update src/components/ui/SectionErrorBoundary.tsx
This commit is contained in:
@@ -1,90 +0,0 @@
|
||||
import { Component, type ErrorInfo, type ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* Per-section error boundary inserted around every assembled section by the
|
||||
* backend's page-assembler. Goal: a single section that throws at runtime
|
||||
* (missing required prop, broken `.map`, etc.) shows a small placeholder
|
||||
* instead of taking down the entire page with a white screen.
|
||||
*
|
||||
* Also reports the failure via the `/__webild/render-status` probe channel
|
||||
* so Bob-AI's post-commit poll picks up the section name + error message and
|
||||
* the model gets the signal to fix the right section on the next loop turn.
|
||||
*
|
||||
* The probe POST is best-effort and silent — sandbox-only (gated by
|
||||
* `window.parent !== window`), so production deploys never call it.
|
||||
*/
|
||||
|
||||
interface Props {
|
||||
/** Section slug — same value the wrapping `<div data-section="…">` uses. */
|
||||
name: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
const RENDER_STATUS_URL = "/__webild/render-status";
|
||||
|
||||
export default class SectionErrorBoundary extends Component<Props, State> {
|
||||
state: State = { hasError: false };
|
||||
|
||||
static getDerivedStateFromError(error: unknown): State {
|
||||
return {
|
||||
hasError: true,
|
||||
errorMessage:
|
||||
error instanceof Error ? error.message : String(error ?? "unknown"),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, info: ErrorInfo) {
|
||||
if (typeof window === "undefined") return;
|
||||
if (window.parent === window) return;
|
||||
try {
|
||||
fetch(RENDER_STATUS_URL, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ok: false,
|
||||
reason: "section_error_boundary",
|
||||
section: this.props.name,
|
||||
error: String(error?.message || error || "unknown"),
|
||||
stack: String(error?.stack || "").slice(0, 4000),
|
||||
componentStack: String(info?.componentStack || "").slice(0, 4000),
|
||||
t: Date.now(),
|
||||
}),
|
||||
keepalive: true,
|
||||
}).catch(() => {});
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div
|
||||
aria-label="Section render error placeholder"
|
||||
className="w-content-width mx-auto my-8 p-6 card rounded text-center"
|
||||
>
|
||||
<p className="text-base font-medium text-foreground">
|
||||
This section failed to render.
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-foreground/60">
|
||||
Section: <code className="font-mono">{this.props.name}</code>
|
||||
</p>
|
||||
{this.state.errorMessage ? (
|
||||
<p className="mt-2 text-xs text-foreground/50 max-w-xl mx-auto break-words">
|
||||
{this.state.errorMessage}
|
||||
</p>
|
||||
) : null}
|
||||
<p className="mt-3 text-xs text-foreground/40">
|
||||
Tell Bob exactly what's wrong (e.g. "fix the {this.props.name} section") to retry.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user