···11BSD 3-Clause License
2233-Copyright (c) 2022, quantstack
33+Copyright (c) 2024, quantstack
44All rights reserved.
5566Redistribution and use in source and binary forms, with or without
···6677### Python package
8899-This extension can be distributed as Python
1010-packages. All of the Python
1111-packaging instructions in the `pyproject.toml` file to wrap your extension in a
1212-Python package. Before generating a package, we first need to install `build`.
99+This extension can be distributed as Python packages. All of the Python
1010+packaging instructions are in the `pyproject.toml` file to wrap your extension in a
1111+Python package. Before generating a package, you first need to install some tools:
1212+1313+```bash
1414+pip install build twine hatch
1515+```
1616+1717+Bump the version using `hatch`. By default this will create a tag.
1818+See the docs on [hatch-nodejs-version](https://github.com/agoose77/hatch-nodejs-version#semver) for details.
1919+2020+```bash
2121+hatch version <new-version>
2222+```
2323+2424+Make sure to clean up all the development files before building the package:
13251426```bash
1515-pip install build twine
2727+jlpm clean:all
2828+```
2929+3030+You could also clean up the local git repository:
3131+3232+```bash
3333+git clean -dfX
1634```
17351836To create a Python source package (`.tar.gz`) and the binary package (`.whl`) in the `dist/` directory, do:
···40584159## Automated releases with the Jupyter Releaser
42604343-The extension repository should already be compatible with the Jupyter Releaser.
4444-4545-Check out the [workflow documentation](https://github.com/jupyter-server/jupyter_releaser#typical-workflow) for more information.
6161+The extension repository should already be compatible with the Jupyter Releaser. But
6262+the GitHub repository and the package managers need to be properly set up. Please
6363+follow the instructions of the Jupyter Releaser [checklist](https://jupyter-releaser.readthedocs.io/en/latest/how_to_guides/convert_repo_from_repo.html).
46644765Here is a summary of the steps to cut a new release:
48664949-- Fork the [`jupyter-releaser` repo](https://github.com/jupyter-server/jupyter_releaser)
5050-- Add `ADMIN_GITHUB_TOKEN`, `PYPI_TOKEN` and `NPM_TOKEN` to the Github Secrets in the fork
5167- Go to the Actions panel
5252-- Run the "Draft Changelog" workflow
5353- 
5454-- Merge the Changelog PR
5555- 
5656-- Using the info from the draft changelong PR, run the "Full Release" workflow
5757- 
5858- 
6868+- Run the "Step 1: Prep Release" workflow
6969+- Check the draft changelog
7070+- Run the "Step 2: Publish Release" workflow
7171+7272+> [!NOTE]
7373+> Check out the [workflow documentation](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html)
7474+> for more information.
59756076## Publishing to `conda-forge`
6177
···1212import { ILauncher } from '@jupyterlab/launcher';
1313import { ITranslator } from '@jupyterlab/translation';
1414import { ISettingRegistry } from '@jupyterlab/settingregistry';
1515-import { IKernelMenu, IMainMenu } from '@jupyterlab/mainmenu';
1515+import { IMainMenu } from '@jupyterlab/mainmenu';
1616+import { ReadonlyPartialJSONObject } from '@lumino/coreutils';
16171718import { IJupyterWidgetRegistry } from '@jupyter-widgets/base';
1819···36373738namespace CommandIDs {
3839 export const createNew = 'blockly:create-new-blockly-file';
4040+ export const interruptKernel = 'blockly:interrupt-kernel';
4141+ export const reconnectToKernel = 'blockly:reconnect-to-kernel';
4242+ export const shutdownKernel = 'blockly:shutdown-kernel';
4343+ export const restartKernel = 'blockly:restart-kernel';
3944}
40454146/**
···110115111116 // The rendermime instance, necessary to render the outputs
112117 // after a code execution. And the mimeType service to get the
113113- // mimeType from the kernel language
118118+ // mimeType from the kernel language, as well as the factory service
119119+ // for the cell content factory.
114120 rendermime: rendermime,
115121 mimetypeService: editorServices.mimeTypeService,
122122+ factoryService: editorServices.factoryService,
116123117124 // The translator instance, used for the internalization of the plugin.
118125 translator: translator
···213220 });
214221 }
215222223223+ /**
224224+ * Whether there is an active Blockly Editor.
225225+ */
226226+ function isEnabled(): boolean {
227227+ return (
228228+ tracker.currentWidget !== null &&
229229+ tracker.currentWidget === app.shell.currentWidget
230230+ );
231231+ }
232232+233233+ // Get the current widget and activate unless the args specify otherwise.
234234+ function getCurrent(args: ReadonlyPartialJSONObject): BlocklyEditor | null {
235235+ const widget = tracker.currentWidget;
236236+ const activate = args['activate'] !== false;
237237+ if (activate && widget) {
238238+ app.shell.activateById(widget.id);
239239+ }
240240+ return widget ?? null;
241241+ }
242242+243243+ // Register kernel commands.
244244+ commands.addCommand(CommandIDs.interruptKernel, {
245245+ label: 'Interrupt Kernel',
246246+ execute: args => {
247247+ const current = getCurrent(args);
248248+ if (!current) {
249249+ return;
250250+ }
251251+ const kernel = current.context.sessionContext.session?.kernel;
252252+ if (kernel) {
253253+ return kernel.interrupt();
254254+ }
255255+ return Promise.resolve(void 0);
256256+ },
257257+ isEnabled
258258+ });
259259+260260+ commands.addCommand(CommandIDs.restartKernel, {
261261+ label: 'Restart Kernel…',
262262+ execute: args => {
263263+ const current = getCurrent(args);
264264+ if (!current) {
265265+ return;
266266+ }
267267+ const kernel = current.context.sessionContext.session?.kernel;
268268+ if (kernel) {
269269+ return kernel.restart();
270270+ }
271271+ return Promise.resolve(void 0);
272272+ },
273273+ isEnabled
274274+ });
275275+276276+ commands.addCommand(CommandIDs.shutdownKernel, {
277277+ label: 'Shut Down Kernel',
278278+ execute: args => {
279279+ const current = getCurrent(args);
280280+ if (!current) {
281281+ return;
282282+ }
283283+ return current.context.sessionContext.shutdown();
284284+ }
285285+ });
286286+287287+ commands.addCommand(CommandIDs.reconnectToKernel, {
288288+ label: 'Reconnect to Kernel',
289289+ execute: args => {
290290+ const current = getCurrent(args);
291291+ if (!current) {
292292+ return;
293293+ }
294294+ const kernel = current.context.sessionContext.session?.kernel;
295295+ if (kernel) {
296296+ return kernel.reconnect();
297297+ }
298298+ return Promise.resolve(void 0);
299299+ }
300300+ });
301301+216302 // Add the command to the main menu
217303 if (mainMenu) {
218218- mainMenu.kernelMenu.kernelUsers.add({
219219- tracker,
220220- interruptKernel: current => {
221221- const kernel = current.context.sessionContext.session?.kernel;
222222- if (kernel) {
223223- return kernel.interrupt();
224224- }
225225- return Promise.resolve(void 0);
226226- },
227227- reconnectToKernel: current => {
228228- const kernel = current.context.sessionContext.session?.kernel;
229229- if (kernel) {
230230- return kernel.reconnect();
231231- }
232232- return Promise.resolve(void 0);
233233- },
234234- restartKernel: current => {
235235- const kernel = current.context.sessionContext.session?.kernel;
236236- if (kernel) {
237237- return kernel.restart();
238238- }
239239- return Promise.resolve(void 0);
240240- },
241241- shutdownKernel: current => current.context.sessionContext.shutdown()
242242- } as IKernelMenu.IKernelUser<BlocklyEditor>);
304304+ mainMenu.kernelMenu.kernelUsers.interruptKernel.add({
305305+ id: CommandIDs.interruptKernel,
306306+ isEnabled
307307+ });
308308+309309+ mainMenu.kernelMenu.kernelUsers.reconnectToKernel.add({
310310+ id: CommandIDs.reconnectToKernel,
311311+ isEnabled
312312+ });
313313+314314+ mainMenu.kernelMenu.kernelUsers.restartKernel.add({
315315+ id: CommandIDs.restartKernel,
316316+ isEnabled
317317+ });
318318+319319+ mainMenu.kernelMenu.kernelUsers.shutdownKernel.add({
320320+ id: CommandIDs.shutdownKernel,
321321+ isEnabled
322322+ });
243323 }
244324245325 if (widgetRegistry) {
+1-1
packages/blockly-extension/src/svg.d.ts
···88// The svg will be imported as a raw string
991010declare module '*.svg' {
1111- const value: string;
1111+ const value: string; // @ts-ignore
1212 export default value;
1313}
···44 DocumentModel
55} from '@jupyterlab/docregistry';
66import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
77-import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
77+import {
88+ IEditorFactoryService,
99+ IEditorMimeTypeService
1010+} from '@jupyterlab/codeeditor';
811912import { BlocklyEditor, BlocklyPanel } from './widget';
1013import { BlocklyRegistry } from './registry';
···2023 private _registry: BlocklyRegistry;
2124 private _rendermime: IRenderMimeRegistry;
2225 private _mimetypeService: IEditorMimeTypeService;
2626+ private _factoryService: IEditorFactoryService;
23272428 /**
2529 * Constructor of BlocklyEditorFactory.
···3135 this._registry = new BlocklyRegistry();
3236 this._rendermime = options.rendermime;
3337 this._mimetypeService = options.mimetypeService;
3838+ this._factoryService = options.factoryService;
3439 }
35403641 get registry(): BlocklyRegistry {
···5459 context.sessionContext,
5560 this._mimetypeService
5661 );
5757- const content = new BlocklyPanel(context, manager, this._rendermime);
6262+ const content = new BlocklyPanel(
6363+ context,
6464+ manager,
6565+ this._rendermime,
6666+ this._factoryService
6767+ );
5868 return new BlocklyEditor({ context, content, manager });
5969 }
6070}
···6979 * A mimeType service instance.
7080 */
7181 mimetypeService: IEditorMimeTypeService;
8282+ /**
8383+ * An editor factory service instance.
8484+ */
8585+ factoryService: IEditorFactoryService;
7286 }
7387}
+14-8
packages/blockly/src/layout.ts
···11import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
22import { ISessionContext, showErrorMessage } from '@jupyterlab/apputils';
33-import { CodeCell, CodeCellModel } from '@jupyterlab/cells';
33+import { Cell, CodeCell, CodeCellModel } from '@jupyterlab/cells';
44+import { IEditorFactoryService } from '@jupyterlab/codeeditor';
4556import { Message } from '@lumino/messaging';
67import { SplitLayout, SplitPanel, Widget } from '@lumino/widgets';
77-import { IIterator, ArrayIterator } from '@lumino/algorithm';
88import { Signal } from '@lumino/signaling';
991010import * as Blockly from 'blockly';
···2929 constructor(
3030 manager: BlocklyManager,
3131 sessionContext: ISessionContext,
3232- rendermime: IRenderMimeRegistry
3232+ rendermime: IRenderMimeRegistry,
3333+ factoryService: IEditorFactoryService
3334 ) {
3435 super({ renderer: SplitPanel.defaultRenderer, orientation: 'vertical' });
3536 this._manager = manager;
···4243 // Creating a CodeCell widget to render the code and
4344 // outputs from the execution reply.
4445 this._cell = new CodeCell({
4545- model: new CodeCellModel({}),
4646- rendermime
4747- });
4646+ contentFactory: new Cell.ContentFactory({
4747+ editorFactory: factoryService.newInlineEditor
4848+ }),
4949+ model: new CodeCellModel(),
5050+ rendermime,
5151+ placeholder: false
5252+ }).initializeState();
5353+4854 // Trust the outputs and set the mimeType for the code
4955 this._cell.addClass('jp-blockly-codeCell');
5056 this._cell.readOnly = true;
···106112 /**
107113 * Create an iterator over the widgets in the layout.
108114 */
109109- iter(): IIterator<Widget> {
110110- return new ArrayIterator([]);
115115+ iter(): IterableIterator<Widget> {
116116+ return [][Symbol.iterator]();
111117 }
112118113119 /**
+9-1
packages/blockly/src/registry.ts
···44import { javascriptGenerator } from 'blockly/javascript';
55import { luaGenerator } from 'blockly/lua';
6677-import En from 'blockly/msg/en';
77+import * as En from 'blockly/msg/en';
8899import { IBlocklyRegistry } from './token';
1010import { TOOLBOX } from './utils';
1111import type { ToolboxDefinition } from 'blockly/core/utils/toolbox';
1212import { BlockDefinition } from 'blockly/core/blocks';
1313+import { installAllBlocks } from '@blockly/field-colour';
13141415/**
1516 * BlocklyRegistry is the class that JupyterLab-Blockly exposes
···3233 this._generators.set('python', pythonGenerator);
3334 this._generators.set('javascript', javascriptGenerator);
3435 this._generators.set('lua', luaGenerator);
3636+3737+ // register color blocks with their respective language generators
3838+ installAllBlocks({
3939+ javascript: javascriptGenerator,
4040+ python: pythonGenerator,
4141+ lua: luaGenerator
4242+ });
3543 }
36443745 /**
+9-2
packages/blockly/src/widget.ts
···2020 Spacer
2121} from './toolbar';
2222import { CodeCell } from '@jupyterlab/cells';
2323+import { IEditorFactoryService } from '@jupyterlab/codeeditor';
23242425/**
2526 * DocumentWidget: widget that represents the view or editor for a file type.
···9192 constructor(
9293 context: DocumentRegistry.IContext<DocumentModel>,
9394 manager: BlocklyManager,
9494- rendermime: IRenderMimeRegistry
9595+ rendermime: IRenderMimeRegistry,
9696+ factoryService: IEditorFactoryService
9597 ) {
9698 super({
9797- layout: new BlocklyLayout(manager, context.sessionContext, rendermime)
9999+ layout: new BlocklyLayout(
100100+ manager,
101101+ context.sessionContext,
102102+ rendermime,
103103+ factoryService
104104+ )
98105 });
99106 this.addClass('jp-BlocklyPanel');
100107 this._context = context;