Improve preview iframe error handling: show new errors after dismiss & UX readability

This commit is contained in:
Will Chen
2025-04-15 21:27:20 -07:00
parent 041d3614e8
commit a6981a061c
2 changed files with 19 additions and 15 deletions

View File

@@ -35,7 +35,7 @@ import { showError } from "@/lib/toast";
import { SandboxConfig } from "@/ipc/ipc_types"; import { SandboxConfig } from "@/ipc/ipc_types";
interface ErrorBannerProps { interface ErrorBannerProps {
error: string | null; error: string | undefined;
onDismiss: () => void; onDismiss: () => void;
onAIFix: () => void; onAIFix: () => void;
} }
@@ -44,11 +44,11 @@ const ErrorBanner = ({ error, onDismiss, onAIFix }: ErrorBannerProps) => {
if (!error) return null; if (!error) return null;
return ( return (
<div className="absolute top-2 left-2 right-2 z-10 bg-red-50 dark:bg-red-950/50 border border-red-200 dark:border-red-800 rounded-md shadow-sm p-2"> <div className="absolute top-2 left-2 right-2 z-10 bg-red-50 dark:bg-red-950 border border-red-200 dark:border-red-800 rounded-md shadow-sm p-2">
{/* Close button in top left */} {/* Close button in top left */}
<button <button
onClick={onDismiss} onClick={onDismiss}
className="absolute top-1 left-1 p-1 hover:bg-red-100 dark:hover:bg-red-900/50 rounded" className="absolute top-1 left-1 p-1 hover:bg-red-100 dark:hover:bg-red-900 rounded"
> >
<X size={14} className="text-red-500 dark:text-red-400" /> <X size={14} className="text-red-500 dark:text-red-400" />
</button> </button>
@@ -60,11 +60,11 @@ const ErrorBanner = ({ error, onDismiss, onAIFix }: ErrorBannerProps) => {
{/* Tip message */} {/* Tip message */}
<div className="mt-2 px-6"> <div className="mt-2 px-6">
<div className="relative p-2 bg-red-100 dark:bg-red-900/50 rounded-sm flex gap-1 items-center"> <div className="relative p-2 bg-red-100 dark:bg-red-900 rounded-sm flex gap-1 items-center">
<div> <div>
<Lightbulb size={16} className=" text-red-800 dark:text-red-300" /> <Lightbulb size={16} className=" text-red-800 dark:text-red-300" />
</div> </div>
<span className="text-sm text-red-700 dark:text-red-400"> <span className="text-sm text-red-700 dark:text-red-200">
<span className="font-medium">Tip: </span>Check if refreshing the <span className="font-medium">Tip: </span>Check if refreshing the
page or restarting the app fixes the error. page or restarting the app fixes the error.
</span> </span>
@@ -88,10 +88,10 @@ const ErrorBanner = ({ error, onDismiss, onAIFix }: ErrorBannerProps) => {
// Preview iframe component // Preview iframe component
export const PreviewIframe = ({ export const PreviewIframe = ({
loading, loading,
error, loadingErrorMessage,
}: { }: {
loading: boolean; loading: boolean;
error: Error | null; loadingErrorMessage: string | undefined;
}) => { }) => {
const selectedAppId = useAtomValue(selectedAppIdAtom); const selectedAppId = useAtomValue(selectedAppIdAtom);
const { appUrl } = useAtomValue(appUrlAtom); const { appUrl } = useAtomValue(appUrlAtom);
@@ -100,8 +100,9 @@ export const PreviewIframe = ({
// State to trigger iframe reload // State to trigger iframe reload
const [reloadKey, setReloadKey] = useState(0); const [reloadKey, setReloadKey] = useState(0);
const [iframeError, setIframeError] = useState<string | null>(null); const [errorMessage, setErrorMessage] = useState<string | undefined>(
const [showError, setShowError] = useState(true); loadingErrorMessage
);
const setInputValue = useSetAtom(chatInputValueAtom); const setInputValue = useSetAtom(chatInputValueAtom);
const [availableRoutes, setAvailableRoutes] = useState< const [availableRoutes, setAvailableRoutes] = useState<
Array<{ path: string; label: string }> Array<{ path: string; label: string }>
@@ -171,7 +172,7 @@ export const PreviewIframe = ({
if (type === "window-error") { if (type === "window-error") {
const errorMessage = `Error in ${payload.filename} (line ${payload.lineno}, col ${payload.colno}): ${payload.message}`; const errorMessage = `Error in ${payload.filename} (line ${payload.lineno}, col ${payload.colno}): ${payload.message}`;
console.error("Iframe error:", errorMessage); console.error("Iframe error:", errorMessage);
setIframeError(errorMessage); setErrorMessage(errorMessage);
setAppOutput((prev) => [ setAppOutput((prev) => [
...prev, ...prev,
{ {
@@ -183,7 +184,7 @@ export const PreviewIframe = ({
} else if (type === "unhandled-rejection") { } else if (type === "unhandled-rejection") {
const errorMessage = `Unhandled Promise Rejection: ${payload.reason}`; const errorMessage = `Unhandled Promise Rejection: ${payload.reason}`;
console.error("Iframe unhandled rejection:", errorMessage); console.error("Iframe unhandled rejection:", errorMessage);
setIframeError(errorMessage); setErrorMessage(errorMessage);
setAppOutput((prev) => [ setAppOutput((prev) => [
...prev, ...prev,
{ {
@@ -412,10 +413,10 @@ export const PreviewIframe = ({
<div className="relative flex-grow "> <div className="relative flex-grow ">
<ErrorBanner <ErrorBanner
error={showError ? error?.message || iframeError : null} error={errorMessage}
onDismiss={() => setShowError(false)} onDismiss={() => setErrorMessage(undefined)}
onAIFix={() => { onAIFix={() => {
setInputValue(`Fix the error in ${error?.message || iframeError}`); setInputValue(`Fix the error in ${errorMessage}`);
}} }}
/> />

View File

@@ -162,7 +162,10 @@ export function PreviewPanel() {
<Panel id="content" minSize={30}> <Panel id="content" minSize={30}>
<div className="h-full overflow-y-auto"> <div className="h-full overflow-y-auto">
{previewMode === "preview" ? ( {previewMode === "preview" ? (
<PreviewIframe loading={loading} error={error} /> <PreviewIframe
loading={loading}
loadingErrorMessage={error?.message}
/>
) : ( ) : (
<CodeView loading={loading} error={error} app={app} /> <CodeView loading={loading} error={error} app={app} />
)} )}