Skip to content

Lecture-level live compute: per-lecture WASM/JupyterLite enablement (page metadata + compatibility testing) #114

Description

@mmcky

Motivation

In-page live compute (#98) is opt-in per project via project.thebe, with JupyterLite/Pyodide as the QuantEcon default. But WASM is an optional run environment that won't be available to every lecture series — Pyodide runs pure Python plus packages compiled for it (numpy, scipy, pandas, matplotlib, sympy), while numba and JAX won't import (see the README "Live compute" caveat). Even within a compatible series, individual lectures may not run.

Goal: the live-compute Power button appears only on lectures that are certified compatible; incompatible lectures in the same project don't show it. That needs per-lecture metadata, a theme-side gate, and a testing story that certifies compatibility before the yaml entry is added.

What the MyST schema supports today (investigated 2026-08-06)

Page-level thebe: frontmatter is not supported: in myst-frontmatter v1.9.5, thebe appears in PROJECT_FRONTMATTER_KEYS only ("These keys only exist on the project", dist/project/types.js:30-39), it's absent from PAGE_FRONTMATTER_KEYS, and validatePageFrontmatter silently drops a page-level thebe: key with a warning. So per-lecture enablement can't be expressed through the standard thebe config, and shouldn't wait on an upstream schema change.

What does pass through per page is the site: frontmatter object — validated only as "an object" with arbitrary keys preserved (page/validators.js:70-74), and already used by the theme for per-page options and (in #83) the git_metadata override. That's the natural carrier.

Proposed design

1. Per-lecture metadata — one yaml entry in each certified lecture's frontmatter:

site:
  live_compute: true

project.thebe stays as-is (it carries the kernel configuration); the page flag gates whether this lecture surfaces the control.

2. Theme gate — two pattern-consistent implementation points exist in the current code (feat/thebe-live-compute branch):

Option Where Effect
Narrow Extend the render condition in PageContent.tsx (compute?.enabled && features.notebookCompute && article.kind === Notebook && <ComputeToolbarSlot/>) with the page flag, and mirror it on ExecuteScopeProvider's enable prop Hides the toolbar button and disables the execution machinery for that page; the empty:hidden header slot collapses cleanly
Broad Pass a page-frontmatter-derived optionOverrideFn to ComputeOptionsProvider in Page.tsx — a shipped-but-unused escape hatch (`(opts) => ExtendedCoreOptions undefined); returning undefinedflipscompute.enabled` wholesale

Either way the new key should be documented in app/types.ts TemplateOptions alongside the launch_* keys.

3. Compatibility testing to stamp the flag — the metadata should come from testing, not guesswork. Sketch: a job in the lecture repo executes each lecture's cells under the Pyodide kernel and records pass/fail per lecture; the report drives which lectures get the yaml entry. This can start manual (run the lecture in the browser, record the result) and be automated later (Pyodide under Node, or headless jupyterlite). Certification needs re-running when a lecture's package usage changes.

Open questions

  • Default when the flag is absent. Absent = today's behaviour (button shows wherever project.thebe is set) is the least surprising and lets series adopt incrementally by adding live_compute: false to known-bad lectures; the inverse (default off, opt-in per lecture) is safer for the certification story but makes enabling a series a many-file change.
  • Narrow vs broad gate — should the flag hide only the toolbar control, or also figure compute and the execution scope? Broad is probably what "not compatible" means.
  • Upstream — worth proposing page-level thebe to jupyter-book/mystmd as the first-class version of this; the site: passthrough works today without waiting on it.

References

Related decision, same investigation: relocating the Power toggle into the launch dropdown was assessed and rejected for the control itself (Radix unmounts popover content on close, so a live kernel's status/controls would be invisible whenever the popover is shut; the providers also sit outside the toolbar tree). A trigger-only "Run in browser" entry in the launch menu that boots the kernel via an event bridge — with controls staying in the header — remains feasible as a follow-up if discoverability warrants it; details on #98.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions