Skip to content

Store selected kernel and toolbox in .jpblockly file #103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 84 additions & 1 deletion packages/blockly/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import { ISignal, Signal } from '@lumino/signaling';
import * as Blockly from 'blockly';

import { BlocklyRegistry } from './registry';
import { ToolboxDefinition } from 'blockly/core/utils/toolbox';
import {
BlockInfo,
DynamicCategoryInfo,
StaticCategoryInfo,
ToolboxDefinition,
ToolboxInfo,
ToolboxItemInfo
} from 'blockly/core/utils/toolbox';

/**
* BlocklyManager the manager for each document
Expand All @@ -17,6 +24,7 @@ import { ToolboxDefinition } from 'blockly/core/utils/toolbox';
*/
export class BlocklyManager {
private _toolbox: string;
private _allowedBlocks: string[];
private _generator: Blockly.Generator;
private _registry: BlocklyRegistry;
private _selectedKernel: KernelSpec.ISpecModel;
Expand All @@ -37,6 +45,7 @@ export class BlocklyManager {
this._mimetypeService = mimetypeService;

this._toolbox = 'default';
this._filterToolbox();
this._generator = this._registry.generators.get('python');

this._changed = new Signal<this, BlocklyManager.Change>(this);
Expand Down Expand Up @@ -112,6 +121,7 @@ export class BlocklyManager {
if (this._toolbox !== name) {
const toolbox = this._registry.toolboxes.get(name);
this._toolbox = toolbox ? name : 'default';
this._filterToolbox();
this._changed.emit('toolbox');
}
}
Expand All @@ -129,6 +139,79 @@ export class BlocklyManager {
return list;
}

/**
* Get the list of allowed blocks. If undefined, all blocks are allowed.
*
* @returns The list of allowed blocks.
*/
getAllowedBlocks() {
return this._allowedBlocks;
}

/**
* Set the list of allowed blocks. If undefined, all blocks are allowed.
*
* @param allowedBlocks The list of allowed blocks.
*/
setAllowedBlocks(allowedBlocks: string[]) {
this._allowedBlocks = allowedBlocks;
this._filterToolbox();
this._changed.emit('toolbox');
}

private _filterToolbox() {
const toolbox = this._registry.toolboxes.get(this._toolbox) as ToolboxInfo;
if (toolbox) {
this._filterContents(toolbox.contents);
}
}

private _filterContents(contents: ToolboxItemInfo[]): number {
let visible = 0;
contents.forEach(itemInfo => {
if ('kind' in itemInfo) {
if (itemInfo.kind.toUpperCase() === 'CATEGORY') {
if ('contents' in itemInfo) {
const categoryInfo = itemInfo as StaticCategoryInfo;
if (this._filterContents(categoryInfo.contents) > 0) {
visible++;
categoryInfo.hidden = 'false';
} else {
categoryInfo.hidden = 'true';
}
} else if ('custom' in itemInfo) {
const categoryInfo = itemInfo as DynamicCategoryInfo;
if (
this._allowedBlocks === undefined ||
this._allowedBlocks.includes(categoryInfo.custom.toLowerCase())
) {
categoryInfo.hidden = 'false';
visible++;
console.log(`Category ${categoryInfo.custom} is allowed`);
} else {
categoryInfo.hidden = 'true';
console.log(`Category ${categoryInfo.custom} is not allowed`);
}
}
} else if (itemInfo.kind.toUpperCase() === 'BLOCK') {
const blockInfo = itemInfo as BlockInfo;
if (
this._allowedBlocks === undefined ||
this._allowedBlocks.includes(blockInfo.type.toLowerCase())
) {
blockInfo.disabled = false;
blockInfo.disabledReasons = [];
visible++;
} else {
blockInfo.disabled = true;
blockInfo.disabledReasons = ['This block is not allowed'];
}
}
}
});
return visible;
}

/**
* Set the selected kernel.
*
Expand Down
67 changes: 63 additions & 4 deletions packages/blockly/src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
} from '@jupyterlab/docregistry';
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { runIcon } from '@jupyterlab/ui-components';
import { showErrorMessage } from '@jupyterlab/apputils';

import { PartialJSONObject } from '@lumino/coreutils';
import { SplitPanel } from '@lumino/widgets';
import { Signal } from '@lumino/signaling';

Expand Down Expand Up @@ -82,6 +84,7 @@ export namespace BlocklyEditor {
*/
export class BlocklyPanel extends SplitPanel {
private _context: DocumentRegistry.IContext<DocumentModel>;
private _manager: BlocklyManager;
private _rendermime: IRenderMimeRegistry;

/**
Expand All @@ -105,6 +108,7 @@ export class BlocklyPanel extends SplitPanel {
});
this.addClass('jp-BlocklyPanel');
this._context = context;
this._manager = manager;
this._rendermime = rendermime;

// Load the content of the file when the context is ready
Expand Down Expand Up @@ -139,9 +143,55 @@ export class BlocklyPanel extends SplitPanel {
}

private _load(): void {
// Loading the content of the document into the workspace
const content = this._context.model.toJSON() as any as Blockly.Workspace;
(this.layout as BlocklyLayout).workspace = content;
const fileContent = this._context.model.toJSON();
const fileFormat = fileContent['format'];
// Check if format is set or if we have legacy content
if (fileFormat === undefined && fileContent['blocks']) {
// Load legacy content
(this.layout as BlocklyLayout).workspace =
fileContent as any as Blockly.Workspace;
} else if (fileFormat === 2) {
// Load the content from the "workspace" key
const workspace = fileContent['workspace'] as any as Blockly.Workspace;
(this.layout as BlocklyLayout).workspace = workspace;
const metadata = fileContent['metadata'];
if (metadata) {
if (metadata['toolbox']) {
const toolbox = metadata['toolbox'];
if (
this._manager.listToolboxes().find(value => value.value === toolbox)
) {
this._manager.setToolbox(metadata['toolbox']);
} else {
// Unknown toolbox
showErrorMessage(
'Unknown toolbox',
`The toolbox '${toolbox}' is not available. Using default toolbox.`
);
}
}
if (metadata['kernel'] && metadata['kernel'] !== 'No kernel') {
const kernel = metadata['kernel'];
if (
this._manager.listKernels().find(value => value.value === kernel)
) {
this._manager.selectKernel(metadata['kernel']);
} else {
// Unknown kernel
console.warn(`Unknown kernel in blockly file: ${kernel}`);
}
}
if (metadata['allowed_blocks']) {
this._manager.setAllowedBlocks(metadata['allowed_blocks']);
}
}
} else {
// Unsupported format
showErrorMessage(
'Unsupported file format',
`The file format '${fileFormat}' is not supported by the Blockly editor.`
);
}
}

private _onSave(
Expand All @@ -150,7 +200,16 @@ export class BlocklyPanel extends SplitPanel {
): void {
if (state === 'started') {
const workspace = (this.layout as BlocklyLayout).workspace;
this._context.model.fromJSON(workspace as any);
const fileContent: PartialJSONObject = {
format: 2,
workspace: workspace as any,
metadata: {
toolbox: this._manager.getToolbox(),
allowed_blocks: this._manager.getAllowedBlocks(),
kernel: this._manager.kernel
}
};
this._context.model.fromJSON(fileContent);
}
}
}
Loading