Skip to content

Commit d0f1633

Browse files
authored
Add error boundary to notebook preview (#732)
1 parent e3126ce commit d0f1633

File tree

2 files changed

+48
-34
lines changed

2 files changed

+48
-34
lines changed

packages/components/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ export { groupConsecutiveStreamOutputs } from "./utils/output-grouping.js";
7676
export { OutputTypesDemoPage } from "./OutputTypesDemoPage.js";
7777
export { Incrementor } from "./Incrementor.js";
7878

79+
// Error boundary
80+
export { ErrorBoundary } from "react-error-boundary";
81+
7982
// Re-export types from schema for convenience
8083
export type {
8184
OutputData,

packages/notebook-preview/src/NotebookRenderer.tsx

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
SuspenseSpinner,
66
ExecutionCount,
77
SyntaxHighlighter,
8+
ErrorBoundary,
89
} from "@runtimed/components";
910

1011
// Jupyter notebook types
@@ -158,25 +159,31 @@ function CodeCell({ cell, language }: { cell: JupyterCell; language: string }) {
158159
<div className="min-w-0 flex-1">
159160
{/* Input */}
160161
<div className="overflow-hidden rounded border border-gray-200">
161-
<SyntaxHighlighter
162-
language={language}
163-
enableCopy={true}
164-
customStyle={{ fontSize: "0.8rem" }}
162+
<ErrorBoundary
163+
fallback={<div>Error rendering syntax highlighter</div>}
165164
>
166-
{source}
167-
</SyntaxHighlighter>
165+
<SyntaxHighlighter
166+
language={language}
167+
enableCopy={true}
168+
customStyle={{ fontSize: "0.8rem" }}
169+
>
170+
{source}
171+
</SyntaxHighlighter>
172+
</ErrorBoundary>
168173
</div>
169174

170175
{/* Outputs */}
171176
{outputs.length > 0 && (
172177
<div className="mt-2 rounded border border-gray-100 bg-white">
173-
<SuspenseSpinner>
174-
<OutputsContainer>
175-
{outputs.map((output) => (
176-
<SingleOutput key={output.id} output={output} />
177-
))}
178-
</OutputsContainer>
179-
</SuspenseSpinner>
178+
<ErrorBoundary fallback={<div>Error rendering outputs</div>}>
179+
<SuspenseSpinner>
180+
<OutputsContainer>
181+
{outputs.map((output) => (
182+
<SingleOutput key={output.id} output={output} />
183+
))}
184+
</OutputsContainer>
185+
</SuspenseSpinner>
186+
</ErrorBoundary>
180187
</div>
181188
)}
182189
</div>
@@ -210,9 +217,11 @@ function MarkdownCell({ cell }: { cell: JupyterCell }) {
210217
<div className="flex items-start gap-2">
211218
<div className="w-12 flex-shrink-0" />
212219
<div className="min-w-0 flex-1 p-3">
213-
<SuspenseSpinner>
214-
<SingleOutput output={markdownOutput} />
215-
</SuspenseSpinner>
220+
<ErrorBoundary fallback={<div>Error rendering markdown</div>}>
221+
<SuspenseSpinner>
222+
<SingleOutput output={markdownOutput} />
223+
</SuspenseSpinner>
224+
</ErrorBoundary>
216225
</div>
217226
</div>
218227
</div>
@@ -243,24 +252,26 @@ export function NotebookRenderer({ notebook }: { notebook: JupyterNotebook }) {
243252

244253
return (
245254
<div className="notebook-preview py-4 pr-4 pl-2">
246-
{notebook.cells.map((cell, index) => {
247-
switch (cell.cell_type) {
248-
case "code":
249-
return (
250-
<CodeCell
251-
key={cell.id || index}
252-
cell={cell}
253-
language={language}
254-
/>
255-
);
256-
case "markdown":
257-
return <MarkdownCell key={cell.id || index} cell={cell} />;
258-
case "raw":
259-
return <RawCell key={cell.id || index} cell={cell} />;
260-
default:
261-
return null;
262-
}
263-
})}
255+
<ErrorBoundary fallback={<div>Error rendering notebook</div>}>
256+
{notebook.cells.map((cell, index) => {
257+
switch (cell.cell_type) {
258+
case "code":
259+
return (
260+
<CodeCell
261+
key={cell.id || index}
262+
cell={cell}
263+
language={language}
264+
/>
265+
);
266+
case "markdown":
267+
return <MarkdownCell key={cell.id || index} cell={cell} />;
268+
case "raw":
269+
return <RawCell key={cell.id || index} cell={cell} />;
270+
default:
271+
return null;
272+
}
273+
})}
274+
</ErrorBoundary>
264275
</div>
265276
);
266277
}

0 commit comments

Comments
 (0)