Skip to content

Commit

Permalink
Add save handler for Panel Layout Builder (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Jan 15, 2024
1 parent 9df3d7d commit f559573
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 129 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
248 changes: 124 additions & 124 deletions package.json
Original file line number Diff line number Diff line change
@@ -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
}
}
}
}
49 changes: 47 additions & 2 deletions src/manager.ts
Original file line number Diff line number Diff line change
@@ -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<DocumentRegistry.IModel> | null;
private _comm: Kernel.IComm | undefined;

constructor(
app: JupyterFrontEnd,
context: DocumentRegistry.IContext<DocumentRegistry.IModel>,
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);
const 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;
}
Expand Down
9 changes: 7 additions & 2 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ class LumenRenderButton

export class NBWidgetExtension implements INBWidgetExtension {
_docmanager!: IDocumentManager;
_app: JupyterFrontEnd;

constructor(app: JupyterFrontEnd) {
this._app = app;
}

createNew(
nb: NotebookPanel,
Expand All @@ -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(
{
Expand Down Expand Up @@ -248,7 +253,7 @@ export const extension: JupyterFrontEndPlugin<IPanelPreviewTracker> = {
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);

Expand Down

0 comments on commit f559573

Please sign in to comment.