starboard-notebook
starboard-notebook copied to clipboard
Evaluate all script (grand)children of HTML output to render output of Bokeh and Plotly
(As discussed on the Discord server) to render plotly in Starboard Notebook I was coming up with this kind of hack: manually evaluating Javascript that was added to the DOM:
document.querySelectorAll('div.cell-bottom * script[type|="text/javascript"]').forEach(function(e) { eval(e.innerText); })
This PR is intended to start the discussion on if and how we might want to address this in Starboard Notebook itself. Right now libraries, like Bokeh and Plotly will not work out of the box, which is unfortunate.
We should also consider security implications of a fix similar to this one. Of course it helps if CORS and friends are configured properly, but there might be more risks besides stealing (session) data from a user by having them execute malicious code.
Plotly example
import pandas # to populate the entry in sys.modules and keep plotly happy
import micropip
await micropip.install('plotly')
import plotly.express as px
from js import document
x = [1, 2, 3]
y = [1, 2, 3]
fig = px.scatter(x=x, y=y)
html = fig.to_html(
include_plotlyjs=True,
full_html=False,
default_height='350px',
)
div = document.createElement('div')
div.innerHTML = html
div
document.querySelectorAll('div.cell-bottom * script[type|="text/javascript"]').forEach(function(e) { eval(e.innerText); })
Bokeh example
from bokeh.plotting import figure, show, save
from bokeh.io import output_file
from js import document
p = figure()
p.line([1, 2, 3], [1, 2, 3])
output_file('/tmp/output.html', mode='inline')
save(p)
# TODO bokeh probably has a better way to get this HTML out
with open('/tmp/output.html', 'r') as f:
contents = f.read()
div = document.createElement('div')
div.innerHTML = contents
div
document.querySelectorAll('div.cell-bottom * script[type|="text/javascript"]').forEach(function(e) { eval(e.innerText); })
Looks good to me!
I don't think it has security implications, the notebook should (always) be run in a sandbox (i.e. in an iframe), or one should trust the content if you want to embed it at the top level. Embedding at the top level (i.e. in the root page) is not really supported properly (and will require some cleanup/redesign, as now the CSS will surely clash, and perhaps some globals too).
Good to hear!