starboard-jupyter
Version:
Jupyter-backed cells for Starboard Notebook
160 lines (127 loc) • 4.69 kB
text/typescript
import {
CellTypeDefinition,
CellHandlerAttachParameters,
CellElements,
Cell,
StarboardPlugin,
} from "starboard-notebook/dist/src/types";
import { Runtime, ControlButton } from "starboard-notebook/dist/src/types";
import "./styles";
import { JupyterPluginSettings } from "./types";
import { StarboardJupyterManager } from "./components/kernelManager";
import { OutputArea } from "@jupyterlab/outputarea";
import { createJupyterOutputArea } from "./output";
import { TemplateResult } from "lit-element/lit-element";
export { createJupyterOutputArea } from "./output";
declare global {
interface Window {
runtime: Runtime;
$_: any;
}
}
// Singleton global kernel manager.
let globalKernelManager: StarboardJupyterManager;
function registerJupyter(runtime: Runtime, jupyterOpts: JupyterPluginSettings = { headerText: "Jupyter Plugin" }) {
/* These globals are exposed by Starboard Notebook. We can re-use them so we don't have to bundle them again. */
const lit = runtime.exports.libraries.lit;
const StarboardTextEditor = runtime.exports.elements.StarboardTextEditor;
const cellControlsTemplate = runtime.exports.templates.cellControls;
const icons = runtime.exports.templates.icons;
globalKernelManager = new StarboardJupyterManager(jupyterOpts);
const JUPYTER_CELL_TYPE_DEFINITION: CellTypeDefinition = {
name: "Jupyter",
cellType: ["jupyter"],
createHandler: (cell: Cell, runtime: Runtime) => new JupyterCellHandler(cell, runtime),
};
class JupyterCellHandler {
private elements!: CellElements;
private editor: any;
private outputArea: OutputArea;
private lastRunId = 0;
private isCurrentlyRunning: boolean = false;
cell: Cell;
runtime: Runtime;
constructor(cell: Cell, runtime: Runtime) {
this.cell = cell;
this.runtime = runtime;
this.outputArea = createJupyterOutputArea();
}
private getControls(): TemplateResult | string {
const icon = this.isCurrentlyRunning ? icons.ClockIcon : icons.PlayCircleIcon;
const tooltip = this.isCurrentlyRunning ? "Cell is running" : "Run Cell";
const runButton: ControlButton = {
icon,
tooltip,
callback: () => this.runtime.controls.emit({ id: this.cell.id, type: "RUN_CELL" }),
};
let buttons = [runButton];
return cellControlsTemplate({ buttons });
}
attach(params: CellHandlerAttachParameters): void {
this.elements = params.elements;
const topElement = this.elements.topElement;
lit.render(this.getControls(), this.elements.topControlsElement);
this.editor = new StarboardTextEditor(this.cell, this.runtime, {
language: "python",
});
topElement.appendChild(this.editor);
this.elements.bottomElement.appendChild(this.outputArea.node);
}
async run() {
const codeToRun = this.cell.textContent;
this.lastRunId++;
const currentRunId = this.lastRunId;
this.isCurrentlyRunning = true;
lit.render(this.getControls(), this.elements.topControlsElement);
await globalKernelManager.runCode({ code: codeToRun }, this.outputArea);
await this.outputArea.future.done;
if (this.lastRunId === currentRunId) {
this.isCurrentlyRunning = false;
lit.render(this.getControls(), this.elements.topControlsElement);
}
const val = this.outputArea.model.toJSON();
window.$_ = val;
return val;
}
focusEditor() {
this.editor.focus();
}
async dispose() {
this.editor.remove();
}
clear() {
this.outputArea.model.clear();
}
}
runtime.definitions.cellTypes.register(JUPYTER_CELL_TYPE_DEFINITION.cellType, JUPYTER_CELL_TYPE_DEFINITION);
const existingKernelUI = document.querySelector("starboard-jupyter-manager");
if (existingKernelUI) {
(existingKernelUI as StarboardJupyterManager).remove();
}
if (jupyterOpts.mount) {
jupyterOpts.mount.appendChild(globalKernelManager);
} else {
const nb = document.querySelector("starboard-notebook");
if (nb) nb.prepend(globalKernelManager);
}
}
const pluginExports = {
createJupyterOutputArea: createJupyterOutputArea,
getGlobalKernelManager: () => {
return globalKernelManager;
},
};
export const plugin: StarboardPlugin<JupyterPluginSettings, typeof pluginExports> = {
id: "starboard-jupyter",
metadata: {
name: "Jupyter for Starboard",
},
exports: pluginExports,
async register(runtime: Runtime, opts?: JupyterPluginSettings) {
if (opts === undefined) {
opts = { headerText: "Jupyter Plugin" };
}
registerJupyter(runtime, opts);
},
};
export default plugin;