import React from "react";
import ReactDOM from "react-dom/client";
import { invoke } from "@tauri-apps/api/core";
import App from "./App";
import "./styles.css";
const ISSUES_URL = "https://github.com/madhavajay/rho/issues/new";
async function reportFromCrash() {
// The crash itself is already auto-captured by Sentry (the boundary re-throws to
// the global handler). This adds a screenshot to the clipboard + opens an issue.
try {
const { default: html2canvas } = await import("html2canvas-pro");
const canvas = await html2canvas(document.body, {
backgroundColor: null,
logging: false,
scale: Math.min(window.devicePixelRatio || 1, 2)
});
const blob = await new Promise<Blob | null>((resolve) =>
canvas.toBlob((value) => resolve(value), "image/png")
);
if (blob && typeof ClipboardItem !== "undefined" && navigator.clipboard?.write) {
await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
}
} catch {
// capture is best-effort
}
// window.open is a no-op in the Tauri webview; use the native opener command.
void invoke("open_external_url", { url: ISSUES_URL }).catch(() => {});
}
type BoundaryState = { error: Error | null };
class ErrorBoundary extends React.Component<{ children: React.ReactNode }, BoundaryState> {
state: BoundaryState = { error: null };
static getDerivedStateFromError(error: Error): BoundaryState {
return { error };
}
componentDidCatch(error: Error) {
// React boundaries swallow the throw, so it never reaches window.onerror
// where the auto-injected Sentry browser handler lives. Re-throw on a
// macrotask so Sentry still captures the crash while we show a fallback.
window.setTimeout(() => {
throw error;
}, 0);
}
render() {
if (this.state.error) {
return (
<main className="app-shell desktop-wallpaper">
<section className="crash-screen">
<h1>Something went wrong</h1>
<p>
The app hit an unexpected error and it was reported to Sentry automatically. You can
also open a GitHub issue (a screenshot is copied to your clipboard to paste in).
</p>
<pre>{this.state.error.message}</pre>
<div className="crash-actions">
<button
type="button"
className="primary-action"
onClick={() => window.location.reload()}
>
Reload app
</button>
<button type="button" onClick={() => void reportFromCrash()}>
Report this
</button>
</div>
</section>
</main>
);
}
return this.props.children;
}
}
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</React.StrictMode>
);