Skip to content

Scope (Plugin) Architecture

Jon Mease edited this page Jul 2, 2020 · 2 revisions

Overview

While initially motivated by the needs of plotly.py, Kaleido is designed to make it fairly straightforward to add support for additional web-based visualization libraries. Plugins in Kaleido are called "scopes".

We hope to collaborate with many other web-based visualization libraries to finally solve this problem across the web-based visualization community.

Architecture

This section describes the current requirements for adding a new scope (with Python support) for a new visualization library. This section will assume that the new library is named superviz.

repos/kaleido/py/kaleido/scopes/superviz.py

First add a new Python class named SupervizScope to a new file at repos/kaleido/py/kaleido/scopes/superviz.py. This class should subclass BaseScope. It must implement the constructor and override the scope_name property to return the string 'superviz'. The constructor should accept and validate any global configuration values the library needs. In particular, this may include the path/URL of the superviz.js library.

See repos/kaleido/py/kaleido/scopes/plotly.py for a reference example.

repos/kaleido/cc/scopes/Superviz.h

Add a new SupervizScope C++ class to a new header file at repos/kaleido/cc/scopes/Superviz.h. This class should subclass BaseScope. It must override the ScopeName method to return the string "superviz". In the constructor, it should look for command-line switches that correspond to the global configuration values above. These switches will have identical names to the Python variables defined above, but with underscores replaced by hyphens. The scriptTags list property should be updated with the <script> tag URLs that should be included in the initial HTML document. The localScriptFiles list property should be updated with the paths of local JavaScript files to load dynamically after the initial HTML document has been loaded.

It must also override the BuildCallArguments method. This method is responsible for returning a std::vector of chromium CallArguments. These should correspond to the same global configuration parameters, but here the order is significant, so choose an order that matches the argument order of the JavaScript render method described below.

See repos/kaleido/cc/scopes/Plotly.h for a reference example.

repos/kaleido/cc/scopes/Factory.h

Update the LoadScope function in repos/kaleido/cc/scopes/Factory.h so that an instance of the SupervizScope class is returned when the input string is "superviz".

repos/kaleido/js/src/superviz/render.js

Next, add a new render JavaScript function to a new file at repos/kaleido/js/src/superviz/render.js. The first argument to this function will be a JavaScript object that corresponds to the arguments to the BaseScope.transform Python method. For the Plotly scope these are data, format, width, height, and scale. The second through last argument will match the arguments constructed in the BuildCallArguments C++ method above.

This function is responsible for returning a Promise that resolves to an object that includes the result of the image export attempt. The object should have the following properties

  • code: If an error occurred, the code should be a non-zero integer and an associated message should be included that describes the error. If export was successful, code should be 0
  • message: If an error occurred, this should be a string containing the error message. If export was successful, message should be null
  • result: The image export result as a string. All formats except svg should be base64 encoded. Special Case: If the input format is pdf, then the render function should choose the most appropriate image format that will be embedded in pdf (e.g. svg) and instead of returning a result, it should set this image as the src property of the <img> tag with id kaleido-image.
  • format, width, height, scale: The format, width, height, and scale factor that were used. Even though these values are inputs, the render function may supply its own defaults and whatever values were actually used to generate the image should be included here. If the input format was pdf, then the format returned here should be whatever image format was used to generate the image that will be embedded in the PDF. e.g. svg.
  • pdfBgColor: If the format is pdf, this property should contain the desired background color of the figure. It is recommended that, if possible, the background color the associated figure image be set to fully transparent so that the PDF background color will fully show through. If format was not 'pdf' this property should be set to null.

Additional JavaScript helper functions can be added to the repos/kaleido/js/src/superviz/ directory. The JavaScript files are bundled using browserify on build. Additional NPM dependencies can be added to repos/kaleido/js/package.json. Note that the visualization library itself shouldn't be added as an NPM dependency, this is because we want to keep the resulting JavaScript bundle as small as possible, and we don't want to have to release a new version of Kaleido for each release of the various visualization libraries. Instead, the visualization libraries should be loaded from a CDN url by default and added to scriptTags above. To support offline use, it is also helpful to support loading the visualization library from a local JavaScript file, adding the path to localScriptFiles instead.

See repos/kaleido/js/src/plotly/render.js for a reference example.

repos/kaleido/js/src/index.js

Update the module.exports section of repos/kaleido/js/src/index.js to include:

superviz: require("./superviz/render")

Other language wrappers

As support is added for additional language wrappers (other than Python), a new scope class/interface will be needed for each wrapper language. If stored in this repository, these wrappers should be added to the repos/kaleido/{language_name}/ director.

Future direction

This scope architecture will surely evolve over time as support is added for additional libraries. Once we get experience with additional libraries, it may be possible for scopes to be specified entirely in JavaScript + Python, without requiring C++ changes.

Additionally, we may want to extend this architecture to more naturally support non-image transform operations.