Skip to content

Commit 94adec5

Browse files
committed
Adds a manager to allow other extensions to add more blocks or change the toolbox. Also generates and executes code
1 parent 3e326e2 commit 94adec5

File tree

8 files changed

+563
-315
lines changed

8 files changed

+563
-315
lines changed

package.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,20 @@
4545
"watch:labextension": "jupyter labextension watch ."
4646
},
4747
"dependencies": {
48-
"@jupyterlab/application": "^4.0.0-alpha.4",
49-
"@jupyterlab/apputils": "^4.0.0-alpha.4",
50-
"@jupyterlab/docregistry": "^4.0.0-alpha.4",
48+
"@jupyterlab/application": "^4.0.0-alpha.7",
49+
"@jupyterlab/apputils": "^4.0.0-alpha.7",
50+
"@jupyterlab/docregistry": "^4.0.0-alpha.7",
51+
"@jupyterlab/outputarea": "^4.0.0-alpha.7",
52+
"@jupyterlab/rendermime": "^4.0.0-alpha.7",
53+
"@lumino/algorithm": "^1.9.1",
54+
"@lumino/coreutils": "^1.12.0",
55+
"@lumino/messaging": "^1.10.1",
5156
"@lumino/signaling": "^1.10.1",
52-
"@lumino/widgets": "^1.31.0",
57+
"@lumino/widgets": "^1.31.1",
5358
"blockly": "^7.20211209.2"
5459
},
5560
"devDependencies": {
56-
"@jupyterlab/builder": "^4.0.0-alpha.4",
61+
"@jupyterlab/builder": "^4.0.0-alpha.7",
5762
"@typescript-eslint/eslint-plugin": "^5.12.1",
5863
"@typescript-eslint/parser": "^5.12.1",
5964
"eslint": "^8.9.0",

src/factory.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,34 @@ import {
33
DocumentRegistry,
44
DocumentModel
55
} from '@jupyterlab/docregistry';
6+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
67

78
import { BlocklyEditor, BlocklyPanel } from './widget';
9+
import { BlocklyManager } from './manager';
810

911
/**
10-
* A widget factory to create new instances of ExampleDocWidget.
12+
* A widget factory to create new instances of BlocklyEditor.
1113
*/
1214
export class BlocklyEditorFactory extends ABCWidgetFactory<
1315
BlocklyEditor,
1416
DocumentModel
1517
> {
18+
private _manager: BlocklyManager;
19+
private _rendermime: IRenderMimeRegistry;
20+
1621
/**
17-
* Constructor of ExampleWidgetFactory.
22+
* Constructor of BlocklyEditorFactory.
1823
*
1924
* @param options Constructor options
2025
*/
21-
constructor(options: DocumentRegistry.IWidgetFactoryOptions) {
26+
constructor(options: BlocklyEditorFactory.IOptions) {
2227
super(options);
28+
this._manager = new BlocklyManager();
29+
this._rendermime = options.rendermime;
30+
}
31+
32+
get manager(): BlocklyManager {
33+
return this._manager;
2334
}
2435

2536
/**
@@ -33,7 +44,16 @@ export class BlocklyEditorFactory extends ABCWidgetFactory<
3344
): BlocklyEditor {
3445
return new BlocklyEditor({
3546
context,
36-
content: new BlocklyPanel(context)
47+
content: new BlocklyPanel(context, this._manager, this._rendermime)
3748
});
3849
}
3950
}
51+
52+
export namespace BlocklyEditorFactory {
53+
export interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
54+
/*
55+
* A rendermime instance.
56+
*/
57+
rendermime: IRenderMimeRegistry;
58+
}
59+
}

src/index.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import {
33
JupyterFrontEndPlugin,
44
ILayoutRestorer
55
} from '@jupyterlab/application';
6-
76
import { WidgetTracker } from '@jupyterlab/apputils';
7+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
88

99
import { BlocklyEditorFactory } from './factory';
10-
10+
import { IBlocklyManager } from './token';
1111
import { BlocklyEditor } from './widget';
1212

1313
/**
@@ -18,11 +18,16 @@ const FACTORY = 'Blockly editor';
1818
/**
1919
* Initialization data for the jupyterlab-blocky extension.
2020
*/
21-
const plugin: JupyterFrontEndPlugin<void> = {
21+
const plugin: JupyterFrontEndPlugin<IBlocklyManager> = {
2222
id: 'jupyterlab-blocky:plugin',
2323
autoStart: true,
24-
requires: [ILayoutRestorer],
25-
activate: (app: JupyterFrontEnd, restorer: ILayoutRestorer) => {
24+
requires: [ILayoutRestorer, IRenderMimeRegistry],
25+
provides: IBlocklyManager,
26+
activate: (
27+
app: JupyterFrontEnd,
28+
restorer: ILayoutRestorer,
29+
rendermime: IRenderMimeRegistry
30+
): IBlocklyManager => {
2631
console.log('JupyterLab extension jupyterlab-blocky is activated!');
2732

2833
// Namespace for the tracker
@@ -46,7 +51,12 @@ const plugin: JupyterFrontEndPlugin<void> = {
4651
const widgetFactory = new BlocklyEditorFactory({
4752
name: FACTORY,
4853
modelName: 'text',
49-
fileTypes: ['json']
54+
fileTypes: ['json'],
55+
defaultFor: ['json'],
56+
canStartKernel: true,
57+
preferKernel: true,
58+
shutdownOnClose: true,
59+
rendermime: rendermime
5060
});
5161

5262
// Add the widget to the tracker when it's created
@@ -59,6 +69,8 @@ const plugin: JupyterFrontEndPlugin<void> = {
5969
});
6070
// Registering the widget factory
6171
app.docRegistry.addWidgetFactory(widgetFactory);
72+
73+
return widgetFactory.manager;
6274
}
6375
};
6476

src/layout.ts

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,45 @@
1-
import { Layout, Widget } from '@lumino/widgets';
1+
import { SimplifiedOutputArea, OutputAreaModel } from '@jupyterlab/outputarea';
2+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
3+
import { ISessionContext } from '@jupyterlab/apputils';
24

5+
import { Message } from '@lumino/messaging';
36
import { PartialJSONValue } from '@lumino/coreutils';
4-
7+
import { PanelLayout, Widget } from '@lumino/widgets';
58
import { IIterator, ArrayIterator } from '@lumino/algorithm';
69

7-
import { Message } from '@lumino/messaging';
8-
910
import * as Blockly from 'blockly';
1011

11-
import { TOOLBOX } from './utils';
12+
import { BlocklyManager } from './manager';
1213

1314
/**
1415
* A blockly layout to host the Blockly editor.
1516
*/
16-
export class BlocklyLayout extends Layout {
17-
private _workspace: Blockly.WorkspaceSvg;
17+
export class BlocklyLayout extends PanelLayout {
1818
private _host: HTMLElement;
19+
private _manager: BlocklyManager;
20+
private _workspace: Blockly.WorkspaceSvg;
21+
private _sessionContext: ISessionContext;
22+
private _outputArea: SimplifiedOutputArea;
1923

2024
/**
2125
* Construct a `BlocklyLayout`.
2226
*
2327
*/
24-
constructor() {
28+
constructor(
29+
manager: BlocklyManager,
30+
sessionContext: ISessionContext,
31+
rendermime: IRenderMimeRegistry
32+
) {
2533
super();
26-
console.debug('[BlocklyLayout]');
34+
this._manager = manager;
35+
this._sessionContext = sessionContext;
2736

2837
// Creating the container for the Blockly editor
2938
this._host = document.createElement('div');
30-
this._host.className = 'grid-stack';
39+
this._outputArea = new SimplifiedOutputArea({
40+
model: new OutputAreaModel({ trusted: true }),
41+
rendermime
42+
});
3143
}
3244

3345
get workspace(): PartialJSONValue {
@@ -55,9 +67,8 @@ export class BlocklyLayout extends Layout {
5567
*/
5668
init(): void {
5769
super.init();
58-
console.debug('[BlocklyLayout] init');
5970
// Add the blockly container into the DOM
60-
this.parent!.node.appendChild(this._host);
71+
this.addWidget(new Widget({ node: this._host }));
6172
}
6273

6374
/**
@@ -76,49 +87,61 @@ export class BlocklyLayout extends Layout {
7687
return;
7788
}
7889

90+
run(): void {
91+
const code = this._manager.generator.workspaceToCode(this._workspace);
92+
SimplifiedOutputArea.execute(code, this._outputArea, this._sessionContext)
93+
.then(resp => {
94+
this.addWidget(this._outputArea);
95+
this._resizeWorkspace();
96+
})
97+
.catch(e => console.error(e));
98+
}
99+
79100
/**
80101
* Handle `update-request` messages sent to the widget.
81102
*/
82103
protected onUpdateRequest(msg: Message): void {
83-
console.debug('[BlocklyLayout] onUpdateRequest');
84-
// TODO: write the resize logic
104+
this._resizeWorkspace();
85105
}
86106

87107
/**
88108
* Handle `resize-request` messages sent to the widget.
89109
*/
90110
protected onResize(msg: Message): void {
91-
console.debug('[BlocklyLayout] onResize');
92-
// TODO: write the resize logic
93-
const rect = this.parent.node.getBoundingClientRect();
94-
this._host.style.width = rect.width + 'px';
95-
this._host.style.height = rect.height + 'px';
96-
Blockly.svgResize(this._workspace);
111+
this._resizeWorkspace();
97112
}
98113

99114
/**
100115
* Handle `fit-request` messages sent to the widget.
101116
*/
102117
protected onFitRequest(msg: Message): void {
103-
console.debug('[BlocklyLayout] onFitRequest');
104-
// TODO: write the resize logic
105-
//
118+
this._resizeWorkspace();
106119
}
107120

108121
/**
109122
* Handle `after-attach` messages sent to the widget.
110123
*/
111124
protected onAfterAttach(msg: Message): void {
112-
console.debug('[BlocklyLayout] onAfterAttach');
113125
this._workspace = Blockly.inject(this._host, {
114-
toolbox: TOOLBOX
126+
toolbox: this._manager.toolbox
115127
});
116128
}
117129

118-
/**
119-
* Handle `after-show` messages sent to the widget.
120-
*/
121-
protected onAfterShow(msg: Message): void {
122-
console.debug('[BlocklyLayout] onAfterShow');
130+
private _resizeWorkspace(): void {
131+
const rect = this.parent.node.getBoundingClientRect();
132+
const { height } = this._outputArea.node.getBoundingClientRect();
133+
this._host.style.width = rect.width + 'px';
134+
const margin = rect.height / 3;
135+
136+
if (height > margin) {
137+
this._host.style.height = rect.height - margin + 'px';
138+
this._outputArea.node.style.height = margin + 'px';
139+
this._outputArea.node.style.overflowY = 'scroll';
140+
} else {
141+
this._host.style.height = rect.height - height + 'px';
142+
this._outputArea.node.style.overflowY = 'hidden';
143+
}
144+
145+
Blockly.svgResize(this._workspace);
123146
}
124147
}

src/manager.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { JSONObject } from '@lumino/coreutils';
2+
3+
import * as Blockly from 'blockly';
4+
5+
import BlocklyPy from 'blockly/python';
6+
7+
import { IBlocklyManager } from './token';
8+
import { TOOLBOX } from './utils';
9+
10+
export class BlocklyManager implements IBlocklyManager {
11+
private _toolbox: JSONObject;
12+
private _activeGenerator: Blockly.Generator;
13+
private _generators: Map<string, Blockly.Generator>;
14+
15+
/**
16+
* Constructor of BlocklyEditorFactory.
17+
*
18+
* @param options Constructor options
19+
*/
20+
constructor() {
21+
this._toolbox = TOOLBOX;
22+
this._activeGenerator = BlocklyPy;
23+
this._generators = new Map<string, Blockly.Generator>();
24+
}
25+
26+
get toolbox(): JSONObject {
27+
return this._toolbox;
28+
}
29+
30+
set activeGenerator(name: string) {
31+
this._activeGenerator = this._generators.get(name);
32+
}
33+
34+
get generator(): Blockly.Generator {
35+
return this._activeGenerator;
36+
}
37+
38+
registerToolbox(value: JSONObject): void {
39+
this._toolbox = value;
40+
}
41+
42+
registerBlocks(blocks: JSONObject[]): void {
43+
Blockly.defineBlocksWithJsonArray(blocks);
44+
}
45+
46+
registerGenerator(kernel: string, generator: Blockly.Generator): void {
47+
this._generators.set(kernel, generator);
48+
}
49+
}

src/token.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Token } from '@lumino/coreutils';
2+
import { JSONObject } from '@lumino/coreutils';
3+
4+
import * as Blockly from 'blockly';
5+
6+
/**
7+
* The manager token.
8+
*/
9+
export const IBlocklyManager = new Token<IBlocklyManager>(
10+
'jupyterlab-blockly/manager'
11+
);
12+
13+
export interface IBlocklyManager {
14+
registerToolbox(value: JSONObject): void;
15+
registerBlocks(blocks: JSONObject[]): void;
16+
registerGenerator(kernel: string, generator: Blockly.Generator): void;
17+
}

0 commit comments

Comments
 (0)