···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
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+3030+- ipykernel
3131+- xeus-python
3232+- xeus-lua
3333+- [JavaScript](https://github.com/n-riesco/ijavascript#installation)
3434+- [JavaScript](https://github.com/yunabe/tslab)
3535+2836## Uninstall
29373038To remove the extension, execute:
···3240```bash
3341pip uninstall jupyterlab_blockly
3442```
3535-36433744## Contributing
3845···4552`yarn` or `npm` in lieu of `jlpm` below.
46534754```bash
4848-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
4956micromamba activate blockly
5057# Clone the repo to your local environment
5158# Change directory to the jupyterlab_blockly directory
5259# Install package in development mode
5360pip install -e .
6161+# Installing pre-commit to run command when adding commits
6262+pre-commit install
5463# Link your development version of the extension with JupyterLab
5564jupyter labextension develop . --overwrite
5665# 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
···33 JupyterFrontEndPlugin,
44 ILayoutRestorer
55} from '@jupyterlab/application';
66+import { jsonIcon } from '@jupyterlab/ui-components';
67import { WidgetTracker } from '@jupyterlab/apputils';
78import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
99+import { IEditorServices } from '@jupyterlab/codeeditor';
810import { ICommandPalette } from '@jupyterlab/apputils';
911import { IFileBrowserFactory } from '@jupyterlab/filebrowser';
1012import { ILauncher } from '@jupyterlab/launcher';
···1214import { ISettingRegistry } from '@jupyterlab/settingregistry';
13151416import { BlocklyEditorFactory } from './factory';
1515-import { IBlocklyManager } from './token';
1717+import { IBlocklyRegisty } from './token';
1618import { BlocklyEditor } from './widget';
17191820import { blockly_icon } from './icons';
···3638/**
3739 * Initialization data for the jupyterlab-blocky extension.
3840 */
3939-const plugin: JupyterFrontEndPlugin<IBlocklyManager> = {
4141+const plugin: JupyterFrontEndPlugin<IBlocklyRegisty> = {
4042 id: 'jupyterlab-blocky:plugin',
4143 autoStart: true,
4244 requires: [
4345 ILayoutRestorer,
4446 IRenderMimeRegistry,
4747+ IEditorServices,
4548 IFileBrowserFactory,
4649 ISettingRegistry,
4750 ITranslator
4851 ],
4952 optional: [ILauncher, ICommandPalette],
5050- provides: IBlocklyManager,
5353+ provides: IBlocklyRegisty,
5154 activate: (
5255 app: JupyterFrontEnd,
5356 restorer: ILayoutRestorer,
5457 rendermime: IRenderMimeRegistry,
5858+ editorServices: IEditorServices,
5559 browserFactory: IFileBrowserFactory,
5660 settings: ISettingRegistry,
5761 translator: ITranslator,
5862 launcher: ILauncher | null,
5963 palette: ICommandPalette | null
6060- ): IBlocklyManager => {
6464+ ): IBlocklyRegisty => {
6165 console.log('JupyterLab extension jupyterlab-blocky is activated!');
62666367 // Namespace for the tracker
···8488 const widgetFactory = new BlocklyEditorFactory({
8589 name: FACTORY,
8690 modelName: 'text',
8787- fileTypes: ['json'],
8888- defaultFor: ['json'],
9191+ fileTypes: ['blockly'],
9292+ defaultFor: ['blockly'],
89939094 // Kernel options, in this case we need to execute the code generated
9195 // in the blockly editor. The best way would be to use kernels, for
···96100 shutdownOnClose: true,
9710198102 // The rendermime instance, necessary to render the outputs
9999- // after a code execution.
103103+ // after a code execution. And the mimeType service to get the
104104+ // mimeType from the kernel language
100105 rendermime: rendermime,
106106+ mimetypeService: editorServices.mimeTypeService,
101107102108 // The translator instance, used for the internalization of the plugin.
103109 translator: translator
···114120 });
115121 tracker.add(widget);
116122 });
123123+ // Registering the file type
124124+ app.docRegistry.addFileType({
125125+ name: 'blockly',
126126+ displayName: 'Blockly',
127127+ contentType: 'file',
128128+ fileFormat: 'json',
129129+ extensions: ['.jpblockly'],
130130+ mimeTypes: ['application/json'],
131131+ icon: jsonIcon,
132132+ iconLabel: 'JupyterLab-Blockly'
133133+ });
117134 // Registering the widget factory
118135 app.docRegistry.addWidgetFactory(widgetFactory);
119136···140157 console.log(`Current Language : '${language}'`);
141158142159 // Transmitting the current language to the manager.
143143- widgetFactory.manager.setlanguage(language);
160160+ widgetFactory.registry.setlanguage(language);
144161 });
145162146163 commands.addCommand(command, {
···187204 });
188205 }
189206190190- return widgetFactory.manager;
207207+ return widgetFactory.registry;
191208 }
192209};
193210
+44-28
src/layout.ts
···11-import { SimplifiedOutputArea, OutputAreaModel } from '@jupyterlab/outputarea';
21import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
33-import { ISessionContext } from '@jupyterlab/apputils';
44-// import { ITranslator } from '@jupyterlab/translation';
22+import { ISessionContext, showErrorMessage } from '@jupyterlab/apputils';
33+import { CodeCell, CodeCellModel } from '@jupyterlab/cells';
5465import { Message } from '@lumino/messaging';
76import { PartialJSONValue } from '@lumino/coreutils';
···2221 private _manager: BlocklyManager;
2322 private _workspace: Blockly.WorkspaceSvg;
2423 private _sessionContext: ISessionContext;
2525- // private _translator: ITranslator;
2626- private _outputArea: SimplifiedOutputArea;
2424+ private _cell: CodeCell;
27252826 /**
2927 * Construct a `BlocklyLayout`.
···3331 manager: BlocklyManager,
3432 sessionContext: ISessionContext,
3533 rendermime: IRenderMimeRegistry
3636- // translator: ITranslator
3734 ) {
3835 super();
3936 this._manager = manager;
4037 this._sessionContext = sessionContext;
4141- // this._translator = translator;
42384339 // Creating the container for the Blockly editor
4440 // and the output area to render the execution replies.
4541 this._host = document.createElement('div');
46424747- // Creating a SimplifiedOutputArea widget to render the
4343+ // Creating a CodeCell widget to render the code and
4844 // outputs from the execution reply.
4949- this._outputArea = new SimplifiedOutputArea({
5050- model: new OutputAreaModel({ trusted: true }),
4545+ this._cell = new CodeCell({
4646+ model: new CodeCellModel({}),
5147 rendermime
5248 });
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);
5355 }
54565557 get workspace(): PartialJSONValue {
···103105 run(): void {
104106 // Serializing our workspace into the chosen language generator.
105107 const code = this._manager.generator.workspaceToCode(this._workspace);
108108+ this._cell.model.sharedModel.setSource(code);
109109+ this.addWidget(this._cell);
110110+ this._resizeWorkspace();
106111107112 // Execute the code using the kernel, by using a static method from the
108113 // same class to make an execution request.
109109- SimplifiedOutputArea.execute(code, this._outputArea, this._sessionContext)
110110- .then(resp => {
111111- this.addWidget(this._outputArea);
112112- this._resizeWorkspace();
113113- })
114114- .catch(e => console.error(e));
114114+ if (this._sessionContext.hasNoKernel) {
115115+ // Check whether there is a kernel
116116+ showErrorMessage(
117117+ 'Select a valid kernel',
118118+ `There is not a valid kernel selected, select one from the dropdown menu in the toolbar.
119119+ If there isn't a valid kernel please install 'xeus-python' from Pypi.org or using mamba.
120120+ `
121121+ );
122122+ } else {
123123+ CodeCell.execute(this._cell, this._sessionContext)
124124+ .then(() => this._resizeWorkspace())
125125+ .catch(e => console.error(e));
126126+ }
115127 }
116128117129 /**
···144156 toolbox: this._manager.toolbox,
145157 theme: THEME
146158 });
147147-148148- // let categories: string;
149149-150150- // Loading the ITranslator
151151- // const trans = this._translator.load('jupyterlab-blockly');
152152-153153- // categories = trans.__('Category');
154159 }
155160156161 private _resizeWorkspace(): void {
157162 //Resize logic.
158163 const rect = this.parent.node.getBoundingClientRect();
159159- const { height } = this._outputArea.node.getBoundingClientRect();
160160- this._host.style.width = rect.width + 'px';
164164+ const { height } = this._cell.node.getBoundingClientRect();
161165 const margin = rect.height / 3;
162166163167 if (height > margin) {
164168 this._host.style.height = rect.height - margin + 'px';
165165- this._outputArea.node.style.height = margin + 'px';
166166- this._outputArea.node.style.overflowY = 'scroll';
169169+ this._cell.node.style.height = margin + 'px';
170170+ this._cell.node.style.overflowY = 'scroll';
167171 } else {
168172 this._host.style.height = rect.height - height + 'px';
169169- this._outputArea.node.style.overflowY = 'hidden';
173173+ this._cell.node.style.overflowY = 'scroll';
170174 }
171175172176 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+ }
173189 }
174190}
+121-149
src/manager.ts
···11-import { JSONObject } from '@lumino/coreutils';
11+import { ISessionContext } from '@jupyterlab/apputils';
22+import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
33+import { KernelSpec, KernelConnection } from '@jupyterlab/services';
44+import { IChangedArgs } from '@jupyterlab/coreutils';
55+26import { ISignal, Signal } from '@lumino/signaling';
77+import { JSONObject } from '@lumino/coreutils';
3849import * as Blockly from 'blockly';
51066-import BlocklyPy from 'blockly/python';
77-import * as En from 'blockly/msg/en';
88-99-import { IBlocklyManager } from './token';
1010-import { TOOLBOX } from './utils';
1111+import { BlocklyRegistry } from './registry';
11121212-export class BlocklyManager implements IBlocklyManager {
1313+/**
1414+ * BlocklyManager the manager for each document
1515+ * to select the toolbox and the generator that the
1616+ * user wants to use on a specific document.
1717+ */
1818+export class BlocklyManager {
1319 private _toolbox: JSONObject;
1414- private _activeGenerator: Blockly.Generator;
1515- private _generators: Map<string, Blockly.Generator>;
1616- private _language: string;
1717- private _changed: Signal<BlocklyManager, void>;
2020+ private _generator: Blockly.Generator;
2121+ private _registry: BlocklyRegistry;
2222+ private _selectedKernel: KernelSpec.ISpecModel;
2323+ private _sessionContext: ISessionContext;
2424+ private _mimetypeService: IEditorMimeTypeService;
2525+ private _changed: Signal<this, BlocklyManager.Change>;
18261927 /**
2020- * Constructor of BlocklyEditorFactory.
2121- *
2222- * @param options Constructor options
2828+ * Constructor of BlocklyManager.
2329 */
2424- constructor() {
2525- this._toolbox = TOOLBOX;
2626- this._activeGenerator = BlocklyPy;
2727- this._generators = new Map<string, Blockly.Generator>();
2828- this._language = 'En'; // By default we choose English.
2929- this._changed = new Signal<BlocklyManager, void>(this);
3030+ constructor(
3131+ registry: BlocklyRegistry,
3232+ sessionContext: ISessionContext,
3333+ mimetypeService: IEditorMimeTypeService
3434+ ) {
3535+ this._registry = registry;
3636+ this._sessionContext = sessionContext;
3737+ this._mimetypeService = mimetypeService;
3838+3939+ this._toolbox = this._registry.toolboxes.get('default');
4040+ this._generator = this._registry.generators.get('python');
4141+4242+ this._changed = new Signal<this, BlocklyManager.Change>(this);
4343+ this._sessionContext.kernelChanged.connect(this._onKernelChanged, this);
3044 }
31454646+ /**
4747+ * Returns the selected toolbox.
4848+ */
3249 get toolbox(): JSONObject {
3350 return this._toolbox;
3451 }
35523636- set activeGenerator(name: string) {
3737- this._activeGenerator = this._generators.get(name);
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+ }
6767+ }
6868+6969+ /**
7070+ * Returns the name of the selected kernel.
7171+ */
7272+ get kernel(): string | undefined {
7373+ return this._selectedKernel?.name || 'No kernel';
3874 }
39757676+ /**
7777+ * Returns the selected generator.
7878+ */
4079 get generator(): Blockly.Generator {
4141- return this._activeGenerator;
8080+ return this._generator;
4281 }
43824444- get changed(): ISignal<BlocklyManager, void> {
8383+ /**
8484+ * Signal triggered when the manager changes.
8585+ */
8686+ get changed(): ISignal<this, BlocklyManager.Change> {
4587 return this._changed;
4688 }
47894848- set language(language: string) {
4949- this._language = language;
9090+ /**
9191+ * Dispose.
9292+ */
9393+ dispose(): void {
9494+ this._sessionContext.kernelChanged.disconnect(this._onKernelChanged, this);
5095 }
51965252- get language(): string {
5353- return this._language;
9797+ /**
9898+ * Set the selected toolbox.
9999+ *
100100+ * @argument name The name of the toolbox.
101101+ */
102102+ setToolbox(name: string) {
103103+ this._toolbox = this._registry.toolboxes.get(name);
54104 }
551055656- registerToolbox(value: JSONObject): void {
5757- this._toolbox = value;
106106+ /**
107107+ * Set the selected kernel.
108108+ *
109109+ * @argument name The name of the kernel.
110110+ */
111111+ selectKernel(name: string) {
112112+ this._sessionContext.changeKernel({ name });
58113 }
591146060- registerBlocks(blocks: JSONObject[]): void {
6161- Blockly.defineBlocksWithJsonArray(blocks);
6262- }
6363-6464- registerGenerator(kernel: string, generator: Blockly.Generator): void {
6565- this._generators.set(kernel, generator);
115115+ /**
116116+ * Set the selected toolbox.
117117+ *
118118+ * @returns the list of available kernels for Blockly
119119+ */
120120+ listKernels(): { label: string; value: string }[] {
121121+ const specs = this._sessionContext.specsManager.specs.kernelspecs;
122122+ const list: { label: string; value: string }[] = [];
123123+ Object.keys(specs).forEach(key => {
124124+ const language = specs[key].language;
125125+ if (this._registry.generators.has(language)) {
126126+ list.push({ label: specs[key].display_name, value: specs[key].name });
127127+ }
128128+ });
129129+ return list;
66130 }
671316868- setlanguage(language: string): void {
6969- this.language = language;
7070- Private.importLanguageModule(language);
132132+ private _onKernelChanged(
133133+ sender: ISessionContext,
134134+ args: IChangedArgs<KernelConnection, KernelConnection, 'kernel'>
135135+ ): void {
136136+ const specs = this._sessionContext.specsManager.specs.kernelspecs;
137137+ if (args.newValue && specs[args.newValue.name] !== undefined) {
138138+ this._selectedKernel = specs[args.newValue.name];
139139+ const language = specs[args.newValue.name].language;
140140+ this._generator = this._registry.generators.get(language);
141141+ this._changed.emit('kernel');
142142+ }
71143 }
72144}
731457474-// Dynamically importing the language modules needed for each respective
7575-// user, in order to change the Blockly language in accordance to the
7676-// JL one.
7777-namespace Private {
7878- export async function importLanguageModule(language: string) {
7979- let module: Promise<any>;
8080- switch (language) {
8181- case 'En':
8282- module = import('blockly/msg/en');
8383- break;
8484- case 'Es':
8585- module = import('blockly/msg/es');
8686- break;
8787- case 'Fr':
8888- module = import('blockly/msg/fr');
8989- break;
9090- case 'Sa' || 'Ar':
9191- module = import('blockly/msg/ar');
9292- break;
9393- case 'Cz':
9494- module = import('blockly/msg/cs');
9595- break;
9696- case 'Dk':
9797- module = import('blockly/msg/da');
9898- break;
9999- case 'De':
100100- module = import('blockly/msg/de');
101101- break;
102102- case 'Gr':
103103- module = import('blockly/msg/el');
104104- break;
105105- case 'Ee':
106106- module = import('blockly/msg/et');
107107- break;
108108- case 'Fi':
109109- module = import('blockly/msg/fi');
110110- break;
111111- case 'Il':
112112- module = import('blockly/msg/he');
113113- break;
114114- case 'Hu':
115115- module = import('blockly/msg/hu');
116116- break;
117117- case 'Am':
118118- module = import('blockly/msg/hy');
119119- break;
120120- case 'Id':
121121- module = import('blockly/msg/id');
122122- break;
123123- case 'It':
124124- module = import('blockly/msg/it');
125125- break;
126126- case 'Jp':
127127- module = import('blockly/msg/ja');
128128- break;
129129- case 'Kr':
130130- module = import('blockly/msg/ko');
131131- break;
132132- case 'Lt':
133133- module = import('blockly/msg/lt');
134134- break;
135135- case 'Nl':
136136- module = import('blockly/msg/nl');
137137- break;
138138- case 'Pl':
139139- module = import('blockly/msg/pl');
140140- break;
141141- case 'Br':
142142- module = import('blockly/msg/pt');
143143- break;
144144- case 'Ro':
145145- module = import('blockly/msg/ro');
146146- break;
147147- case 'Ru':
148148- module = import('blockly/msg/ru');
149149- break;
150150- case 'Lk':
151151- module = import('blockly/msg/si');
152152- break;
153153- case 'Tr':
154154- module = import('blockly/msg/tr');
155155- break;
156156- case 'Ua':
157157- module = import('blockly/msg/uk');
158158- break;
159159- case 'Vn':
160160- module = import('blockly/msg/vi');
161161- break;
162162- case 'Tw':
163163- module = import('blockly/msg/zh-hant');
164164- break;
165165- case 'Cn':
166166- module = import('blockly/msg/zh-hans');
167167- break;
168168- // Complete with all the cases taken from: (last updates June 2022)
169169- // List of languages in blockly: https://github.com/google/blockly/tree/master/msg/js
170170- // List of languages in Lab: https://github.com/jupyterlab/language-packs/tree/master/language-packs
171171- default:
172172- console.warn('Language not found. Loading english');
173173- module = Promise.resolve(En);
174174- break;
175175- }
176176-177177- // Setting the current language in Blockly.
178178- module.then(lang => {
179179- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
180180- // @ts-ignore
181181- Blockly.setLocale(lang);
182182- });
183183- }
146146+/**
147147+ * BlocklyManager the manager for each document
148148+ * to select the toolbox and the generator that the
149149+ * user wants to use on a specific document.
150150+ */
151151+export namespace BlocklyManager {
152152+ /**
153153+ * The argument of the signal manager changed.
154154+ */
155155+ export type Change = 'toolbox' | 'kernel';
184156}
+205
src/registry.ts
···11+import { JSONObject } from '@lumino/coreutils';
22+33+import * as Blockly from 'blockly';
44+55+import BlocklyPy from 'blockly/python';
66+import BlocklyJS from 'blockly/javascript';
77+import BlocklyLua from 'blockly/lua';
88+99+import En from 'blockly/msg/en';
1010+1111+import { IBlocklyRegisty } from './token';
1212+import { TOOLBOX } from './utils';
1313+1414+/**
1515+ * BlocklyRegistry is the class that JupyterLab-Blockly exposes
1616+ * to other plugins. This registry allows other plugins to register
1717+ * new Toolboxes, Blocks and Generators that users can use in the
1818+ * Blockly editor.
1919+ */
2020+export class BlocklyRegistry implements IBlocklyRegisty {
2121+ private _toolboxes: Map<string, JSONObject>;
2222+ private _generators: Map<string, Blockly.Generator>;
2323+2424+ /**
2525+ * Constructor of BlocklyRegistry.
2626+ */
2727+ constructor() {
2828+ this._toolboxes = new Map<string, JSONObject>();
2929+ this._toolboxes.set('default', TOOLBOX);
3030+3131+ this._generators = new Map<string, Blockly.Generator>();
3232+ this._generators.set('python', BlocklyPy);
3333+ this._generators.set('javascript', BlocklyJS);
3434+ this._generators.set('lua', BlocklyLua);
3535+ }
3636+3737+ /**
3838+ * Returns a map with all the toolboxes.
3939+ */
4040+ get toolboxes(): Map<string, JSONObject> {
4141+ return this._toolboxes;
4242+ }
4343+4444+ /**
4545+ * Returns a map with all the generators.
4646+ */
4747+ get generators(): Map<string, Blockly.Generator> {
4848+ return this._generators;
4949+ }
5050+5151+ /**
5252+ * Register a toolbox for the editor.
5353+ *
5454+ * @argument name Name of the toolbox.
5555+ *
5656+ * @argument value Toolbox to register.
5757+ */
5858+ registerToolbox(name: string, value: JSONObject): void {
5959+ this._toolboxes.set(name, value);
6060+ }
6161+6262+ /**
6363+ * Register new blocks.
6464+ *
6565+ * @argument name Name of the toolbox.
6666+ *
6767+ * @argument value Toolbox to register.
6868+ */
6969+ registerBlocks(blocks: JSONObject[]): void {
7070+ Blockly.defineBlocksWithJsonArray(blocks);
7171+ }
7272+7373+ /**
7474+ * Register new generators.
7575+ *
7676+ * @argument name Name of the toolbox.
7777+ *
7878+ * @argument value Toolbox to register.
7979+ *
8080+ * #### Notes
8181+ * When registering a generator, the name should correspond to the language
8282+ * used by a kernel.
8383+ *
8484+ * If you register a generator for an existing language this will be overwritten.
8585+ */
8686+ registerGenerator(name: string, generator: Blockly.Generator): void {
8787+ this._generators.set(name, generator);
8888+ }
8989+9090+ setlanguage(language: string): void {
9191+ Private.importLanguageModule(language);
9292+ }
9393+}
9494+9595+namespace Private {
9696+ // Dynamically importing the language modules needed for each respective
9797+ // user, in order to change the Blockly language in accordance to the
9898+ // JL one.
9999+ export async function importLanguageModule(language: string) {
100100+ let module: Promise<any>;
101101+ switch (language) {
102102+ case 'En':
103103+ module = import('blockly/msg/en');
104104+ break;
105105+ case 'Es':
106106+ module = import('blockly/msg/es');
107107+ break;
108108+ case 'Fr':
109109+ module = import('blockly/msg/fr');
110110+ break;
111111+ case 'Sa' || 'Ar':
112112+ module = import('blockly/msg/ar');
113113+ break;
114114+ case 'Cz':
115115+ module = import('blockly/msg/cs');
116116+ break;
117117+ case 'Dk':
118118+ module = import('blockly/msg/da');
119119+ break;
120120+ case 'De':
121121+ module = import('blockly/msg/de');
122122+ break;
123123+ case 'Gr':
124124+ module = import('blockly/msg/el');
125125+ break;
126126+ case 'Ee':
127127+ module = import('blockly/msg/et');
128128+ break;
129129+ case 'Fi':
130130+ module = import('blockly/msg/fi');
131131+ break;
132132+ case 'Il':
133133+ module = import('blockly/msg/he');
134134+ break;
135135+ case 'Hu':
136136+ module = import('blockly/msg/hu');
137137+ break;
138138+ case 'Am':
139139+ module = import('blockly/msg/hy');
140140+ break;
141141+ case 'Id':
142142+ module = import('blockly/msg/id');
143143+ break;
144144+ case 'It':
145145+ module = import('blockly/msg/it');
146146+ break;
147147+ case 'Jp':
148148+ module = import('blockly/msg/ja');
149149+ break;
150150+ case 'Kr':
151151+ module = import('blockly/msg/ko');
152152+ break;
153153+ case 'Lt':
154154+ module = import('blockly/msg/lt');
155155+ break;
156156+ case 'Nl':
157157+ module = import('blockly/msg/nl');
158158+ break;
159159+ case 'Pl':
160160+ module = import('blockly/msg/pl');
161161+ break;
162162+ case 'Br':
163163+ module = import('blockly/msg/pt');
164164+ break;
165165+ case 'Ro':
166166+ module = import('blockly/msg/ro');
167167+ break;
168168+ case 'Ru':
169169+ module = import('blockly/msg/ru');
170170+ break;
171171+ case 'Lk':
172172+ module = import('blockly/msg/si');
173173+ break;
174174+ case 'Tr':
175175+ module = import('blockly/msg/tr');
176176+ break;
177177+ case 'Ua':
178178+ module = import('blockly/msg/uk');
179179+ break;
180180+ case 'Vn':
181181+ module = import('blockly/msg/vi');
182182+ break;
183183+ case 'Tw':
184184+ module = import('blockly/msg/zh-hant');
185185+ break;
186186+ case 'Cn':
187187+ module = import('blockly/msg/zh-hans');
188188+ break;
189189+ default:
190190+ // Complete with all the cases taken from: (last updates June 2022)
191191+ // List of languages in blockly: https://github.com/google/blockly/tree/master/msg/js
192192+ // List of languages in Lab: https://github.com/jupyterlab/language-packs/tree/master/language-packs
193193+ console.warn('Language not found. Loading english');
194194+ module = Promise.resolve(En);
195195+ break;
196196+ }
197197+198198+ // Setting the current language in Blockly.
199199+ module.then(lang => {
200200+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
201201+ // @ts-ignore
202202+ Blockly.setLocale(lang);
203203+ });
204204+ }
205205+}
+42-8
src/token.ts
···11-import { Token } from '@lumino/coreutils';
22-import { JSONObject } from '@lumino/coreutils';
11+import { Token, JSONObject } from '@lumino/coreutils';
3243import * as Blockly from 'blockly';
5465/**
77- * The manager token.
66+ * The registry token.
87 */
99-export const IBlocklyManager = new Token<IBlocklyManager>(
1010- 'jupyterlab-blockly/manager'
88+export const IBlocklyRegisty = new Token<IBlocklyRegisty>(
99+ 'jupyterlab-blockly/registry'
1110);
12111313-export interface IBlocklyManager {
1414- registerToolbox(value: JSONObject): void;
1212+/**
1313+ * BlocklyRegistry is the class that JupyterLab-Blockly exposes
1414+ * to other plugins. This registry allows other plugins to register
1515+ * new Toolboxes, Blocks and Generators that users can use in the
1616+ * Blockly editor.
1717+ */
1818+export interface IBlocklyRegisty {
1919+ /**
2020+ * Register a toolbox for the editor.
2121+ *
2222+ * @argument name Name of the toolbox.
2323+ *
2424+ * @argument value Toolbox to register.
2525+ */
2626+ registerToolbox(name: string, value: JSONObject): void;
2727+2828+ /**
2929+ * Register new blocks.
3030+ *
3131+ * @argument name Name of the toolbox.
3232+ *
3333+ * @argument value Toolbox to register.
3434+ */
1535 registerBlocks(blocks: JSONObject[]): void;
1616- registerGenerator(kernel: string, generator: Blockly.Generator): void;
3636+3737+ /**
3838+ * Register new generators.
3939+ *
4040+ * @argument name Name of the toolbox.
4141+ *
4242+ * @argument value Toolbox to register.
4343+ *
4444+ * #### Notes
4545+ * When registering a generator, the name should correspond to the language
4646+ * used by a kernel.
4747+ *
4848+ * If you register a generator for an existing language this will be overwritten.
4949+ */
5050+ registerGenerator(name: string, generator: Blockly.Generator): void;
1751}
+2
src/toolbar/index.ts
···11+export * from './selector';
22+export * from './utils';