https://kotlinlang.org logo
Title
p

Peter

03/30/2021, 6:44 PM
Hi, I embedded the EChart library in Kotlin Jupyter and got it all working. Very nice overall experience, I can see Kotlin for interactive development really working out for many use-cases. But I noticed different behavior between a notebook and lab environment. If I use a notebook, I have to use the iframe solution, aka HTML(..., true) otherwise it won't work since the JavaScript library doesn't get initialized. And when using a Jupyter lab it is exactly the opposite, so I have to use the regular embedded HTML option HTML(..., false). 1. It there a way to auto detect in which environment my code is running (even in the Notebook API I couldn't find this function) so I can make these changes without bothering users 2. I didn't notice this difference in the past when using Python in notebooks and labs (but might have missed it). Is this a deliberate choice or enforced by the Jupyter platforms?
a

altavir

03/31/2021, 5:29 AM
It is the problem with notebooks we've been struggling against for quite some time. Lab and classic have completely different ways to load js. Classic isolates the environment for a single cell and uses requirejs to add scripts, whereas lab has a common environment for a page. For python libraries there are always two different sets of plugins. I know that kernel team is thinking about that hard. I personally would just drop support for classic.
r

roman.belov

03/31/2021, 8:54 AM
cc @Ilya Muradyan
p

Peter

03/31/2021, 11:58 AM
Thanks for the info. I guess for me the only reason to still support classic notebooks is the better Kotlin support for pro-actively showing compilation errors in a cell. But I guess over time that will also be supported in a lab environment.
i

Ilya Muradyan

04/01/2021, 8:01 AM
Hello, Peter! You may try to use resources in your JSON descriptor or in library integration. There is an example. It produces the following HTML/JS:
<div id="kotlin_out_0"/>
<script type="text/javascript">
    if(!window.kotlinQueues) {
        window.kotlinQueues = {};
    }
    if(!window.kotlinQueues["testLib2"]) {
        var resQueue = [];
        window.kotlinQueues["testLib2"] = resQueue;
        window["call_testLib2"] = function(f) {
            resQueue.push(f);
        }
    }
    (function (){
        var modifiers = [(function(script) {
            script.src = "<https://cdn.plot.ly/plotly-latest.min.js>"
        }),
            (function(script) {
                script.textContent = "function test_fun(x) {\n    return (\"var\" + x);\n}\n"
                script.type = "text/javascript";
            })]
        var e = document.getElementById("kotlin_out_0");
        modifiers.forEach(function (gen) {
            var script = document.createElement("script");
            gen(script)
            script.onload = function() {
                window["call_testLib2"] = function(f) {f();};
                window.kotlinQueues["testLib2"].forEach(function(f) {f();});
                window.kotlinQueues["testLib2"] = [];
            };
            script.onerror = function() {
                window["call_testLib2"] = function(f) {};
                window.kotlinQueues["testLib2"] = [];
                var div = document.createElement("div");
                div.style.color = 'darkred';
                div.textContent = 'Error loading resource testLib2';
                document.getElementById("kotlin_out_0").appendChild(div);
            };

            e.appendChild(script);
        })
    })()
</script>
It gives you an ability to use your library objects inside the wrapper. For example, if the library defines Plotly object, you may write the following in your JS:
call_testLib2(() => { Plotly.plot(...) })
But I can't recall if it works both in Notebook and Lab
a

altavir

04/01/2021, 8:04 AM
Sadly, it does not work well in Classic. I've spent quilte a lot of time, trying to make it work. The problem is that each cell has its own environment, so you need to initialize the external script for each cell. The only way to communicate between cells is using the require-js mechanism and Notebook uses some kind of mangled prefixes for it, so I did not manage to make it work either. If the script is loaded via CDN, the best way is to just add a stand-alone script loaded for each cell, but for local bundles it won't work.
The only recommended way fron Jupyter documentation is to use custom.js. And it probably could not be changed without running some kind of installation. The solution may be to automatically add required scripts to custom.js on descriptor load and then reload the notebook, but it is obviously ugly.
p

Peter

04/04/2021, 10:05 AM
Thanks, seems indeed that custom.js is the only easy way to solve this. Since I use docker images for now, this is just a small change to the docker build. P.S Still would be nice at JVM level find out if it is running in Lab or Classic context so I can adjust the output accordingly. P.P.S Still not entirely sure why I cannot load echarts.js (even when completely embedded in a script tag) while other javascript code in the script tag gets execute correctly.