diff --git a/apps/web/src/components/files/FilePreviewPanel.test.ts b/apps/web/src/components/files/FilePreviewPanel.test.ts index d9361ba21244a..d689a393d8db2 100644 --- a/apps/web/src/components/files/FilePreviewPanel.test.ts +++ b/apps/web/src/components/files/FilePreviewPanel.test.ts @@ -296,7 +296,7 @@ Third}`; }, ], }); - // IPython traceback frames can omit trailing newlines or place ANSI resets after them. + // IPython traceback frames carry ANSI SGR colors and can omit trailing newlines. const failed = openNotebook( bytes( JSON.stringify({ @@ -304,7 +304,7 @@ Third}`; cells: [ { cell_type: "code", - source: "fail()", + source: 'print("\u001b[32m")\nfail()', outputs: [ { output_type: "error", @@ -330,7 +330,7 @@ Third}`; "\u001b[31mTraceback (most recent call last):\n\u001b[0m", "\u001b[32mCell In[3], line 1\n----> 1 fail()\n\u001b[0m\u001b[39m", "\u001b[31mValueError: preview proof\u001b[0m", - "final entry", + "final entry \\x1b[31m kept", ], }, ], @@ -340,6 +340,7 @@ Third}`; ), ); if (failed.type !== "notebook") throw new Error("Expected notebook"); + expect(failed.cells[0]!.source).toBe('print("\u001b[32m")\nfail()'); expect(failed.cells[0]!.outputs).toEqual([ { kind: "text", @@ -351,7 +352,7 @@ Third}`; }, { kind: "text", - text: "\u001b[31mTraceback (most recent call last):\n\u001b[0m\u001b[32mCell In[3], line 1\n----> 1 fail()\n\u001b[0m\u001b[39m\u001b[31mValueError: preview proof\u001b[0m\nfinal entry", + text: "Traceback (most recent call last):\nCell In[3], line 1\n----> 1 fail()\nValueError: preview proof\nfinal entry \\x1b[31m kept", }, ]); expect(() => openNotebook(bytes('{"nbformat":3,"cells":[]}'))).toThrow("version 4"); diff --git a/apps/web/src/components/files/extendedDocumentPreview.ts b/apps/web/src/components/files/extendedDocumentPreview.ts index 6b840a00543a6..384e3a3e78dac 100644 --- a/apps/web/src/components/files/extendedDocumentPreview.ts +++ b/apps/web/src/components/files/extendedDocumentPreview.ts @@ -431,12 +431,14 @@ function notebookText(value: unknown, limit = 256_000): string { if (result.length > limit) throw new Error("A notebook cell is too large to preview."); return result; } -// IPython traceback frames may omit a final newline or follow it with ANSI SGR sequences. +// IPython traceback frames carry ANSI SGR colors a
cannot render and may omit a final newline.
function tracebackLines(value: unknown): unknown {
if (!Array.isArray(value)) return value;
- const frames = value.filter((frame): frame is string => typeof frame === "string");
+ const frames = value
+ .filter((frame): frame is string => typeof frame === "string")
+ .map((frame) => frame.replaceAll(/\u001b\[[\d;:]*m/g, ""));
return frames.map((frame, index) =>
- index && !/\n(?:\u001b\[[\d;:]*m)*$/.test(frames[index - 1]!) ? `\n${frame}` : frame,
+ index && !frames[index - 1]!.endsWith("\n") ? `\n${frame}` : frame,
);
}
export function openNotebook(bytes: ArrayBuffer): OfficeWorkerResult {