···55Blockly extension for JupyterLab.
6677## Blockly
88+89Blockly is a library from Google for building beginner-friendly block-based programming languages.
9101011Docs: https://developers.google.com/blockly/guides/overview
1112Repo: https://github.com/google/blockly
12131313-1414## Requirements
15151616-* JupyterLab == 3.4
1616+- JupyterLab == 3.4
17171818## Install
19192020To install the extension, execute:
21212222```bash
2323-micromamba create -n blockly -c conda-forge python ipykernel xeus-python xeus-lua
2323+micromamba create -n blockly -c conda-forge python jupyterlab==3.4 ipykernel xeus-python xeus-lua jupyterlab-language-pack-es-ES jupyterlab-language-pack-fr-FR
2424micromamba activate blockly
2525pip install jupyterlab_blockly
2626```
27272828#### Kernels
2929-* ipykernel
3030-* xeus-python
3131-* xeus-lua
3232-* [JavaScript](https://github.com/n-riesco/ijavascript#installation)
3333-* [JavaScript](https://github.com/yunabe/tslab)
2929+3030+- ipykernel
3131+- xeus-python
3232+- xeus-lua
3333+- [JavaScript](https://github.com/n-riesco/ijavascript#installation)
3434+- [JavaScript](https://github.com/yunabe/tslab)
34353536## Uninstall
3637···4041pip uninstall jupyterlab_blockly
4142```
42434343-4444## Contributing
45454646### Development install
···5252`yarn` or `npm` in lieu of `jlpm` below.
53535454```bash
5555-micromamba create -n blockly -c conda-forge python nodejs yarn jupyterlab==3.4 jupyter-packaging
5555+micromamba create -n blockly -c conda-forge python nodejs pre-commit yarn jupyterlab==3.4 jupyter-packaging jupyterlab-language-pack-es-ES jupyterlab-language-pack-fr-FR ipykernel xeus-python xeus-lua
5656micromamba activate blockly
5757# Clone the repo to your local environment
5858# Change directory to the jupyterlab_blockly directory
5959# Install package in development mode
6060pip install -e .
6161+# Installing pre-commit to run command when adding commits
6262+pre-commit install
6163# Link your development version of the extension with JupyterLab
6264jupyter labextension develop . --overwrite
6365# Rebuild extension Typescript source after making changes
+1-1
RELEASE.md
···1515pip install build twine
1616```
17171818-To create a Python source package (``.tar.gz``) and the binary package (`.whl`) in the `dist/` directory, do:
1818+To create a Python source package (`.tar.gz`) and the binary package (`.whl`) in the `dist/` directory, do:
19192020```bash
2121python -m build
···44 DocumentModel
55} from '@jupyterlab/docregistry';
66import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
77+import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
7889import { BlocklyEditor, BlocklyPanel } from './widget';
910import { BlocklyRegistry } from './registry';
···1819> {
1920 private _registry: BlocklyRegistry;
2021 private _rendermime: IRenderMimeRegistry;
2222+ private _mimetypeService: IEditorMimeTypeService;
21232224 /**
2325 * Constructor of BlocklyEditorFactory.
···2830 super(options);
2931 this._registry = new BlocklyRegistry();
3032 this._rendermime = options.rendermime;
3333+ this._mimetypeService = options.mimetypeService;
3134 }
32353336 get registry(): BlocklyRegistry {
···4346 protected createNewWidget(
4447 context: DocumentRegistry.IContext<DocumentModel>
4548 ): BlocklyEditor {
4646- const manager = new BlocklyManager(this._registry, context.sessionContext);
4949+ const manager = new BlocklyManager(
5050+ this._registry,
5151+ context.sessionContext,
5252+ this._mimetypeService
5353+ );
4754 const content = new BlocklyPanel(context, manager, this._rendermime);
4855 return new BlocklyEditor({ context, content, manager });
4956 }
···5562 * A rendermime instance.
5663 */
5764 rendermime: IRenderMimeRegistry;
6565+ /*
6666+ * A mimeType service instance.
6767+ */
6868+ mimetypeService: IEditorMimeTypeService;
5869 }
5970}
+6-1
src/index.ts
···66import { jsonIcon } from '@jupyterlab/ui-components';
77import { WidgetTracker } from '@jupyterlab/apputils';
88import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
99+import { IEditorServices } from '@jupyterlab/codeeditor';
910import { ICommandPalette } from '@jupyterlab/apputils';
1011import { IFileBrowserFactory } from '@jupyterlab/filebrowser';
1112import { ILauncher } from '@jupyterlab/launcher';
···4344 requires: [
4445 ILayoutRestorer,
4546 IRenderMimeRegistry,
4747+ IEditorServices,
4648 IFileBrowserFactory,
4749 ISettingRegistry,
4850 ITranslator
···5355 app: JupyterFrontEnd,
5456 restorer: ILayoutRestorer,
5557 rendermime: IRenderMimeRegistry,
5858+ editorServices: IEditorServices,
5659 browserFactory: IFileBrowserFactory,
5760 settings: ISettingRegistry,
5861 translator: ITranslator,
···97100 shutdownOnClose: true,
9810199102 // The rendermime instance, necessary to render the outputs
100100- // after a code execution.
103103+ // after a code execution. And the mimeType service to get the
104104+ // mimeType from the kernel language
101105 rendermime: rendermime,
106106+ mimetypeService: editorServices.mimeTypeService,
102107103108 // The translator instance, used for the internalization of the plugin.
104109 translator: translator
+32-15
src/layout.ts
···11-import { SimplifiedOutputArea, OutputAreaModel } from '@jupyterlab/outputarea';
21import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
32import { ISessionContext, showErrorMessage } from '@jupyterlab/apputils';
33+import { CodeCell, CodeCellModel } from '@jupyterlab/cells';
4455import { Message } from '@lumino/messaging';
66import { PartialJSONValue } from '@lumino/coreutils';
···2121 private _manager: BlocklyManager;
2222 private _workspace: Blockly.WorkspaceSvg;
2323 private _sessionContext: ISessionContext;
2424- private _outputArea: SimplifiedOutputArea;
2424+ private _cell: CodeCell;
25252626 /**
2727 * Construct a `BlocklyLayout`.
···4040 // and the output area to render the execution replies.
4141 this._host = document.createElement('div');
42424343- // Creating a SimplifiedOutputArea widget to render the
4343+ // Creating a CodeCell widget to render the code and
4444 // outputs from the execution reply.
4545- this._outputArea = new SimplifiedOutputArea({
4646- model: new OutputAreaModel({ trusted: true }),
4545+ this._cell = new CodeCell({
4646+ model: new CodeCellModel({}),
4747 rendermime
4848 });
4949+ // Trust the outputs and set the mimeType for the code
5050+ this._cell.readOnly = true;
5151+ this._cell.model.trusted = true;
5252+ this._cell.model.mimeType = this._manager.mimeType;
5353+5454+ this._manager.changed.connect(this._onManagerChanged, this);
4955 }
50565157 get workspace(): PartialJSONValue {
···99105 run(): void {
100106 // Serializing our workspace into the chosen language generator.
101107 const code = this._manager.generator.workspaceToCode(this._workspace);
108108+ this._cell.model.sharedModel.setSource(code);
109109+ this.addWidget(this._cell);
110110+ this._resizeWorkspace();
102111103112 // Execute the code using the kernel, by using a static method from the
104113 // same class to make an execution request.
···111120 `
112121 );
113122 } else {
114114- SimplifiedOutputArea.execute(code, this._outputArea, this._sessionContext)
115115- .then(resp => {
116116- this.addWidget(this._outputArea);
117117- this._resizeWorkspace();
118118- })
123123+ CodeCell.execute(this._cell, this._sessionContext)
124124+ .then(() => this._resizeWorkspace())
119125 .catch(e => console.error(e));
120126 }
121127 }
···155161 private _resizeWorkspace(): void {
156162 //Resize logic.
157163 const rect = this.parent.node.getBoundingClientRect();
158158- const { height } = this._outputArea.node.getBoundingClientRect();
159159- this._host.style.width = rect.width + 'px';
164164+ const { height } = this._cell.node.getBoundingClientRect();
160165 const margin = rect.height / 3;
161166162167 if (height > margin) {
163168 this._host.style.height = rect.height - margin + 'px';
164164- this._outputArea.node.style.height = margin + 'px';
165165- this._outputArea.node.style.overflowY = 'scroll';
169169+ this._cell.node.style.height = margin + 'px';
170170+ this._cell.node.style.overflowY = 'scroll';
166171 } else {
167172 this._host.style.height = rect.height - height + 'px';
168168- this._outputArea.node.style.overflowY = 'hidden';
173173+ this._cell.node.style.overflowY = 'scroll';
169174 }
170175171176 Blockly.svgResize(this._workspace);
177177+ }
178178+179179+ private _onManagerChanged(
180180+ sender: BlocklyManager,
181181+ change: BlocklyManager.Change
182182+ ) {
183183+ if (change === 'kernel') {
184184+ // Serializing our workspace into the chosen language generator.
185185+ const code = this._manager.generator.workspaceToCode(this._workspace);
186186+ this._cell.model.sharedModel.setSource(code);
187187+ this._cell.model.mimeType = this._manager.mimeType;
188188+ }
172189 }
173190}
+24-1
src/manager.ts
···11import { ISessionContext } from '@jupyterlab/apputils';
22+import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
23import { KernelSpec, KernelConnection } from '@jupyterlab/services';
34import { IChangedArgs } from '@jupyterlab/coreutils';
45···2021 private _registry: BlocklyRegistry;
2122 private _selectedKernel: KernelSpec.ISpecModel;
2223 private _sessionContext: ISessionContext;
2424+ private _mimetypeService: IEditorMimeTypeService;
2325 private _changed: Signal<this, BlocklyManager.Change>;
24262527 /**
2628 * Constructor of BlocklyManager.
2729 */
2828- constructor(registry: BlocklyRegistry, sessionContext: ISessionContext) {
3030+ constructor(
3131+ registry: BlocklyRegistry,
3232+ sessionContext: ISessionContext,
3333+ mimetypeService: IEditorMimeTypeService
3434+ ) {
2935 this._registry = registry;
3036 this._sessionContext = sessionContext;
3737+ this._mimetypeService = mimetypeService;
31383239 this._toolbox = this._registry.toolboxes.get('default');
3340 this._generator = this._registry.generators.get('python');
···4148 */
4249 get toolbox(): JSONObject {
4350 return this._toolbox;
5151+ }
5252+5353+ /**
5454+ * Returns the mimeType for the selected kernel.
5555+ *
5656+ * Note: We need the mimeType for the syntax highlighting
5757+ * when rendering the code.
5858+ */
5959+ get mimeType(): string {
6060+ if (this._selectedKernel) {
6161+ return this._mimetypeService.getMimeTypeByLanguage({
6262+ name: this._selectedKernel.language
6363+ });
6464+ } else {
6565+ return 'text/plain';
6666+ }
4467 }
45684669 /**