From 5c08463430c34576aaa20ffd8eeae7db3a9b0609 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 15 Jan 2024 11:38:02 +0100 Subject: [PATCH 1/5] Add save handler for Panel Layout Builder --- src/manager.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- src/plugin.ts | 9 +++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/manager.ts b/src/manager.ts index 3be9282..4a8503e 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -1,27 +1,72 @@ import { IDisposable } from '@lumino/disposable'; +import { JSONExt } from '@lumino/coreutils'; + +import { + URLExt, + PageConfig, +} from '@jupyterlab/coreutils'; import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { Kernel } from '@jupyterlab/services'; +import { Kernel, ServerConnection } from '@jupyterlab/services'; + +import { JupyterFrontEnd } from '@jupyterlab/application'; + +const fetch = ServerConnection.makeRequest; + +const API_ROOT = URLExt.join(PageConfig.getBaseUrl(), '/panel-preview/'); +const API_LAYOUT = URLExt.join(API_ROOT, '/layout/'); /** * A micro manager that contains the document context */ export class ContextManager implements IDisposable { _wManager: any; + private _app: JupyterFrontEnd; private _context: DocumentRegistry.IContext | null; private _comm: Kernel.IComm | undefined; constructor( + app: JupyterFrontEnd, context: DocumentRegistry.IContext, manager: any ) { + this._app = app; this._context = context; this._wManager = manager; this._comm = undefined; + context.saveState.connect( + async (context: any, status: string) => { + if (status != 'started') { + return + } + const layout_path = URLExt.join(API_LAYOUT, context.path) + let response = await fetch(layout_path, {method: 'GET'}, this._app.serviceManager.serverSettings) + if (response.status !== 200) + return + const layout = await response.json() + let changed = false; + for (const cell of context.model.cells) { + const cell_layout = layout.cells[cell.id] + const cell_meta = cell.getMetadata() + if (!JSONExt.deepEqual(cell_meta['panel-layout'], cell_layout)) { + cell.setMetadata('panel-layout', cell_layout) + changed = true + } + } + const nb_meta = context.model.getMetadata() + if (!JSONExt.deepEqual(nb_meta['panel-cell-order'], layout.order)) { + context.model.setMetadata('panel-cell-order', layout.order) + changed = true + } + if (changed) { + context.save() + } + } + ) context.sessionContext.statusChanged.connect( - (session: any, status: any) => { + (session: any, status: string) => { if (status === 'restarting' || status === 'dead') { this._comm = undefined; } diff --git a/src/plugin.ts b/src/plugin.ts index b087348..2d05539 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -178,6 +178,11 @@ class LumenRenderButton export class NBWidgetExtension implements INBWidgetExtension { _docmanager!: IDocumentManager; + _app: JupyterFrontEnd; + + constructor(app: JupyterFrontEnd) { + this._app = app + } createNew( nb: NotebookPanel, @@ -196,7 +201,7 @@ export class NBWidgetExtension implements INBWidgetExtension { ] as any); } - const manager = new ContextManager(context, renderer.manager); + const manager = new ContextManager(this._app, context, renderer.manager); nb.content.rendermime.addFactory( { @@ -248,7 +253,7 @@ export const extension: JupyterFrontEndPlugin = { menu: IMainMenu | null, settingRegistry: ISettingRegistry | null ) => { - const nb_extension = new NBWidgetExtension(); + const nb_extension = new NBWidgetExtension(app); nb_extension._docmanager = docmanager; app.docRegistry.addWidgetExtension('Notebook', nb_extension); From 16fdcac390a654cc37dcf94b380efe3a821e1346 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 15 Jan 2024 13:33:48 +0100 Subject: [PATCH 2/5] Run prettier --- package.json | 248 ++++++++++++++++++++++++------------------------- src/manager.ts | 62 ++++++------- src/plugin.ts | 2 +- 3 files changed, 155 insertions(+), 157 deletions(-) diff --git a/package.json b/package.json index b0c89eb..5043895 100644 --- a/package.json +++ b/package.json @@ -1,127 +1,127 @@ { - "name": "@pyviz/jupyterlab_pyviz", - "version": "3.0.0", - "description": "A JupyterLab extension for rendering HoloViz content.", - "keywords": [ - "jupyter", - "jupyterlab", - "jupyterlab-extension" - ], - "homepage": "https://github.com/holoviz/pyviz_comms", - "bugs": { - "url": "https://github.com/holoviz/pyviz_comms/issues" - }, - "license": "BSD-3-Clause", - "author": { - "name": "Philipp Rudiger" - }, - "files": [ - "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", - "style/**/*.{css,.js,eot,gif,html,jpg,json,png,svg,woff2,ttf}", - "schema/*.json", - "style/index.js" - ], - "main": "lib/index.js", - "types": "lib/index.d.ts", - "style": "style/index.css", - "repository": { - "type": "git", - "url": "https://github.com/holoviz/pyviz_comms.git" - }, - "scripts": { - "build": "jlpm build:lib && jlpm build:labextension:dev", - "build:labextension": "jupyter labextension build .", - "build:labextension:dev": "jupyter labextension build --development True .", - "build:lib": "tsc --sourceMap", - "build:lib:prod": "tsc", - "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension", - "clean": "jlpm clean:lib", - "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache", - "clean:labextension": "rimraf pyviz_comms/labextension pyviz_comms/_version.py", - "clean:lib": "rimraf lib tsconfig.tsbuildinfo", - "clean:lintcache": "rimraf .eslintcache .stylelintcache", - "eslint": "jlpm eslint:check --fix", - "eslint:check": "eslint . --cache --ext .ts,.tsx", - "install:extension": "jlpm build", - "lint": "jlpm prettier && jlpm eslint", - "lint:check": "jlpm prettier:check && jlpm eslint:check", - "prettier": "jlpm prettier:base --write --list-different", - "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"", - "prettier:check": "jlpm prettier:base --check", - "stylelint": "jlpm stylelint:check --fix", - "stylelint:check": "stylelint --cache \"style/**/*.css\"", - "watch": "run-p watch:src watch:labextension", - "watch:labextension": "jupyter labextension watch .", - "watch:src": "tsc -w --sourceMap" - }, - "dependencies": { - "@jupyterlab/application": "^4.0.3", - "@jupyterlab/apputils": "^4.1.3", - "@jupyterlab/coreutils": "^6.0.3", - "@jupyterlab/docregistry": "^4.0.3", - "@jupyterlab/fileeditor": "^4.0.3", - "@jupyterlab/mainmenu": "^4.0.3", - "@jupyterlab/notebook": "^4.0.3", - "@jupyterlab/settingregistry": "^4.0.3", - "@jupyterlab/ui-components": "^4.0.3", - "@lumino/coreutils": "^2.1.1", - "@lumino/signaling": "^2.1.1", - "tippy.js": "^6" - }, - "resolutions": { - "@lumino/widgets": "^2.1.1", - "react": "^17.0.1", - "react-dom": "^17.0.1" - }, - "peerDependencies": { - "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6", - "@jupyter-widgets/jupyterlab-manager": "^5.0.4" - }, - "devDependencies": { - "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6", - "@jupyter-widgets/jupyterlab-manager": "^5.0.7", - "@jupyterlab/builder": "^4.0.0", - "@jupyterlab/testutils": "^3.0.0", - "@types/json-schema": "^7.0.11", - "@types/node": "^14.14.16", - "@types/react": "^18.0.26", - "@types/react-dom": "^17.0.0", - "@typescript-eslint/eslint-plugin": "^5.55.0", - "@typescript-eslint/parser": "^5.55.0", - "css-loader": "^6.7.1", - "eslint": "^8.36.0", - "eslint-config-prettier": "^8.8.0", - "eslint-plugin-prettier": "^5.0.0", - "npm-run-all": "^4.1.5", - "prettier": "^3.0.0", - "rimraf": "^4.4.1", - "source-map-loader": "^1.0.2", - "style-loader": "^3.3.1", - "stylelint": "^15.10.1", - "stylelint-config-recommended": "^13.0.0", - "stylelint-config-standard": "^34.0.0", - "stylelint-prettier": "^4.0.0", - "typescript": "~5.0.2", - "yjs": "^13.5.40" - }, - "sideEffects": [ - "style/*.css", - "style/index.js" - ], - "styleModule": "style/index.js", - "jupyterlab": { - "extension": true, - "outputDir": "pyviz_comms/labextension", - "schemaDir": "schema", - "sharedPackages": { - "@jupyter-widgets/jupyterlab-manager": { - "bundled": false, - "singleton": true - }, - "@jupyter-widgets/base": { - "bundled": false, - "singleton": true - } - } + "name": "@pyviz/jupyterlab_pyviz", + "version": "3.0.0", + "description": "A JupyterLab extension for rendering HoloViz content.", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "homepage": "https://github.com/holoviz/pyviz_comms", + "bugs": { + "url": "https://github.com/holoviz/pyviz_comms/issues" + }, + "license": "BSD-3-Clause", + "author": { + "name": "Philipp Rudiger" + }, + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,.js,eot,gif,html,jpg,json,png,svg,woff2,ttf}", + "schema/*.json", + "style/index.js" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "style": "style/index.css", + "repository": { + "type": "git", + "url": "https://github.com/holoviz/pyviz_comms.git" + }, + "scripts": { + "build": "jlpm build:lib && jlpm build:labextension:dev", + "build:labextension": "jupyter labextension build .", + "build:labextension:dev": "jupyter labextension build --development True .", + "build:lib": "tsc --sourceMap", + "build:lib:prod": "tsc", + "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension", + "clean": "jlpm clean:lib", + "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache", + "clean:labextension": "rimraf pyviz_comms/labextension pyviz_comms/_version.py", + "clean:lib": "rimraf lib tsconfig.tsbuildinfo", + "clean:lintcache": "rimraf .eslintcache .stylelintcache", + "eslint": "jlpm eslint:check --fix", + "eslint:check": "eslint . --cache --ext .ts,.tsx", + "install:extension": "jlpm build", + "lint": "jlpm prettier && jlpm eslint", + "lint:check": "jlpm prettier:check && jlpm eslint:check", + "prettier": "jlpm prettier:base --write --list-different", + "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"", + "prettier:check": "jlpm prettier:base --check", + "stylelint": "jlpm stylelint:check --fix", + "stylelint:check": "stylelint --cache \"style/**/*.css\"", + "watch": "run-p watch:src watch:labextension", + "watch:labextension": "jupyter labextension watch .", + "watch:src": "tsc -w --sourceMap" + }, + "dependencies": { + "@jupyterlab/application": "^4.0.3", + "@jupyterlab/apputils": "^4.1.3", + "@jupyterlab/coreutils": "^6.0.3", + "@jupyterlab/docregistry": "^4.0.3", + "@jupyterlab/fileeditor": "^4.0.3", + "@jupyterlab/mainmenu": "^4.0.3", + "@jupyterlab/notebook": "^4.0.3", + "@jupyterlab/settingregistry": "^4.0.3", + "@jupyterlab/ui-components": "^4.0.3", + "@lumino/coreutils": "^2.1.1", + "@lumino/signaling": "^2.1.1", + "tippy.js": "^6" + }, + "resolutions": { + "@lumino/widgets": "^2.1.1", + "react": "^17.0.1", + "react-dom": "^17.0.1" + }, + "peerDependencies": { + "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6", + "@jupyter-widgets/jupyterlab-manager": "^5.0.4" + }, + "devDependencies": { + "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6", + "@jupyter-widgets/jupyterlab-manager": "^5.0.7", + "@jupyterlab/builder": "^4.0.0", + "@jupyterlab/testutils": "^3.0.0", + "@types/json-schema": "^7.0.11", + "@types/node": "^14.14.16", + "@types/react": "^18.0.26", + "@types/react-dom": "^17.0.0", + "@typescript-eslint/eslint-plugin": "^5.55.0", + "@typescript-eslint/parser": "^5.55.0", + "css-loader": "^6.7.1", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0", + "npm-run-all": "^4.1.5", + "prettier": "^3.0.0", + "rimraf": "^4.4.1", + "source-map-loader": "^1.0.2", + "style-loader": "^3.3.1", + "stylelint": "^15.10.1", + "stylelint-config-recommended": "^13.0.0", + "stylelint-config-standard": "^34.0.0", + "stylelint-prettier": "^4.0.0", + "typescript": "~5.0.2", + "yjs": "^13.5.40" + }, + "sideEffects": [ + "style/*.css", + "style/index.js" + ], + "styleModule": "style/index.js", + "jupyterlab": { + "extension": true, + "outputDir": "pyviz_comms/labextension", + "schemaDir": "schema", + "sharedPackages": { + "@jupyter-widgets/jupyterlab-manager": { + "bundled": false, + "singleton": true + }, + "@jupyter-widgets/base": { + "bundled": false, + "singleton": true + } } + } } diff --git a/src/manager.ts b/src/manager.ts index 4a8503e..d2b0b22 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -1,10 +1,7 @@ import { IDisposable } from '@lumino/disposable'; import { JSONExt } from '@lumino/coreutils'; -import { - URLExt, - PageConfig, -} from '@jupyterlab/coreutils'; +import { URLExt, PageConfig } from '@jupyterlab/coreutils'; import { DocumentRegistry } from '@jupyterlab/docregistry'; @@ -36,35 +33,36 @@ export class ContextManager implements IDisposable { this._wManager = manager; this._comm = undefined; - context.saveState.connect( - async (context: any, status: string) => { - if (status != 'started') { - return - } - const layout_path = URLExt.join(API_LAYOUT, context.path) - let response = await fetch(layout_path, {method: 'GET'}, this._app.serviceManager.serverSettings) - if (response.status !== 200) - return - const layout = await response.json() - let changed = false; - for (const cell of context.model.cells) { - const cell_layout = layout.cells[cell.id] - const cell_meta = cell.getMetadata() - if (!JSONExt.deepEqual(cell_meta['panel-layout'], cell_layout)) { - cell.setMetadata('panel-layout', cell_layout) - changed = true - } - } - const nb_meta = context.model.getMetadata() - if (!JSONExt.deepEqual(nb_meta['panel-cell-order'], layout.order)) { - context.model.setMetadata('panel-cell-order', layout.order) - changed = true - } - if (changed) { - context.save() - } + context.saveState.connect(async (context: any, status: string) => { + if (status != 'started') { + return; } - ) + const layout_path = URLExt.join(API_LAYOUT, context.path); + let response = await fetch( + layout_path, + { method: 'GET' }, + this._app.serviceManager.serverSettings + ); + if (response.status !== 200) return; + const layout = await response.json(); + let changed = false; + for (const cell of context.model.cells) { + const cell_layout = layout.cells[cell.id]; + const cell_meta = cell.getMetadata(); + if (!JSONExt.deepEqual(cell_meta['panel-layout'], cell_layout)) { + cell.setMetadata('panel-layout', cell_layout); + changed = true; + } + } + const nb_meta = context.model.getMetadata(); + if (!JSONExt.deepEqual(nb_meta['panel-cell-order'], layout.order)) { + context.model.setMetadata('panel-cell-order', layout.order); + changed = true; + } + if (changed) { + context.save(); + } + }); context.sessionContext.statusChanged.connect( (session: any, status: string) => { if (status === 'restarting' || status === 'dead') { diff --git a/src/plugin.ts b/src/plugin.ts index 2d05539..8ef7495 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -181,7 +181,7 @@ export class NBWidgetExtension implements INBWidgetExtension { _app: JupyterFrontEnd; constructor(app: JupyterFrontEnd) { - this._app = app + this._app = app; } createNew( From d18e205bb60e14039ad3200b23a03447cedcec80 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 15 Jan 2024 15:42:06 +0100 Subject: [PATCH 3/5] Linting fixes --- src/manager.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/manager.ts b/src/manager.ts index d2b0b22..e591595 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -34,16 +34,18 @@ export class ContextManager implements IDisposable { this._comm = undefined; context.saveState.connect(async (context: any, status: string) => { - if (status != 'started') { + if (status !== 'started') { return; } const layout_path = URLExt.join(API_LAYOUT, context.path); - let response = await fetch( + const response = await fetch( layout_path, { method: 'GET' }, this._app.serviceManager.serverSettings ); - if (response.status !== 200) return; + if (response.status !== 200) { + return; + } const layout = await response.json(); let changed = false; for (const cell of context.model.cells) { From 6764c9f35bce46f4da0277c1f81def8dab44a94d Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 15 Jan 2024 16:30:19 +0100 Subject: [PATCH 4/5] Run prettier again --- src/manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manager.ts b/src/manager.ts index e591595..423d329 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -44,7 +44,7 @@ export class ContextManager implements IDisposable { this._app.serviceManager.serverSettings ); if (response.status !== 200) { - return; + return; } const layout = await response.json(); let changed = false; From d06cbd58e503558b20be6248c24b95359bbe759d Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 15 Jan 2024 16:33:01 +0100 Subject: [PATCH 5/5] Fix README badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 932b071..e07858d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pyviz_comms -![Github Actions Status](https://github.com/holoviz/pyviz_comms/workflows/test/badge.svg) +![Github Actions Status](https://github.com/holoviz/pyviz_comms/workflows/tests/badge.svg) Offers a simple bidirectional communication architecture between Python and JavaScript, with support for Jupyter comms in both the classic notebook and Jupyterlab.