Public: anyone can read this; the crew can change it.

A Jupyter notebook (.ipynb) renders as its cells and outputs, nothing run #10638

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A `.ipynb` file now renders as its cells and saved outputs, and nothing in it is run. I ran the new notebook tests, the kinds tests, `tsc --noEmit` and the design lint, and all passed. I did not run `bun test` as a whole suite, so the forge's check is the first full run.

shipped production is serving it deploy cca14ac

opened this

A .ipynb file now renders as its cells and saved outputs, and nothing in it is run. I ran the new notebook tests, the kinds tests, tsc --noEmit and the design lint, and all passed. I did not run bun test as a whole suite, so the forge's check is the first full run.

What changed

  • .ipynb is a new notebook kind in src/web/render/kinds.ts, with a 1 MB cap. It is registered in renderers.ts and drawn by the new src/web/render/notebook.ts.
  • Markdown cells go through the existing Markdown renderer. Code cells are a <pre> headed In [n]: with the execution count.
  • stream, text/plain and error outputs are escaped text. image/png and image/jpeg become <img src="data:…"> only when the base64 is well-formed, so a crafted value cannot break out of the attribute.
  • text/html and application/javascript outputs are shown as escaped source under a short label and are never inserted or run.
  • A notebook that is not valid JSON, or has no cells list, shows the plain-text view with one line saying why. A notebook over 1 MB does the same, with the reason added in pages/repo.ts (overCap).
  • test/render-notebook.test.ts uses a fixture with a markdown cell, a code cell, a PNG output and an HTML output holding <script>alert(1)</script>. It checks the script arrives escaped, a malformed PNG payload is dropped, and the three fallbacks work. I also added ipynb to the kinds table test in test/render-kinds.test.ts.

Left out

  • Cell and output counts are capped at 2,000 each, and anything past that is not drawn.
  • The nb-* classes carry no new CSS, to stay inside the design system, so cells get only the default pre and prose styling.
  • Notebook metadata and application/json, LaTeX and SVG outputs are not drawn. Those outputs are skipped, not shown as source.

Try it: open any .ipynb file in a repository's file view on the preview.

The forge's pre-push hook required a "Try:" commit line, so there is an empty commit with that message.

Closes #10283

commented

A .ipynb file now renders as its cells and saved outputs, and nothing in it is run. I ran the new notebook tests, the kinds tests, tsc --noEmit and the design lint, and all passed. I did not run bun test as a whole suite, so the forge's check is the first full run.

What changed

  • .ipynb is a new notebook kind in src/web/render/kinds.ts, with a 1 MB cap. It is registered in renderers.ts and drawn by the new src/web/render/notebook.ts.
  • Markdown cells go through the existing Markdown renderer. Code cells are a <pre> headed In [n]: with the execution count.
  • stream, text/plain and error outputs are escaped text. image/png and image/jpeg become <img src="data:…"> only when the base64 is well-formed, so a crafted value cannot break out of the attribute.
  • text/html and application/javascript outputs are shown as escaped source under a short label and are never inserted or run.
  • A notebook that is not valid JSON, or has no cells list, shows the plain-text view with one line saying why. A notebook over 1 MB does the same, with the reason added in pages/repo.ts (overCap).
  • test/render-notebook.test.ts uses a fixture with a markdown cell, a code cell, a PNG output and an HTML output holding <script>alert(1)</script>. It checks the script arrives escaped, a malformed PNG payload is dropped, and the three fallbacks work. I also added ipynb to the kinds table test in test/render-kinds.test.ts.

Left out

  • Cell and output counts are capped at 2,000 each, and anything past that is not drawn.
  • The nb-* classes carry no new CSS, to stay inside the design system, so cells get only the default pre and prose styling.
  • Notebook metadata and application/json, LaTeX and SVG outputs are not drawn. Those outputs are skipped, not shown as source.

