feat(thebe): enable in-page live compute via JupyterLite (Phase 2) - #98
feat(thebe): enable in-page live compute via JupyterLite (Phase 2)#98mmcky wants to merge 13 commits into
Conversation
Enable opt-in in-page execution via the standard MyST `thebe` frontmatter.
The infra was already wired (ComputeOptionsProvider/ThebeLoaderAndServer in
Page.tsx; ExecuteScopeProvider/NotebookToolbar in PageContent.tsx) and gated
on `project.thebe`; setting it surfaces the @myst-theme/jupyter NotebookToolbar
(Power -> Run/Restart/Clear once a kernel connects) on notebook pages, which is
the per-notebook live-compute toggle — no extra top-bar control, consistent
with the framework. Enablement is config-derived
(`compute.enabled = !!thebeFrontmatterToOptions(project.thebe)`).
QuantEcon default is JupyterLite (`thebe: { lite: true }`): Python runs in the
browser via Pyodide — no server/Binder (avoids the flaky Binder, #26). Verified
end-to-end: clicking Power boots the Pyodide kernel in-browser.
- fixture: set `project.thebe: { lite: true }` so the toolbar is exercised.
- test: assert the live-compute Power toggle renders on /notebook; refresh the
notebook.png + launch-open.png darwin snapshots (the toolbar now renders on
the notebook page and behind the launch popover).
- docs: README "Live compute" section incl. the Pyodide package caveat.
Completes Phase 2 (launch parity). Part of #88.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/update-snapshots Refreshing the linux Chromium baselines for the new live-compute toolbar (notebook.png) and the launch popover backdrop (launch-open.png). Darwin baselines are already updated in db64a28. |
|
|
🎭 Refreshed visual baselines in 22e3a63:
|
🎭 Visual regression resultsDetails
Skipped testsmobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › launch-colab |
… trigger CI) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Enables opt-in in-page live compute in the QuantEcon MyST theme by exercising the existing Thebe/Jupyter integration via project.thebe configuration (defaulting the visual fixture to JupyterLite), plus adds a lightweight UI assertion and documentation updates to reflect Phase 2 completion.
Changes:
- Update the visual fixture to set
project.thebe: { lite: true }(JupyterLite/Pyodide). - Add a Playwright assertion that the live-compute “Power” toggle renders on
/notebook. - Document the feature in
README.mdand mark Phase 2 complete inPLAN.md.
Reviewed changes
Copilot reviewed 4 out of 10 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
tests/visual/theme.spec.ts |
Adds a new live-compute-toggle test asserting the compute toggle UI is visible. |
tests/visual/fixture/myst.yml.in |
Enables Thebe/JupyterLite in the visual fixture via project.thebe.lite. |
README.md |
Documents how to enable live compute (Thebe/JupyterLite) and notes Pyodide limitations. |
PLAN.md |
Updates Phase 2 status/description to reflect Thebe live compute completion and BinderHub being dropped. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…opilot #98) - README/PLAN: reword "thebe frontmatter" -> `project.thebe` in `myst.yml` (project-level config read from the project manifest, not per-page frontmatter). - test: match the live-compute toggle by /start compute/i regex, resilient to upstream label wording/casing changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The native @myst-theme/jupyter NotebookToolbar renders as a right-aligned floating sticky pill, which looked orphaned on the notebook page. Wrap it in a labelled "JupyterLite · live compute" bar at the top of the article and neutralise the floating/sticky/right-aligned layout in app.css, so the Power toggle (and Run/Restart/Clear once connected) group together as one intentional control. - PageContent.tsx: labelled bar wrapper around NotebookToolbar. - styles/app.css: neutralise `.myst-jp-nb-toolbar` float/sticky/justify-end inside `.qe-compute-bar`. - refresh notebook.png + launch-open.png darwin snapshots. - README/PLAN: describe the labelled bar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/update-snapshots Relocated the live-compute controls into a labelled "JupyterLite · live compute" bar (was a floating right-aligned pill), so refreshing the linux notebook.png + launch-open.png baselines. Darwin baselines updated in a646e73. |
|
🎭 Refreshed visual baselines in 0b6c752:
|
… trigger CI) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per design feedback, the live-compute control now lives in the fixed header toolbar (next to Launch) instead of a floating pill / in-article bar, so it is always visible while scrolling and clusters with the other run controls. The @myst-theme/jupyter NotebookToolbar is article-coupled (its Power button calls both useThebeServer.connect() and useExecutionScope.start(slug)), and the header is mounted outside the Thebe providers — so a naive header button can't reach the connect hooks. Solved with a React portal: ComputeToolbarSlot (rendered from PageContent, inside the providers) portals the toolbar's DOM into a #qe-compute-slot in Toolbar.tsx. React context flows through the React tree, so its hooks/connection keep working without lifting the provider tree. - ComputeToolbarSlot.tsx: client-only createPortal of NotebookToolbar. - Toolbar.tsx: empty:hidden #qe-compute-slot <li> next to Launch. - styles/app.css: neutralise the floating pill, match 20px header icons. - desktop-only (like Launch); live-compute-toggle test skips mobile-chrome. - refresh notebook.png + launch-open.png darwin snapshots; README/PLAN updated. Verified end-to-end: Power in the header boots the Pyodide kernel; Run/Restart/ Clear then appear inline in the header. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/update-snapshots Moved the live-compute toggle into the header toolbar (next to Launch) via a portal, refreshing the linux notebook.png + launch-open.png baselines. Darwin baselines updated in 1f5fcfe. |
|
🎭 Refreshed visual baselines in 0037be9:
|
… trigger CI) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a second, minimal visual fixture (tests/visual/fixture-no-thebe/) that does NOT set `project.thebe`, served on a second port, and assert that the live-compute toggle is absent on a real notebook page there. This covers the `!compute.enabled` branch of the gate — the toggle only appears when a project opts into Thebe, not merely because a page is a notebook. - serve.sh: FIXTURE_DIR env selects the fixture project (default `fixture`). - playwright.config.ts: second webServer (NO_THEBE_PORT, default 3112). - theme.spec.ts: live-compute-toggle-absent-without-thebe (desktop-only), with a "Notebook outputs" sanity check so count-0 can't false-pass. - DOM-count assertion, so no new snapshots. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@DrDrij this is plugging in a power button into the title bar when
The power button enables |
# Conflicts: # tests/visual/fixture/myst.yml.in
|
@DrDrij friendly nudge on this one — it's the last thing holding up Phase 2. The question from my comment above: when Just after your read on the placement. If it looks right I'll merge as-is; if you'd rather it sat somewhere else, happy to move it. For context, the PR is rebased on current |
|
Some context for the placement sign-off: the question of whether this toggle could live inside the launch dropdown instead of the header came up again, so we re-verified the June judgement against the actual code (component tree, the compiled Why the control can't live in the dropdown:
What would be feasible later, if discoverability turns out to be a problem: a trigger-only "Run in browser (JupyterLite)" entry in the launch menu that fires an event to a small booter component inside the providers, with the spinner and Run/Restart/Clear still appearing in the header slot. All the required hooks are exported, so it's moderate work rather than a rebuild — but it adds coordination machinery (idempotency guard, separate enablement plumbing, splitting the panel's So the header placement stands as the one home that keeps a live kernel's status permanently visible. Related follow-up: #114 tracks per-lecture enablement (the Power button only on lectures certified Pyodide-compatible) — page-level |
…t iterations
A pre-merge audit (three lenses over the diff vs main, findings
adversarially verified) confirmed the code carries no residue from the two
abandoned placements — the app.css block maps 1:1 to shipped upstream
classes, no orphan ids/selectors — but surfaced tidy items around the edges:
- CHANGELOG: the feature had NO [Unreleased] entry, and releases build
their notes from this file. Added, describing the final header-portal
design.
- theme.spec.ts: the presence test asserted the toggle existed *anywhere*,
which couldn't distinguish the final header placement from the earlier
in-article iterations — now scoped to #qe-compute-slot. Fixed the skip
reason copied from launch-colab ("mobile uses MobileActionsMenu" — the
mobile menu has no compute entry; the toggle is desktop-only).
- playwright.config.ts: the no-thebe port default (3112) was duplicated in
the config and the spec, kept in sync only by convention — the config now
writes the resolved value back to process.env as the single source of
truth. Also guard PORT === NO_THEBE_PORT: with reuseExistingServer, the
second webServer entry would silently reuse the thebe-enabled server and
the absent-toggle test would assert against the wrong fixture.
- PLAN.md: repaired a comma-spliced, unwrapped line left where the three
placement rewrites collided.
- fixture-no-thebe/myst.yml.in: don't propagate the retired
quantecon-theme-src name into a new file.
No rendering changes; snapshots untouched. Typecheck, full visual suite
(15) and FOUC guard (2) pass locally.

Summary
Enables opt-in in-page live compute via the standard MyST
thebefrontmatter, completing Phase 2 (launch parity). The execution infra was already wired in the theme (ComputeOptionsProvider+ThebeLoaderAndServerinPage.tsx;ExecuteScopeProvider+NotebookToolbarinPageContent.tsx) and gated behindproject.thebe— so this turns it on, sets the QuantEcon default, relocates the control into the header, verifies it end-to-end, and documents it.Final design: the header Power toggle
Setting
project.thebesurfaces a Power toggle in the QuantEcon header toolbar next to Launch on notebook pages (desktop). Clicking it boots the kernel; Run / Restart / Clear then take its place and cells execute live. The control is the@myst-theme/jupyterNotebookToolbar, relocated via a React portal:ComputeToolbarSlot.tsx(rendered fromPageContent, inside the Thebe providers) portals it into#qe-compute-slot, anempty:hidden<li>inToolbar.tsx. The portal keeps the component inside the providers — React context flows through the React tree, not the DOM — even though the header mounts outside them, so no provider-tree lift is needed.styles/app.cssneutralises the toolbar's default floating-pill styling and matches the 20px header icon size.The placement went through review iterations on this thread (default floating pill → labelled in-article bar → header toolbar); the header is final. Why the control can't live inside the launch dropdown instead is documented in this comment — verified against the code, with a trigger-only menu entry noted as a possible follow-up. Per-lecture (page-level) enablement is tracked separately in #114.
QuantEcon default: JupyterLite
Python runs in the browser via Pyodide — no server or Binder to host (avoids the flaky Binder, #26). Verified end-to-end: Power boots the Pyodide kernel in-browser and the Run/Restart/Clear controls appear. The Pyodide package caveat (numba/JAX unavailable) is documented in the README, and
binder:/server:backends remain available through the samethebeconfig for projects that need a full environment.Changes
ComputeToolbarSlot.tsx(new) + the slot<li>inToolbar.tsx;PageContent.tsxrenders the slot instead of the inlineNotebookToolbar;styles/app.css#qe-compute-slotrules.project.thebe: { lite: true }in the main visual fixture, plus a second no-thebe fixture (tests/visual/fixture-no-thebe/, served on its own port via a parameterisedserve.sh) for the disabled path.live-compute-toggleasserts the Power toggle renders inside the header slot (scoped to#qe-compute-slot, so a regression to the earlier in-article placements fails);live-compute-toggle-absent-without-thebeproves the toggle is gated on the project opting in, not merely on the page being a notebook. Booting Pyodide is a heavy in-browser download, so kernel execution is verified manually, not in CI.Snapshots
notebook.pngandlaunch-open.pngrefreshed for both platforms (darwin locally, linux via the/update-snapshotsbot — see thread). All checks green on the current head.Closes #88.
🤖 Generated with Claude Code