In a Dev-category discussion titled What would it take to put a live Matplotlib figure in a post?, Alon1 proposes embedding interactive Matplotlib graphs via HTML generated by mpld3, suggesting uploading .html files and rendering them in iframes. Alteras cautions about security risks but offers an alternative: using JupyterLite in-browser with a HighlightJS plugin to execute Python code blocks interactively via iframe — no server-side code needed. Alon1 counters that client-side HTML is low-risk and simpler than managing dependencies for raw Python, though admits running Python scripts in-post would be ideal. Alteras clarifies that pre-generated HTML can be embedded via external sites like CodePen, sharing a working example. merefield confirms feasibility with a screenshot of an embedded Jupyter notebook, offering paid help for automation. Alon1 asks if ipympl (for interactivity) and project-level PYTHONPATH support are possible; merefield affirms both are likely achievable and invites a private discussion. Alon1 tests the iframe approach but finds uploaded .html files download instead of render; Alteras explains Discourse defaults to Content-Disposition: attachment and suggests adapting the Inline PDF Previews workaround.
I know this is out of the scope of most communities, but we are a scientific one, using Python and a lot of graphs (Matplotlib) that we share with each other over Discourse.
It would be so amazing to be able to share an interactive graph instead of a static jpg/png
Consider this example:
import matplotlib.pyplot as plt
import mpld3
import numpy as np
Fs = 4000
f = 100
sample = 200
x = np.arange(sample)
y = np.sin(2 * np.pi * f * x / Fs)
fig, ax = plt.subplots(1,1)
ax.plot(x, y, marker=".")
html_str = mpld3.fig_to_html(fig)
Html_file= open("index.html","w")
Html_file.write(html_str)
Html_file.close()
You would be able to open the index.html file and it will show that (only that it will be INTERACTIVE…)
The method you described would be possible via a custom plugin, combining both a markdown-it style plugin and possibly a nice object storage for the HTMLs. However I’d be cautious about the security of it, letting user inputted code run anywhere on the server. Discourse already does a lot of work just to sanitize a post for HTML and JS to be safely parsed in the backend.
You might have better luck instead trying to embed a Python interpreter in the browser and let it run the code on client, skipping the whole HTML creation. JupyterLite would let you run JupyterLab on the browser with UI. From the documentation, it looks like you can have it auto run code on creation of the iframe. You can feed in the code with a custom HighlightJS plugin that will create an actionable button on any Python code block, or have it run automatically.
So something like:
User sees a post with a Python code block.
User hovers over the code block, where a button appears to let them run the code in browser.
User clicks on the button, which creates an iframe to JupyterLite below the code, importing the code from the code block.
Since this would just be a HighlightJS plugin that just creates an iframe to JupyterLite, you can create it all within a Theme Component, no server code needed.
One thing to note: JupyterLite is described as not being feature complete to JupyterLab, and I’m not sure how it goes about handling package imports, so you might have to host it yourself. JupyterLite uses Pyodide, which supports any wheel on PyPI, so it should be good
I think that the security risk is not that big in case the index.html runs on the client end (on the user’s browser, not on the Discourse server).
Also the advantage in the mpld3 approach is that it is a single-file with no dependencies. If you run raw Python you have to also be able to upload all dependencies like csv files, data files, etc. that are the source of what you want to plot.
Having said that, to be able to run raw Python scripts inside a Discourse post would be just fabulous!
Oh! I completely misunderstood how the HTML would be generated. If the user is pre-generating the HTML, you could have them iframe it from an external site, with the domain whitelisted.
For example, codepen, which should be allowed by default:
<iframe height="300" width="800" style="width: 100%;" scrolling="no" title="mplD3 example, actually working" src="https://codepen.io/gully/embed/BawMGr?default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/gully/pen/BawMGr">
mplD3 example, actually working</a> by gully (<a href="https://codepen.io/gully">@gully</a>)
on <a href="https://codepen.io">CodePen</a>.
</iframe>
That’s really awesome!
I have a couple of questions:
Do you think it will work with ipympl (otherwise the figures are not interactive, which invalidates the whole point)
Do you think it can work with an underlying project folder where we can set PYTHONPATH env variable before launching Jupyter so that users can import functions and dependencies from the joined project?
and yes, I do have a budget if this is reasonable and feasible and can be contributed as open source for the entire Discourse community.
@Alteras Thanks this solution is really straightforward and works if you can find a way to upload the “index.heml” file to the Discourse (which is easy to enable .html upload) and serve it in the iframe code. To only problem, it’s not actually working.. when I try that, it just downloads the index.html instead of rendering it in the iframe. Perhaps there’s a meta tag to tell the iframe to show the file instead of downloading?