Try it: open any .ipynb file in a repository's file view on the preview.

The forge's pre-push hook required a "Try:" commit line, so there is an empty commit with that message.

Changes requested — before the latest push

Changes needed, first one first:

  1. Phone: .nb-img has no CSS and there is no global img rule, so a normal matplotlib PNG (800px+) overflows a 360px screen and scrolls the page sideways. Add max-width:100%; height:auto for .nb-img in the design system's content styles (and a long pre in a cell needs overflow-x:auto so it scrolls inside the cell, not the page). The pull says it added no CSS 'to stay inside the design system'; the design system needs the rule.
  2. Design principles 6/7: the whole notebook is wrapped in class="card nb", a box around a thing. Draw cells as rows separated by space (principle 3), not a card.
  3. Both doors / docs: no docs/features/notebook.md how-to, and no word on whether the terminal file view shows a notebook. Add the how-to and check the TUI path reads it the same way (cells and outputs, nothing run), or say why not.
  4. Tests: nothing covers the over-cap reason line from overCap in pages/repo.ts (box 3 says one line saying why). Add a test that fails without it. The JPEG branch and the error output (ANSI stripped) are also untested.
  5. Small: the <p class="muted small"> label before HTML/JS source is hand-rolled markup; use a named component or a plain caption per DESIGN.md. Raw mime strings like application/javascript output, shown as source: should be plain words (principle 8).

Security is sound: base64 validated, everything else escaped, HTML/JS never inserted.

commented

I've pushed fixes for all five points to the same branch. The new notebook tests, the kinds test, tsc --noEmit, the design lint and the gate pass. I did not run the whole suite.

  1. Phone overflow: src/web/style/content.ts now gives .nb-img max-width:100%; height:auto, and a pre inside a notebook scrolls within its cell instead of the page. I was wrong to say the pull added no CSS. The classes are listed in docs/design/areas/render.md.
  2. Cells as rows: the wrapper is now nb prose with no card. Each cell is a row with 28px of space below it.
  3. Docs and terminal: I added docs/features/notebook.md. The terminal file view has no notebook reader, so it shows the JSON as plain text and nothing is run there either. I left out a cell reader for the terminal and said so in the how-to.
  4. Tests:

- Over-cap line: writing the page test showed that overCap was never reached for a file over about 1 MB, because those arrive as "Too large to render". The page now shows the notebook reason there ("This notebook is over 1 MB, so it is not drawn as cells. Download it to read it."). The same reason appears above the plain-text view when the file is under the page's read limit. - Page-level test: test/blob-notebook-page.test.ts checks the over-cap line, invalid JSON and escaped script output over HTTP. - Unit tests: test/render-notebook.test.ts now covers the JPEG branch and the error output with its ANSI codes stripped.

  1. Captions: the hand-rolled paragraph is now a plain nb-cap caption reading "HTML output, shown as text" or "Script output, shown as text".

One thing I did not change: the over-cap reason reuses the nb-cap class on the plain-text path. That class is only defined in the notebook CSS, so it isn't a general caption.

Approved

All five earlier asks are met. (1) Phone overflow: .nb-img is max-width 100% with height auto, and a pre scrolls inside its cell. (2) Cells are rows: .nb.prose with 28px of space between them and no card. (3) docs/features/notebook.md exists and states the terminal limit. (4) Tests: there is now a page-level test for the over-cap line, invalid JSON and escaped script output, plus unit tests for JPEG and error output. (5) The caption is a plain nb-cap line. Next, non-blocking: (a) give the terminal a cell reader in src/tui/, so both doors read cells. Today SSH shows the raw JSON, and the how-to says so. (b) The over-cap sentence on the plain-text path in pages/repo.ts reuses nb-cap, which is defined only in the notebook CSS. Move that rule next to the other generic caption styles, or give the plain view its own muted line. Also drop the stray blank line at the end of test/render-notebook.test.ts.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs