fix(notebooks): render dashboards & panels correctly in browser JupyterLab#30
Merged
Conversation
…erLab Three ipympl-vs-Agg issues surfaced once the play() MIME fix (#29) made the videos play in browser JupyterLab. All were masked in VS Code, whose inline backend is Agg; the notebooks run under %matplotlib widget (ipympl) for the interactive sandboxes. - Dashboard videos showed only the first of five footprint/trace rows. The build_*_frames helpers rasterize a figure to RGB via canvas.buffer_rgba(), assuming it equals figsize x dpi. Once an ipympl canvas has been displayed, a retina frontend stamps device_pixel_ratio=2 onto later figures, so the buffer comes back 2x with the figure drawn into one quadrant - dropping 4 of 5 rows. Render these offline-composited figures on an explicit Agg canvas (_agg_figure) so the buffer is always exactly figsize x dpi, on every frontend. - Interactive sandbox panels (Stage 1, 2, 3, 5b, 6, 8, 9) came up blank until a slider was nudged. interactive_panel drew before display(canvas); ipympl never pushes a frame rendered before its view is attached. Draw after display. - Static figure cells (optics reveal, vasculature, bleaching, motion) had the same blank-until-interaction problem via display(fig.canvas). Add a show() helper that displays then draws, and route those cells through it. Also restore play()'s height-based scaling: #29 capped height to the source ("never upscale"), which shrank the small dashboards (built at the downsampled movie resolution) to an illegible strip. Honour the requested height (mediapy scales up or down) while keeping the explicit even (h, w) that fixed #28. Verified end-to-end in headless Chromium against a live JupyterLab server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two more browser-JupyterLab issues found reviewing the PR branch, both in Stage 5b (vasculature): - show() rendered blank. Routing the static builder panels through the live ipympl canvas (display + post-attach draw) is unreliable deep in a long notebook - the first frame is never pushed, and windowing re-blanks it on scroll. These panels are static (drawn once, never interacted with), so rasterize to an inline PNG and display that, sidestepping the live canvas entirely. Backend-independent; always paints. - The video looked dim, reading as if the neuropil haze were missing (it is not - until="vasculature" runs through the earlier neuropil step). play() normalized by the absolute max, so a few bright cell peaks crushed the haze pedestal to near-black. Give play() an optional vmax (clip to a percentile, the same scaling the dashboard colourizers use) and pass the 99.9th percentile the panel already computes, so the raw movie reads as bright as its dashboards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The slider panels are fixed-size teaching figures, but ipympl wraps each canvas in a toolbar, a status footer, and a drag-resize handle. With the toolbar already hidden the footer + resize affordance still reserve a band of empty canvas below the plot in browser JupyterLab. Turn header/footer/ resizable off centrally in interactive_panel so the panel ends at the plot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A component's share of the haze scales with its spatial field's mean, but smooth_spatial_field peak-normalizes each field, which lets that mean float ~1.2-3.1x across RNG draws (measured over seeds 0-42) - so one component could dominate the background 3:1 and, in the R/G/B components video, render one channel far brighter than the others. Rescale each component's field to the shared average mean in neuropil_components, so every component carries an equal share. The aggregate level (the mean over components) is unchanged, so amplitude still sets the absolute background and the rendered haze is identical in total - only its split across components is equalized. Ground-truth neuropil_spatial is now mean-balanced rather than individually peak-normalized; the step test asserts the equal-mean invariant instead of peak == 1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…mpl canvas The slider sandboxes (Stages 1-3, 5b, 6, 8, 9) drive an ipympl canvas under %matplotlib widget. ipympl does not reliably push that canvas to a browser- JupyterLab view: near the top of the notebook a post-display draw paints it, but deeper down (Stage 6+) the frame is dropped and the panel comes up blank - sliders visible, plot missing - and windowing re-blanks it on scroll. Render the figure to a PNG and (re)display it in an Output widget on every slider change instead of showing the live canvas. Reliable on every frontend, at any depth. continuous_update is already off so it re-renders on release, and clear_output(wait=True) swaps without flicker. This supersedes the earlier draw-after-display and chrome-trim workarounds (no canvas is shown now, so the toolbar/footer/resize reserve and the first-frame race are both gone). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…w out the scale A few fully-dark outer cells (heavy illumination/vignette falloff -> ~0 gain) land at ~1e-15 SNR. On the log axis matplotlib autoscaled down to them, leaving the detectability transition crammed into the top sliver across a dozen empty decades. Clamp the y-limits to the meaningful range: bottom a few-fold below the 1st-percentile cell (outlier-robust) and the threshold, top just past the brightest - always bracketing the floor line. The dark cells clip off the bottom but stay counted in the legend. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No-op commit to re-run the Read the Docs build, which failed on a transient intersphinx inventory fetch (not a content error - the docs build clean locally with warnings-as-errors). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…unreachable
docs.scipy.org intermittently times out from the build network. Under sphinx
`-W` that ConnectTimeout became a hard failure ("failed to reach any of the
inventories"), breaking every docs build even though nothing in the docs is
wrong. Add a logging filter on the intersphinx logger that drops only that
fetch-failure record; cross-refs into a missing inventory fall back to plain
text (nitpicky is off), and every real warning (broken refs, autodoc import
failures) still fails the build as before. Verified locally: with scipy
unreachable the build now succeeds instead of erroring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #29. Once the
play()MIME fix made the videos play in browser JupyterLab, three more rendering issues became visible. All were masked in VS Code (whose inline backend is Agg); the notebooks run under%matplotlib widget(ipympl) for the interactive sandboxes, and ipympl behaves differently in a browser.Issues fixed
1. Dashboard videos showed only 1 of 5 footprint/trace rows.
build_*_framesrasterize a matplotlib figure to RGB viacanvas.buffer_rgba(), assuming it equalsfigsize × dpi. Once an ipympl canvas has been displayed, a retina frontend stampsdevice_pixel_ratio = 2onto later figures, so the buffer comes back at 2× with the figure drawn into a single quadrant — the height-only rescue in_cursor_panelcan't recover it, so 4 of 5 rows fall outside the panel. Fix: render the offline-composited figures on an explicit Agg canvas (_agg_figure), so the buffer is always exactlyfigsize × dpion every frontend.2. Interactive sandbox panels came up blank until a slider was nudged.
interactive_paneldrew beforedisplay(canvas); ipympl never pushes a frame rendered before its view is attached. Fix: draw after display.3. Static-figure cells (optics reveal, vasculature, bleaching, motion) were blank for the same reason via
display(fig.canvas). Fix: a smallshow()helper that displays then draws, with those cells routed through it.Also
Restore
play()'s height-based scaling. #29 capped the height to the source (min(height, src_h), "never upscale"), which shrank the dashboards — built at the downsampled movie resolution and relying onplay()to scale them up — into an illegible strip. Now the requested height is honoured (mediapy scales up or down) while keeping the explicit even(h, w)that fixed #28.Verification
Reproduced and verified each fix end-to-end in headless Chromium driven against a live JupyterLab server (Playwright): the Stage 4 dashboard now shows all 5 footprints + traces, and the Stage 1 / optics-reveal panels render on first run.
ruffclean; full test suite passes (382).🤖 Generated with Claude Code
📚 Documentation preview 📚: https://minisim--30.org.readthedocs.build/en/30/