From 43cf62a93c5531fcb8c7119f5fb6412228e2c336 Mon Sep 17 00:00:00 2001 From: Will Chen Date: Mon, 5 May 2025 14:19:33 -0700 Subject: [PATCH] Enable exception reporting & default error screen to report GitHub bug (#82) --- src/components/ErrorBoundary.tsx | 119 +++++++++++++++++++++++++++++++ src/components/HelpDialog.tsx | 1 + src/renderer.tsx | 1 + src/router.ts | 2 + 4 files changed, 123 insertions(+) create mode 100644 src/components/ErrorBoundary.tsx diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..97c5c23 --- /dev/null +++ b/src/components/ErrorBoundary.tsx @@ -0,0 +1,119 @@ +import React, { + Component, + ErrorInfo, + ReactNode, + useState, + useEffect, +} from "react"; +import { Button } from "@/components/ui/button"; +import { LightbulbIcon } from "lucide-react"; +import { ErrorComponentProps } from "@tanstack/react-router"; +import { usePostHog } from "posthog-js/react"; +import { IpcClient } from "@/ipc/ipc_client"; + +export function ErrorBoundary({ error }: ErrorComponentProps) { + const [isLoading, setIsLoading] = useState(false); + const posthog = usePostHog(); + + useEffect(() => { + console.error("An error occurred in the route:", error); + posthog.captureException(error); + }, [error]); + + const handleReportBug = async () => { + setIsLoading(true); + try { + // Get system debug info + const debugInfo = await IpcClient.getInstance().getSystemDebugInfo(); + + // Create a formatted issue body with the debug info and error information + const issueBody = ` +## Bug Description + + +## Steps to Reproduce + + +## Expected Behavior + + +## Actual Behavior + + +## Error Details +- Error Name: ${error?.name || "Unknown"} +- Error Message: ${error?.message || "Unknown"} +${error?.stack ? `\n\`\`\`\n${error.stack.slice(0, 1000)}\n\`\`\`` : ""} + +## System Information +- Dyad Version: ${debugInfo.dyadVersion} +- Platform: ${debugInfo.platform} +- Architecture: ${debugInfo.architecture} +- Node Version: ${debugInfo.nodeVersion || "Not available"} +- PNPM Version: ${debugInfo.pnpmVersion || "Not available"} +- Node Path: ${debugInfo.nodePath || "Not available"} +- Telemetry ID: ${debugInfo.telemetryId || "Not available"} + +## Logs +\`\`\` +${debugInfo.logs.slice(-3_500) || "No logs available"} +\`\`\` +`; + + // Create the GitHub issue URL with the pre-filled body + const encodedBody = encodeURIComponent(issueBody); + const encodedTitle = encodeURIComponent( + "[bug] Error in Dyad application" + ); + const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=bug,filed-from-app,client-error&body=${encodedBody}`; + + // Open the pre-filled GitHub issue page + await IpcClient.getInstance().openExternalUrl(githubIssueUrl); + } catch (err) { + console.error("Failed to prepare bug report:", err); + // Fallback to opening the regular GitHub issue page + IpcClient.getInstance().openExternalUrl( + "https://github.com/dyad-sh/dyad/issues/new" + ); + } finally { + setIsLoading(false); + } + }; + + return ( +
+
+

+ Sorry, that shouldn't have happened! +

+ +

There was an error loading the app...

+ + {error && ( +
+

+ Error name: {error.name} +

+

+ Error message: {error.message} +

+
+ )} + +
+ +
+ +
+ +

+ Tip: Try closing and re-opening Dyad as a temporary + workaround. +

+
+
+
+ ); +} diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx index aa4f31a..435ceb7 100644 --- a/src/components/HelpDialog.tsx +++ b/src/components/HelpDialog.tsx @@ -86,6 +86,7 @@ export function HelpDialog({ isOpen, onClose }: HelpDialogProps) { - Node Version: ${debugInfo.nodeVersion || "Not available"} - PNPM Version: ${debugInfo.pnpmVersion || "Not available"} - Node Path: ${debugInfo.nodePath || "Not available"} +- Telemetry ID: ${debugInfo.telemetryId || "Not available"} ## Logs \`\`\` diff --git a/src/renderer.tsx b/src/renderer.tsx index fdba145..ce73096 100644 --- a/src/renderer.tsx +++ b/src/renderer.tsx @@ -16,6 +16,7 @@ const posthogClient = posthog.init( // @ts-ignore debug: import.meta.env.MODE === "development", autocapture: false, + capture_exceptions: true, capture_pageview: false, before_send: (event) => { if (!isTelemetryOptedIn()) { diff --git a/src/router.ts b/src/router.ts index 7cb1f50..0825923 100644 --- a/src/router.ts +++ b/src/router.ts @@ -16,6 +16,7 @@ const routeTree = rootRoute.addChildren([ // src/components/NotFoundRedirect.tsx import * as React from "react"; import { useNavigate } from "@tanstack/react-router"; +import { ErrorBoundary } from "./components/ErrorBoundary"; export function NotFoundRedirect() { const navigate = useNavigate(); @@ -35,6 +36,7 @@ export function NotFoundRedirect() { export const router = createRouter({ routeTree, defaultNotFoundComponent: NotFoundRedirect, + defaultErrorComponent: ErrorBoundary, }); declare module "@tanstack/react-router" {