New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for printing widgets #5850
Conversation
Are you tying print functionality to document widgets (I notice it seems some functionality is on the docmanager)? It would be great if an arbitrary activity could be printable, even if it wasn't a document widget. |
(An example of an interesting printable non-document widget might be the inspector panel) |
Nope. It call the |
fb7ec2a
to
708e465
Compare
Awesome. I'm excited about this work! Also, I'm glad you are experimenting with using Symbols. |
OK this is ready to go from my end. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ian-r-rose - can you comment on the security of printing via iframe? I notice that https://github.com/joseluisq/printd/blob/master/src/index.ts doesn't seem to use any sandbox attributes.
packages/apputils/src/printing.ts
Outdated
/** | ||
* Prints a URL by loading it into an iframe. | ||
* | ||
* NOTE: When https://github.com/joseluisq/printd/issues/20 is fixed this can be removed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our convention is to add a #### Notes
section after the parameters for notes like these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was unable to get this to build, but the overall approach looks good. Looks like it just needs a rebase.
packages/inspector/src/inspector.ts
Outdated
* Print in iframe | ||
*/ | ||
[printSymbol]() { | ||
printWidget.bind(this)('.p-mod-hidden {display: none;}'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to grab more CSS so that this is less ad hoc? I admit this is stretching the bounds of my DOM API knowledge, but if we could do something like node.getAllCSSAsString()
, that would be much nicer.
const factory = new NotebookWidgetFactory({ | ||
name: FACTORY, | ||
fileTypes: ['notebook'], | ||
modelName: 'notebook', | ||
defaultFor: ['notebook'], | ||
preferKernel: true, | ||
baseUrl: services.serverSettings.baseUrl, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the notebook widget already has access to the serviceManager,
so it shouldn't be necessary to pass in the baseUrl
individually.
@@ -475,12 +470,14 @@ function activateNotebookHandler( | |||
// An object for tracking the current notebook settings. | |||
let editorConfig = StaticNotebook.defaultEditorConfig; | |||
let notebookConfig = StaticNotebook.defaultNotebookConfig; | |||
// Pass URL in here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of date comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, thanks!
Thanks @ian-r-rose and @jasongrout for the comments, much appreciated. |
In reference to @ian-r-rose's question about including the base URL in the NotebookPanel, we had a productive discussion during our meeting today about how to avoid this problem. Solutions:
I am currently in favor of the last option (dispatching), because it seems the most general, but also allowing widgets to define their own printing. I will keep exploring this and post possible designs here. |
I was referring to a jupyterlab/packages/docregistry/src/registry.ts Lines 998 to 1005 in 930420a
This, for instance, is what the ipywidgets extension uses to add the widgets mimetype to a notebook: Another option would be to require the |
@saulshanabrook Do you have any insight into what it would require for nbconvert to output static HTML of ipywidget output? There is a conversation about this going on as jupyter/nbconvert#944 |
@gnestor, I do not, but thanks for the pointer. |
So the closest thing is probably decktape, which concentrates on PDF output
of slides. Uses puppeteer (so chrome only, and does some nasty install
stuff).
I had done one of these, also targeted at PDF output based on 15th, but
haven't revisited since qtwebengine landed... Many things were broken in
their old thing. But that's also chromium based.
I think if we were to do anything like this as jupyter, it would have to
support/prefer Firefox.
It's a little "I heard you liked widgets", but it seems like widget
packages would also have to include a kernel-side html renderer to enable
this without a browser in the loop... For some things, this would just mean
packing an AMD package (for now) that nbconvert could find and handle prior
to interpreting the widget... unpkg etc is not a robust solution for
archival content. some widgets could opt for full server-side rendering.
However, lab-forward nbconvert will need a story at some point, which is
when the built application situation will need to be resolved for real... I
don't think most people will want to wait a couple minutes and have to
fiddle with nodejs memory limits to make a static html page after fixing a
typo, and nbviewer just can't support that (unless we get REALLY good at
caching). The upside is that if we _did_ solve it (e.g. no js on server),
the ux of using lab extensions would increase 💯x, and we would get static
output for (almost) free: no worse than any PDF approach thus far.
|
@bollwyvl Yes, it does sound like we would need to use something like decktape that can render the output in a headless browser and convert that to a static HTML, image, PDF, etc.
Why is this?
The simple solution is that widgets provide fallback mimetypes in the output, like |
On Fri, Feb 1, 2019, 21:09 Grant Nestor ***@***.***> wrote:
@bollwyvl <https://github.com/bollwyvl> Yes, it does sound like we would
need to use something like decktape that can render the output in a
headless browser and convert that to a static HTML, image, PDF, etc.
I think if we were to do anything like this as jupyter, it would have to
support/prefer Firefox.
Why is this?
ehm, we shouldn't be contributing to the only-works-on-chrome train wreck.
Also, it's much more supportable in the field due to their ESR release.
It's a little "I heard you liked widgets", but it seems like widget
packages would also have to include a kernel-side html renderer to enable
this without a browser in the loop... For some things, this would just mean
packing an AMD package (for now) that nbconvert could find and handle prior
to interpreting the widget... unpkg etc is not a robust solution for
archival content. some widgets could opt for full server-side rendering.
The simple solution is that widgets provide fallback mimetypes in the
output, like text/html or image/png. Do you envision the "kernel-side
html renderer" providing a text/html output?
Yes, a good archival format should be the ultimate static fallback...
Images without embedded fonts, basically. Or plain text. But made with
something that isn't on the infinite Merry-go-round of browser churn.
However, if you could keep interactivity, you might need to ship some js...
Consider pythreejs: you could use freecad (ha), generate the image, and
dump that. But if you had two geometry things on the page, you'd only want
to embed three js once. Or vrml, lol.
… —
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5850 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AACxRDncVOcrKp-LEEYjYFzwrxhKpZTiks5vJPNKgaJpZM4Z3zp6>
.
|
The one issue with this is that widgets typically interact with a kernel (e.g. that's what differentiates the plotlywidget from the plotly-extension), so when I see a widget rendered outside of a notebook but it doesn't behave the way it's expected to because it's not connected to a kernel (like in the ipywidgets docs: https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Events.html#Linking-traitlets-attributes-in-the-kernel), then I think the result is worse than if it were just a static image. |
Right, so it would be up to the implementation to decide that, and just
emit an image... Or an image in text/html with a tiny bit of interactivity
e.g. zoom if it's a vector... Bad example, I guess, as that could just be a
feature that all vector image get. But jslink models are way cool.
|
@ian-r-rose and I did some pair coding on this today and ironed out most of the kinks. Currently, the issue is that one Safari and on Chrome on linux (but not on Mac) the iframe is not being cleaned up because the Ian is gonna look into this a bit more and maybe experiment with watching how the media queries change as a proxy for when we are done printing. Another option is to only wait for |
Should have been obvious to us earlier, but the reason that |
@saulshanabrook, do you have any thoughts on how to test this in CI? |
JS is blocked, we consider it safe to discard of the hidden iframe once event handling has returned to the main content window.
@saulshanabrook I've added what might be considered a huge hack for deciding when to discard the hidden iframe, but it seems to work okay on Ubuntu with Chrome and Firefox. What do you think? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is really close. Do you have any thoughts on how we would get this working for codemirror instances?
* Copies a node from the base document to the iframe. | ||
*/ | ||
function setIFrameNode(iframe: HTMLIFrameElement, node: HTMLElement) { | ||
iframe.contentDocument.body.appendChild(node.cloneNode(true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to wait for the load
event? My experience with the PDF viewer is that it was necessary, but maybe that's not the case here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't found we needed to wait.
* Returns the URL converting this notebook to a certain | ||
* format with nbconvert. | ||
*/ | ||
export function getNBConvertURL({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping @afshin on this
But they do trigger in Chrome, which is odd...
Looks good to me! I will test it locally in chrome, firefox and safari to make sure it is cleaned up.
Hah... Selenium doesn't cover printing. Looks like our best bet would be to use puppeteer, Chrome's new browser UX testing framework, but it looks like the print dialogue isn't caught yet (puppeteer/puppeteer#3145). They are also starting a FIrefox compatible puppeteer.
I will have a go at it. |
I tested printing a notebook and the inspector on all three, to test the two code paths. They all printed properly and the iframes were cleaned up, besides in safari, printing the inspector resulted in a blank screen. I am fine to call that "success"
Maybe I could document how to test this for a manual release sort of test? I think there are just some UX tests that cannot be automated currently and the best practice would be to have a list we could go down to verify that they are working. This slightly overlaps with @blink1073's work in #6312. Do you think I should put something like that in the |
I started trying to get this working, but kept ending up with an unstyled printout and am finding it hard to debug: Here is the commit that is trying this out. I get rather confused about when I should be waiting for the page to load. If you wanted to try to work through this together this week, I would be game, or happy to merge this as is. |
The docs failure is because a github URL is not resolving properly temporarily, it says "too many hits..." |
Does this mean that printing DOM elements doesn't work in Safari? Did it work with any of the previous iterations of the PR? |
This looks great, thanks for the perseverance @saulshanabrook! |
This closes #1314 by adding the ability to print different widgets, including notebooks.
Features
It adds a new command called
Print...
to theFile
menu, which is enabled when the active widget is printable. This is enabled currently for:This is set to
SUPER P
by default.Not implemented
Implementation
The core printing logic is in the
apputils/src/printing.tsx
file, with the core structure being theIPrintable
interface:The command checks to see if the active widget is printable (i.e. it has the
printSymbol
property and calling that method returns a function) and if so calls that method on it.There are some helper functions provided to support having widgets defer their ability to print to a child widget and the ability to print a widgets DOM node or a custom URL.
To print a DOM node, or a URL, we create an iframe and copy in a DOM node into it or set the src text to HTML text.
Screenshots
Notebook:
Inspector:
JSON:
Image: