Skip to content

Commit a3c491d

Browse files
authored
improvement: better tracebacks, padding, remove button for static files (#6912)
1 parent c21770b commit a3c491d

File tree

12 files changed

+206
-67
lines changed

12 files changed

+206
-67
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,6 @@ docs/_build/
7474
profile_default/
7575
ipython_config.py
7676

77-
# pyenv
78-
.python-version
79-
8077
# pipenv
8178
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
8279
# However, in case of collaboration, if having platform-specific dependencies or dependencies

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

frontend/src/components/editor/output/MarimoTracebackOutput.tsx

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type { CellId } from "@/core/cells/ids";
3030
import { insertDebuggerAtLine } from "@/core/codemirror/editing/debugging";
3131
import { aiEnabledAtom } from "@/core/config/config";
3232
import { getRequestClient } from "@/core/network/requests";
33+
import { isStaticNotebook } from "@/core/static/static-state";
3334
import { isWasm } from "@/core/wasm/utils";
3435
import { renderHTML } from "@/plugins/core/RenderHTML";
3536
import { copyToClipboard } from "@/utils/copy";
@@ -70,6 +71,17 @@ export const MarimoTracebackOutput = ({
7071
// Get last traceback info
7172
const tracebackInfo = extractAllTracebackInfo(traceback)?.at(0);
7273

74+
// Don't show in wasm or static notebooks
75+
const showDebugger =
76+
tracebackInfo &&
77+
tracebackInfo.kind === "cell" &&
78+
!isWasm() &&
79+
!isStaticNotebook();
80+
81+
const showAIFix = onRefactorWithAI && aiEnabled && !isStaticNotebook();
82+
83+
const showSearch = !isStaticNotebook();
84+
7385
const handleRefactorWithAI = (triggerImmediately: boolean) => {
7486
onRefactorWithAI?.({
7587
prompt: `My code gives the following error:\n\n${lastTracebackLine}`,
@@ -104,14 +116,14 @@ export const MarimoTracebackOutput = ({
104116
</AccordionItem>
105117
</Accordion>
106118
<div className="flex gap-2">
107-
{onRefactorWithAI && aiEnabled && (
119+
{showAIFix && (
108120
<AIFixButton
109121
tooltip="Fix with AI"
110122
openPrompt={() => handleRefactorWithAI(false)}
111123
applyAutofix={() => handleRefactorWithAI(true)}
112124
/>
113125
)}
114-
{tracebackInfo && tracebackInfo.kind === "cell" && !isWasm() && (
126+
{showDebugger && (
115127
<Tooltip content={"Attach pdb to the exception point."}>
116128
<Button
117129
size="xs"
@@ -125,50 +137,52 @@ export const MarimoTracebackOutput = ({
125137
</Button>
126138
</Tooltip>
127139
)}
128-
<DropdownMenu>
129-
<DropdownMenuTrigger asChild={true}>
130-
<Button size="xs" variant="text">
131-
Get help
132-
<ChevronDown className="h-3 w-3 ml-1" />
133-
</Button>
134-
</DropdownMenuTrigger>
135-
<DropdownMenuContent align="end" className="w-56">
136-
<DropdownMenuItem asChild={true}>
137-
<a
138-
target="_blank"
139-
href={`https://www.google.com/search?q=${encodeURIComponent(lastTracebackLine)}`}
140-
rel="noreferrer"
141-
>
142-
<SearchIcon className="h-4 w-4 mr-2" />
143-
Search on Google
144-
<ExternalLinkIcon className="h-3 w-3 ml-auto" />
145-
</a>
146-
</DropdownMenuItem>
147-
<DropdownMenuItem asChild={true}>
148-
<a
149-
target="_blank"
150-
href="https://marimo.io/discord?ref=notebook"
151-
rel="noopener"
140+
{showSearch && (
141+
<DropdownMenu>
142+
<DropdownMenuTrigger asChild={true}>
143+
<Button size="xs" variant="text">
144+
Get help
145+
<ChevronDown className="h-3 w-3 ml-1" />
146+
</Button>
147+
</DropdownMenuTrigger>
148+
<DropdownMenuContent align="end" className="w-56">
149+
<DropdownMenuItem asChild={true}>
150+
<a
151+
target="_blank"
152+
href={`https://www.google.com/search?q=${encodeURIComponent(lastTracebackLine)}`}
153+
rel="noreferrer"
154+
>
155+
<SearchIcon className="h-4 w-4 mr-2" />
156+
Search on Google
157+
<ExternalLinkIcon className="h-3 w-3 ml-auto" />
158+
</a>
159+
</DropdownMenuItem>
160+
<DropdownMenuItem asChild={true}>
161+
<a
162+
target="_blank"
163+
href="https://marimo.io/discord?ref=notebook"
164+
rel="noopener"
165+
>
166+
<MessageCircleIcon className="h-4 w-4 mr-2" />
167+
Ask in Discord
168+
<ExternalLinkIcon className="h-3 w-3 ml-auto" />
169+
</a>
170+
</DropdownMenuItem>
171+
<DropdownMenuItem
172+
onClick={() => {
173+
// Strip HTML from the traceback
174+
const div = document.createElement("div");
175+
div.innerHTML = traceback;
176+
const textContent = div.textContent || "";
177+
copyToClipboard(textContent);
178+
}}
152179
>
153-
<MessageCircleIcon className="h-4 w-4 mr-2" />
154-
Ask in Discord
155-
<ExternalLinkIcon className="h-3 w-3 ml-auto" />
156-
</a>
157-
</DropdownMenuItem>
158-
<DropdownMenuItem
159-
onClick={() => {
160-
// Strip HTML from the traceback
161-
const div = document.createElement("div");
162-
div.innerHTML = traceback;
163-
const textContent = div.textContent || "";
164-
copyToClipboard(textContent);
165-
}}
166-
>
167-
<CopyIcon className="h-4 w-4 mr-2" />
168-
Copy to clipboard
169-
</DropdownMenuItem>
170-
</DropdownMenuContent>
171-
</DropdownMenu>
180+
<CopyIcon className="h-4 w-4 mr-2" />
181+
Copy to clipboard
182+
</DropdownMenuItem>
183+
</DropdownMenuContent>
184+
</DropdownMenu>
185+
)}
172186
</div>
173187
</div>
174188
);

frontend/src/components/editor/renderers/vertical-layout/vertical-layout-wrapper.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ export const VerticalLayoutWrapper: React.FC<PropsWithChildren<Props>> = ({
2121
<div
2222
className={cn(
2323
"px-1 sm:px-16 md:px-20 xl:px-24 print:px-0 print:pb-0",
24+
// Large mobile bottom padding due to mobile browser navigation bar
25+
"pb-24 sm:pb-12",
2426
className,
2527
)}
2628
>
2729
<div
2830
className={cn(
29-
// Large mobile bottom padding due to mobile browser navigation bar
30-
"m-auto pb-24 sm:pb-12",
31+
"m-auto",
32+
// This padding needs to be the same from above to be correctly applied
33+
"pb-24 sm:pb-12",
3134
appConfig.width === "compact" &&
3235
"max-w-(--content-width) min-w-[400px]",
3336
appConfig.width === "medium" &&

frontend/src/core/cells/__tests__/session.test.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,18 @@ describe("notebookStateFromSession", () => {
210210

211211
it("handles console outputs in session cell", () => {
212212
const consoleOutputs = [
213-
{ type: "stream", name: "stdout", text: "Hello stdout" } as const,
214-
{ type: "stream", name: "stderr", text: "Hello stderr" } as const,
213+
{
214+
type: "stream",
215+
name: "stdout",
216+
text: "Hello stdout",
217+
mimetype: "text/plain",
218+
} as const,
219+
{
220+
type: "stream",
221+
name: "stderr",
222+
text: "Hello stderr",
223+
mimetype: "text/plain",
224+
} as const,
215225
];
216226
const session = createSession([
217227
createSessionCell("cell-1", [], consoleOutputs),
@@ -356,7 +366,14 @@ describe("notebookStateFromSession", () => {
356366
createSessionCell(
357367
"cell-1",
358368
[],
359-
[{ type: "stream", name: "stdout", text: "output" }],
369+
[
370+
{
371+
type: "stream",
372+
name: "stdout",
373+
text: "output",
374+
mimetype: "text/plain",
375+
},
376+
],
360377
),
361378
]);
362379
const notebook = createNotebook([

frontend/src/core/cells/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ function createCellRuntimeFromSession(
229229
return {
230230
channel: consoleOutput.name === "stderr" ? "stderr" : "stdout",
231231
data: consoleOutput.text,
232-
mimetype: "text/plain",
232+
mimetype: consoleOutput.mimetype ?? "text/plain",
233233
timestamp: DEFAULT_TIMESTAMP,
234234
};
235235
}),

marimo/_schemas/generated/session.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,31 @@ components:
126126
type: object
127127
StreamOutput:
128128
properties:
129+
mimetype:
130+
enum:
131+
- application/json
132+
- application/vnd.marimo+error
133+
- application/vnd.marimo+traceback
134+
- application/vnd.marimo+mimebundle
135+
- application/vnd.vega.v5+json
136+
- application/vnd.vegalite.v5+json
137+
- application/vnd.jupyter.widget-view+json
138+
- image/png
139+
- image/svg+xml
140+
- image/tiff
141+
- image/avif
142+
- image/bmp
143+
- image/gif
144+
- image/jpeg
145+
- video/mp4
146+
- video/mpeg
147+
- text/html
148+
- text/plain
149+
- text/markdown
150+
- text/latex
151+
- text/csv
152+
nullable: true
153+
type: string
129154
name:
130155
enum:
131156
- stdout
@@ -141,6 +166,7 @@ components:
141166
- type
142167
- name
143168
- text
169+
- mimetype
144170
type: object
145171
TimeMetadata:
146172
properties:

marimo/_schemas/session.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class StreamOutput(BaseDict):
3030
type: Literal["stream"]
3131
name: Literal["stdout", "stderr"]
3232
text: str
33+
mimetype: Optional[KnownMimeType]
3334

3435

3536
class StreamMediaOutput(BaseDict):

marimo/_server/session/serialize.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ def serialize_session_view(
151151
if console_out.channel == CellChannel.STDERR
152152
else "stdout",
153153
text=str(console_out.data),
154+
mimetype=console_out.mimetype,
154155
)
155156
)
156157

@@ -235,22 +236,33 @@ def deserialize_session(session: NotebookSessionV1) -> SessionView:
235236
else:
236237
is_stderr = console["name"] == "stderr"
237238
data = console["text"]
238-
# HACK: We need to detect tracebacks in stderr by checking for HTML
239-
# formatting.
240-
is_traceback = (
241-
is_stderr
242-
and isinstance(data, str)
243-
and data.startswith('<span class="codehilite">')
244-
)
239+
240+
# Use mimetype from console if available (new format)
241+
if "mimetype" in console and console["mimetype"] is not None:
242+
mimetype = console["mimetype"]
243+
else:
244+
# Backward compatibility: detect mimetype using heuristics
245+
# HACK: We need to detect tracebacks in stderr by checking for HTML
246+
# formatting.
247+
is_traceback = (
248+
is_stderr
249+
and isinstance(data, str)
250+
and data.startswith('<span class="codehilite">')
251+
)
252+
mimetype = cast(
253+
KnownMimeType,
254+
"application/vnd.marimo+traceback"
255+
if is_traceback
256+
else "text/plain",
257+
)
258+
245259
console_outputs.append(
246260
CellOutput(
247261
channel=CellChannel.STDERR
248262
if is_stderr
249263
else CellChannel.STDOUT,
250264
data=data,
251-
mimetype="application/vnd.marimo+traceback"
252-
if is_traceback
253-
else "text/plain",
265+
mimetype=mimetype,
254266
)
255267
)
256268

packages/openapi/src/session.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ export interface components {
7272
type: "streamMedia";
7373
};
7474
StreamOutput: {
75+
/** @enum {string|null} */
76+
mimetype:
77+
| "application/json"
78+
| "application/vnd.marimo+error"
79+
| "application/vnd.marimo+traceback"
80+
| "application/vnd.marimo+mimebundle"
81+
| "application/vnd.vega.v5+json"
82+
| "application/vnd.vegalite.v5+json"
83+
| "application/vnd.jupyter.widget-view+json"
84+
| "image/png"
85+
| "image/svg+xml"
86+
| "image/tiff"
87+
| "image/avif"
88+
| "image/bmp"
89+
| "image/gif"
90+
| "image/jpeg"
91+
| "video/mp4"
92+
| "video/mpeg"
93+
| "text/html"
94+
| "text/plain"
95+
| "text/markdown"
96+
| "text/latex"
97+
| "text/csv"
98+
| null;
7599
/** @enum {string} */
76100
name: "stdout" | "stderr";
77101
text: string;

0 commit comments

Comments
 (0)