···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';
89import { ICommandPalette } from '@jupyterlab/apputils';
···1213import { ISettingRegistry } from '@jupyterlab/settingregistry';
13141415import { BlocklyEditorFactory } from './factory';
1515-import { IBlocklyManager } from './token';
1616+import { IBlocklyRegisty } from './token';
1617import { BlocklyEditor } from './widget';
17181819import { blockly_icon } from './icons';
···3637/**
3738 * Initialization data for the jupyterlab-blocky extension.
3839 */
3939-const plugin: JupyterFrontEndPlugin<IBlocklyManager> = {
4040+const plugin: JupyterFrontEndPlugin<IBlocklyRegisty> = {
4041 id: 'jupyterlab-blocky:plugin',
4142 autoStart: true,
4243 requires: [
···4748 ITranslator
4849 ],
4950 optional: [ILauncher, ICommandPalette],
5050- provides: IBlocklyManager,
5151+ provides: IBlocklyRegisty,
5152 activate: (
5253 app: JupyterFrontEnd,
5354 restorer: ILayoutRestorer,
···5758 translator: ITranslator,
5859 launcher: ILauncher | null,
5960 palette: ICommandPalette | null
6060- ): IBlocklyManager => {
6161+ ): IBlocklyRegisty => {
6162 console.log('JupyterLab extension jupyterlab-blocky is activated!');
62636364 // Namespace for the tracker
···8485 const widgetFactory = new BlocklyEditorFactory({
8586 name: FACTORY,
8687 modelName: 'text',
8787- fileTypes: ['json'],
8888- defaultFor: ['json'],
8888+ fileTypes: ['blockly'],
8989+ defaultFor: ['blockly'],
89909091 // Kernel options, in this case we need to execute the code generated
9192 // in the blockly editor. The best way would be to use kernels, for
···114115 });
115116 tracker.add(widget);
116117 });
118118+ // Registering the file type
119119+ app.docRegistry.addFileType({
120120+ name: 'blockly',
121121+ displayName: 'Blockly',
122122+ contentType: 'file',
123123+ fileFormat: 'json',
124124+ extensions: ['.jpblockly'],
125125+ mimeTypes: ['application/json'],
126126+ icon: jsonIcon,
127127+ iconLabel: 'JupyterLab-Blockly'
128128+ });
117129 // Registering the widget factory
118130 app.docRegistry.addWidgetFactory(widgetFactory);
119131···140152 console.log(`Current Language : '${language}'`);
141153142154 // Transmitting the current language to the manager.
143143- widgetFactory.manager.setlanguage(language);
155155+ widgetFactory.registry.setlanguage(language);
144156 });
145157146158 commands.addCommand(command, {
···187199 });
188200 }
189201190190- return widgetFactory.manager;
202202+ return widgetFactory.registry;
191203 }
192204};
193205
+17-18
src/layout.ts
···11import { SimplifiedOutputArea, OutputAreaModel } from '@jupyterlab/outputarea';
22import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
33-import { ISessionContext } from '@jupyterlab/apputils';
44-// import { ITranslator } from '@jupyterlab/translation';
33+import { ISessionContext, showErrorMessage } from '@jupyterlab/apputils';
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;
2624 private _outputArea: SimplifiedOutputArea;
27252826 /**
···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.
···106102107103 // Execute the code using the kernel, by using a static method from the
108104 // 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));
105105+ if (this._sessionContext.hasNoKernel) {
106106+ // Check whether there is a kernel
107107+ showErrorMessage(
108108+ 'Select a valid kernel',
109109+ `There is not a valid kernel selected, select one from the dropdown menu in the toolbar.
110110+ If there isn't a valid kernel please install 'xeus-python' from Pypi.org or using mamba.
111111+ `
112112+ );
113113+ } else {
114114+ SimplifiedOutputArea.execute(code, this._outputArea, this._sessionContext)
115115+ .then(resp => {
116116+ this.addWidget(this._outputArea);
117117+ this._resizeWorkspace();
118118+ })
119119+ .catch(e => console.error(e));
120120+ }
115121 }
116122117123 /**
···144150 toolbox: this._manager.toolbox,
145151 theme: THEME
146152 });
147147-148148- // let categories: string;
149149-150150- // Loading the ITranslator
151151- // const trans = this._translator.load('jupyterlab-blockly');
152152-153153- // categories = trans.__('Category');
154153 }
155154156155 private _resizeWorkspace(): void {
+98-149
src/manager.ts
···11-import { JSONObject } from '@lumino/coreutils';
11+import { ISessionContext } from '@jupyterlab/apputils';
22+import { KernelSpec, KernelConnection } from '@jupyterlab/services';
33+import { IChangedArgs } from '@jupyterlab/coreutils';
44+25import { ISignal, Signal } from '@lumino/signaling';
66+import { JSONObject } from '@lumino/coreutils';
3748import * as Blockly from 'blockly';
5966-import BlocklyPy from 'blockly/python';
77-import * as En from 'blockly/msg/en';
1010+import { BlocklyRegistry } from './registry';
81199-import { IBlocklyManager } from './token';
1010-import { TOOLBOX } from './utils';
1111-1212-export class BlocklyManager implements IBlocklyManager {
1212+/**
1313+ * BlocklyManager the manager for each document
1414+ * to select the toolbox and the generator that the
1515+ * user wants to use on a specific document.
1616+ */
1717+export class BlocklyManager {
1318 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>;
1919+ private _generator: Blockly.Generator;
2020+ private _registry: BlocklyRegistry;
2121+ private _selectedKernel: KernelSpec.ISpecModel;
2222+ private _sessionContext: ISessionContext;
2323+ private _changed: Signal<this, BlocklyManager.Change>;
18241925 /**
2020- * Constructor of BlocklyEditorFactory.
2121- *
2222- * @param options Constructor options
2626+ * Constructor of BlocklyManager.
2327 */
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);
2828+ constructor(registry: BlocklyRegistry, sessionContext: ISessionContext) {
2929+ this._registry = registry;
3030+ this._sessionContext = sessionContext;
3131+3232+ this._toolbox = this._registry.toolboxes.get('default');
3333+ this._generator = this._registry.generators.get('python');
3434+3535+ this._changed = new Signal<this, BlocklyManager.Change>(this);
3636+ this._sessionContext.kernelChanged.connect(this._onKernelChanged, this);
3037 }
31383939+ /**
4040+ * Returns the selected toolbox.
4141+ */
3242 get toolbox(): JSONObject {
3343 return this._toolbox;
3444 }
35453636- set activeGenerator(name: string) {
3737- this._activeGenerator = this._generators.get(name);
4646+ /**
4747+ * Returns the name of the selected kernel.
4848+ */
4949+ get kernel(): string | undefined {
5050+ return this._selectedKernel?.name || 'No kernel';
3851 }
39525353+ /**
5454+ * Returns the selected generator.
5555+ */
4056 get generator(): Blockly.Generator {
4141- return this._activeGenerator;
5757+ return this._generator;
4258 }
43594444- get changed(): ISignal<BlocklyManager, void> {
6060+ /**
6161+ * Signal triggered when the manager changes.
6262+ */
6363+ get changed(): ISignal<this, BlocklyManager.Change> {
4564 return this._changed;
4665 }
47664848- set language(language: string) {
4949- this._language = language;
6767+ /**
6868+ * Dispose.
6969+ */
7070+ dispose(): void {
7171+ this._sessionContext.kernelChanged.disconnect(this._onKernelChanged, this);
5072 }
51735252- get language(): string {
5353- return this._language;
7474+ /**
7575+ * Set the selected toolbox.
7676+ *
7777+ * @argument name The name of the toolbox.
7878+ */
7979+ setToolbox(name: string) {
8080+ this._toolbox = this._registry.toolboxes.get(name);
5481 }
55825656- registerToolbox(value: JSONObject): void {
5757- this._toolbox = value;
8383+ /**
8484+ * Set the selected kernel.
8585+ *
8686+ * @argument name The name of the kernel.
8787+ */
8888+ selectKernel(name: string) {
8989+ this._sessionContext.changeKernel({ name });
5890 }
59916060- registerBlocks(blocks: JSONObject[]): void {
6161- Blockly.defineBlocksWithJsonArray(blocks);
9292+ /**
9393+ * Set the selected toolbox.
9494+ *
9595+ * @returns the list of available kernels for Blockly
9696+ */
9797+ listKernels(): { label: string; value: string }[] {
9898+ const specs = this._sessionContext.specsManager.specs.kernelspecs;
9999+ const list: { label: string; value: string }[] = [];
100100+ Object.keys(specs).forEach(key => {
101101+ const language = specs[key].language;
102102+ if (this._registry.generators.has(language)) {
103103+ list.push({ label: specs[key].display_name, value: specs[key].name });
104104+ }
105105+ });
106106+ return list;
62107 }
631086464- registerGenerator(kernel: string, generator: Blockly.Generator): void {
6565- this._generators.set(kernel, generator);
6666- }
6767-6868- setlanguage(language: string): void {
6969- this.language = language;
7070- Private.importLanguageModule(language);
109109+ private _onKernelChanged(
110110+ sender: ISessionContext,
111111+ args: IChangedArgs<KernelConnection, KernelConnection, 'kernel'>
112112+ ): void {
113113+ const specs = this._sessionContext.specsManager.specs.kernelspecs;
114114+ if (args.newValue && specs[args.newValue.name] !== undefined) {
115115+ this._selectedKernel = specs[args.newValue.name];
116116+ const language = specs[args.newValue.name].language;
117117+ this._generator = this._registry.generators.get(language);
118118+ this._changed.emit('kernel');
119119+ }
71120 }
72121}
731227474-// 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- }
123123+/**
124124+ * BlocklyManager the manager for each document
125125+ * to select the toolbox and the generator that the
126126+ * user wants to use on a specific document.
127127+ */
128128+export namespace BlocklyManager {
129129+ /**
130130+ * The argument of the signal manager changed.
131131+ */
132132+ export type Change = 'toolbox' | 'kernel';
184133}
+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';