jupyterpack 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +79 -0
  3. package/lib/document/iframePanel.d.ts +7 -0
  4. package/lib/document/iframePanel.js +20 -0
  5. package/lib/document/jupyterpackDocWidget.d.ts +9 -0
  6. package/lib/document/jupyterpackDocWidget.js +16 -0
  7. package/lib/document/plugin.d.ts +2 -0
  8. package/lib/document/plugin.js +31 -0
  9. package/lib/document/widgetFactory.d.ts +23 -0
  10. package/lib/document/widgetFactory.js +60 -0
  11. package/lib/index.d.ts +2 -0
  12. package/lib/index.js +3 -0
  13. package/lib/pythonWidget/connectionManager.d.ts +18 -0
  14. package/lib/pythonWidget/connectionManager.js +27 -0
  15. package/lib/pythonWidget/kernelExecutor.d.ts +27 -0
  16. package/lib/pythonWidget/kernelExecutor.js +104 -0
  17. package/lib/pythonWidget/pythonWidget.d.ts +13 -0
  18. package/lib/pythonWidget/pythonWidget.js +22 -0
  19. package/lib/pythonWidget/pythonWidgetModel.d.ts +29 -0
  20. package/lib/pythonWidget/pythonWidgetModel.js +75 -0
  21. package/lib/sandpackWidget/sandpackFilesModel.d.ts +27 -0
  22. package/lib/sandpackWidget/sandpackFilesModel.js +114 -0
  23. package/lib/sandpackWidget/sandpackPanel.d.ts +16 -0
  24. package/lib/sandpackWidget/sandpackPanel.js +52 -0
  25. package/lib/swConnection/comm_manager.d.ts +6 -0
  26. package/lib/swConnection/comm_manager.js +46 -0
  27. package/lib/swConnection/connection_manager.d.ts +18 -0
  28. package/lib/swConnection/connection_manager.js +27 -0
  29. package/lib/swConnection/index.d.ts +3 -0
  30. package/lib/swConnection/index.js +68 -0
  31. package/lib/swConnection/sw.d.ts +1 -0
  32. package/lib/swConnection/sw.js +49 -0
  33. package/lib/token.d.ts +3 -0
  34. package/lib/token.js +2 -0
  35. package/lib/tools.d.ts +2 -0
  36. package/lib/tools.js +17 -0
  37. package/lib/type.d.ts +38 -0
  38. package/lib/type.js +9 -0
  39. package/package.json +199 -0
  40. package/src/document/iframePanel.ts +25 -0
  41. package/src/document/jupyterpackDocWidget.ts +19 -0
  42. package/src/document/plugin.ts +44 -0
  43. package/src/document/widgetFactory.ts +79 -0
  44. package/src/index.ts +4 -0
  45. package/src/pythonWidget/connectionManager.ts +43 -0
  46. package/src/pythonWidget/kernelExecutor.ts +140 -0
  47. package/src/pythonWidget/pythonWidget.ts +34 -0
  48. package/src/pythonWidget/pythonWidgetModel.ts +107 -0
  49. package/src/sandpackWidget/sandpackFilesModel.ts +141 -0
  50. package/src/sandpackWidget/sandpackPanel.ts +82 -0
  51. package/src/swConnection/comm_manager.ts +53 -0
  52. package/src/swConnection/connection_manager.ts +43 -0
  53. package/src/swConnection/index.ts +93 -0
  54. package/src/swConnection/sw.ts +57 -0
  55. package/src/token.ts +6 -0
  56. package/src/tools.ts +18 -0
  57. package/src/type.ts +44 -0
  58. package/style/base.css +51 -0
  59. package/style/index.css +1 -0
  60. package/style/index.js +1 -0
package/package.json ADDED
@@ -0,0 +1,199 @@
1
+ {
2
+ "name": "jupyterpack",
3
+ "version": "0.2.0",
4
+ "description": "A JupyterLab extension for serving web app.",
5
+ "keywords": [
6
+ "jupyter",
7
+ "jupyterlab",
8
+ "jupyterlab-extension"
9
+ ],
10
+ "homepage": "https://github.com/trungleduc/jupyterpack",
11
+ "bugs": {
12
+ "url": "https://github.com/trungleduc/jupyterpack/issues"
13
+ },
14
+ "license": "BSD-3-Clause",
15
+ "author": {
16
+ "name": "Trung Le",
17
+ "email": "leductrungxf@gmail.com"
18
+ },
19
+ "files": [
20
+ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
21
+ "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
22
+ "src/**/*.{ts,tsx}"
23
+ ],
24
+ "main": "lib/index.js",
25
+ "types": "lib/index.d.ts",
26
+ "style": "style/index.css",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/trungleduc/jupyterpack.git"
30
+ },
31
+ "scripts": {
32
+ "build:demo": "cd demo && rm -rf .jupyterlite.doit.db _output && jupyter lite build .",
33
+ "update:demo": "node script/update-demo.mjs",
34
+ "build:worker": "webpack --config sw.webpack.config.js --mode development",
35
+ "build:worker:prod": "webpack --config sw.webpack.config.js --mode production",
36
+ "build": "jlpm build:lib && jlpm build:labextension:dev && jlpm build:worker",
37
+ "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension && jlpm build:worker:prod",
38
+ "build:labextension": "jupyter labextension build .",
39
+ "build:labextension:dev": "jupyter labextension build --development True .",
40
+ "build:lib": "tsc --sourceMap",
41
+ "build:lib:prod": "tsc",
42
+ "clean": "jlpm clean:lib",
43
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
44
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
45
+ "clean:labextension": "rimraf jupyterpack/labextension jupyterpack/_version.py",
46
+ "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
47
+ "eslint": "jlpm eslint:check --fix",
48
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
49
+ "install:extension": "jlpm build",
50
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
51
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
52
+ "prettier": "jlpm prettier:base --write --list-different",
53
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
54
+ "prettier:check": "jlpm prettier:base --check",
55
+ "stylelint": "jlpm stylelint:check --fix",
56
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
57
+ "watch": "run-p watch:src watch:labextension",
58
+ "watch:src": "tsc -w --sourceMap",
59
+ "watch:labextension": "jupyter labextension watch ."
60
+ },
61
+ "dependencies": {
62
+ "@codesandbox/sandpack-client": "^2.19.8",
63
+ "@jupyterlab/application": "^4.0.0",
64
+ "comlink": "^4.4.2"
65
+ },
66
+ "devDependencies": {
67
+ "@jupyterlab/builder": "^4.0.0",
68
+ "@types/json-schema": "^7.0.11",
69
+ "@types/react": "^18.0.26",
70
+ "@types/react-addons-linked-state-mixin": "^0.14.22",
71
+ "@types/serviceworker": "^0.0.152",
72
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
73
+ "@typescript-eslint/parser": "^6.1.0",
74
+ "copy-webpack-plugin": "^13.0.1",
75
+ "css-loader": "^6.7.1",
76
+ "eslint": "^8.36.0",
77
+ "eslint-config-prettier": "^8.8.0",
78
+ "eslint-plugin-prettier": "^5.0.0",
79
+ "npm-run-all2": "^7.0.1",
80
+ "prettier": "^3.0.0",
81
+ "rimraf": "^5.0.1",
82
+ "source-map-loader": "^1.0.2",
83
+ "style-loader": "^3.3.1",
84
+ "stylelint": "^15.10.1",
85
+ "stylelint-config-recommended": "^13.0.0",
86
+ "stylelint-config-standard": "^34.0.0",
87
+ "stylelint-csstree-validator": "^3.0.0",
88
+ "stylelint-prettier": "^4.0.0",
89
+ "ts-loader": "^9.2.6",
90
+ "typescript": "~5.8.0",
91
+ "webpack": "^5.76.3",
92
+ "webpack-cli": "^6.0.1",
93
+ "yjs": "^13.5.0"
94
+ },
95
+ "sideEffects": [
96
+ "style/*.css",
97
+ "style/index.js"
98
+ ],
99
+ "styleModule": "style/index.js",
100
+ "publishConfig": {
101
+ "access": "public"
102
+ },
103
+ "jupyterlab": {
104
+ "extension": true,
105
+ "outputDir": "jupyterpack/labextension"
106
+ },
107
+ "eslintIgnore": [
108
+ "node_modules",
109
+ "demo",
110
+ "dist",
111
+ "coverage",
112
+ "**/*.d.ts"
113
+ ],
114
+ "eslintConfig": {
115
+ "extends": [
116
+ "eslint:recommended",
117
+ "plugin:@typescript-eslint/eslint-recommended",
118
+ "plugin:@typescript-eslint/recommended",
119
+ "plugin:prettier/recommended"
120
+ ],
121
+ "parser": "@typescript-eslint/parser",
122
+ "parserOptions": {
123
+ "project": "tsconfig.json",
124
+ "sourceType": "module"
125
+ },
126
+ "plugins": [
127
+ "@typescript-eslint"
128
+ ],
129
+ "rules": {
130
+ "@typescript-eslint/naming-convention": [
131
+ "error",
132
+ {
133
+ "selector": "interface",
134
+ "format": [
135
+ "PascalCase"
136
+ ],
137
+ "custom": {
138
+ "regex": "^I[A-Z]",
139
+ "match": true
140
+ }
141
+ }
142
+ ],
143
+ "@typescript-eslint/no-unused-vars": [
144
+ "warn",
145
+ {
146
+ "args": "none"
147
+ }
148
+ ],
149
+ "@typescript-eslint/no-explicit-any": "off",
150
+ "@typescript-eslint/no-namespace": "off",
151
+ "@typescript-eslint/no-use-before-define": "off",
152
+ "@typescript-eslint/quotes": [
153
+ "error",
154
+ "single",
155
+ {
156
+ "avoidEscape": true,
157
+ "allowTemplateLiterals": false
158
+ }
159
+ ],
160
+ "curly": [
161
+ "error",
162
+ "all"
163
+ ],
164
+ "eqeqeq": "error",
165
+ "prefer-arrow-callback": "error"
166
+ }
167
+ },
168
+ "prettier": {
169
+ "singleQuote": true,
170
+ "trailingComma": "none",
171
+ "arrowParens": "avoid",
172
+ "endOfLine": "auto",
173
+ "overrides": [
174
+ {
175
+ "files": "package.json",
176
+ "options": {
177
+ "tabWidth": 4
178
+ }
179
+ }
180
+ ]
181
+ },
182
+ "stylelint": {
183
+ "extends": [
184
+ "stylelint-config-recommended",
185
+ "stylelint-config-standard",
186
+ "stylelint-prettier/recommended"
187
+ ],
188
+ "plugins": [
189
+ "stylelint-csstree-validator"
190
+ ],
191
+ "rules": {
192
+ "csstree/validator": true,
193
+ "property-no-vendor-prefix": null,
194
+ "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
195
+ "selector-no-vendor-prefix": null,
196
+ "value-no-vendor-prefix": null
197
+ }
198
+ }
199
+ }
@@ -0,0 +1,25 @@
1
+ import { Widget } from '@lumino/widgets';
2
+
3
+ export class IFramePanel extends Widget {
4
+ constructor() {
5
+ super();
6
+ this.addClass('jupyterpack-iframe-panel');
7
+
8
+ this._iframe = document.createElement('iframe');
9
+ this._spinner = document.createElement('div');
10
+ this._spinner.classList.add('jupyterpack-spinner');
11
+ this.node.appendChild(this._spinner);
12
+ this.node.appendChild(this._iframe);
13
+ }
14
+
15
+ toggleSpinner(show: boolean): void {
16
+ if (show) {
17
+ this._spinner.style.display = 'unset';
18
+ } else {
19
+ this._spinner.style.display = 'none';
20
+ }
21
+ }
22
+
23
+ protected _iframe: HTMLIFrameElement;
24
+ protected _spinner: HTMLDivElement;
25
+ }
@@ -0,0 +1,19 @@
1
+ import { DocumentWidget } from '@jupyterlab/docregistry';
2
+
3
+ export class JupyterPackDocWidget extends DocumentWidget {
4
+ constructor(options: DocumentWidget.IOptions) {
5
+ super(options);
6
+ }
7
+
8
+ /**
9
+ * Dispose of the resources held by the widget.
10
+ */
11
+ dispose(): void {
12
+ this.content.dispose();
13
+ super.dispose();
14
+ }
15
+
16
+ onResize = (msg: any): void => {
17
+ window.dispatchEvent(new Event('resize'));
18
+ };
19
+ }
@@ -0,0 +1,44 @@
1
+ import {
2
+ JupyterFrontEnd,
3
+ JupyterFrontEndPlugin
4
+ } from '@jupyterlab/application';
5
+
6
+ import { JupyterPackWidgetFactory } from './widgetFactory';
7
+ import { IConnectionManager } from '../type';
8
+ import { IConnectionManagerToken } from '../token';
9
+
10
+ const FACTORY = 'jupyterpack';
11
+ const CONTENT_TYPE = 'jupyterpack';
12
+
13
+ export const spkPlugin: JupyterFrontEndPlugin<void> = {
14
+ id: 'jupyterpack:spkplugin',
15
+ requires: [IConnectionManagerToken],
16
+ autoStart: true,
17
+ activate: (
18
+ app: JupyterFrontEnd,
19
+ connectionManager: IConnectionManager
20
+ ): void => {
21
+ const widgetFactory = new JupyterPackWidgetFactory({
22
+ name: FACTORY,
23
+ modelName: 'text',
24
+ fileTypes: [CONTENT_TYPE],
25
+ defaultFor: [CONTENT_TYPE],
26
+ commands: app.commands,
27
+ manager: app.serviceManager,
28
+ connectionManager
29
+ });
30
+
31
+ // Registering the widget factory
32
+ app.docRegistry.addWidgetFactory(widgetFactory);
33
+
34
+ // register the filetype
35
+ app.docRegistry.addFileType({
36
+ name: CONTENT_TYPE,
37
+ displayName: 'SPK',
38
+ mimeTypes: ['text/json'],
39
+ extensions: ['.spk', '.SPK'],
40
+ fileFormat: 'json',
41
+ contentType: CONTENT_TYPE
42
+ });
43
+ }
44
+ };
@@ -0,0 +1,79 @@
1
+ import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
2
+ import { Contents, ServiceManager } from '@jupyterlab/services';
3
+ import { CommandRegistry } from '@lumino/commands';
4
+ import { Panel } from '@lumino/widgets';
5
+
6
+ import {
7
+ IConnectionManager,
8
+ IJupyterPackFileFormat,
9
+ JupyterPackFramework
10
+ } from '../type';
11
+ import { SandpackPanel } from '../sandpackWidget/sandpackPanel';
12
+ import { JupyterPackDocWidget } from './jupyterpackDocWidget';
13
+ import { PythonWidgetModel } from '../pythonWidget/pythonWidgetModel';
14
+ import { UUID } from '@lumino/coreutils';
15
+ import { PythonWidget } from '../pythonWidget/pythonWidget';
16
+
17
+ interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
18
+ commands: CommandRegistry;
19
+ manager: ServiceManager.IManager;
20
+ connectionManager: IConnectionManager;
21
+ }
22
+
23
+ export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWidget> {
24
+ constructor(private options: IOptions) {
25
+ super(options);
26
+ this._contentsManager = options.manager.contents;
27
+ }
28
+
29
+ /**
30
+ * Create a new widget given a context.
31
+ *
32
+ * @param context Contains the information of the file
33
+ * @returns The widget
34
+ */
35
+ protected createNewWidget(
36
+ context: DocumentRegistry.IContext<DocumentRegistry.IModel>
37
+ ): JupyterPackDocWidget {
38
+ const content = new Panel();
39
+ content.addClass('jp-jupyterpack-document-panel');
40
+ context.ready.then(() => {
41
+ const jpackModel =
42
+ context.model.toJSON() as any as IJupyterPackFileFormat;
43
+ switch (jpackModel.framework) {
44
+ case JupyterPackFramework.REACT: {
45
+ const jpContent = new SandpackPanel({
46
+ context,
47
+ contentsManager: this._contentsManager
48
+ });
49
+ content.addWidget(jpContent);
50
+ break;
51
+ }
52
+ case JupyterPackFramework.DASH: {
53
+ const model = new PythonWidgetModel({
54
+ context,
55
+ manager: this.options.manager,
56
+ contentsManager: this._contentsManager,
57
+ connectionManager: this.options.connectionManager
58
+ });
59
+ const pythonWidget = new PythonWidget({
60
+ model,
61
+ id: UUID.uuid4()
62
+ });
63
+ content.addWidget(pythonWidget);
64
+ break;
65
+ }
66
+ default: {
67
+ console.error('Unsupported framework');
68
+ break;
69
+ }
70
+ }
71
+ });
72
+ return new JupyterPackDocWidget({
73
+ context,
74
+ content
75
+ });
76
+ }
77
+
78
+ private _contentsManager: Contents.IManager;
79
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { spkPlugin } from './document/plugin';
2
+ import { swPlugin } from './swConnection';
3
+
4
+ export default [swPlugin, spkPlugin];
@@ -0,0 +1,43 @@
1
+ import { IConnectionManager, IDict, IKernelExecutor } from '../type';
2
+ import { UUID } from '@lumino/coreutils';
3
+
4
+ export class ConnectionManager implements IConnectionManager {
5
+ constructor(public instanceId: string) {}
6
+
7
+ async registerConnection(
8
+ kernelExecutor: IKernelExecutor
9
+ ): Promise<{ instanceId: string; kernelClientId: string }> {
10
+ const uuid = UUID.uuid4();
11
+
12
+ this._kernelExecutors.set(uuid, kernelExecutor);
13
+
14
+ return { instanceId: this.instanceId, kernelClientId: uuid };
15
+ }
16
+
17
+ async generateResponse(options: {
18
+ kernelClientId: string;
19
+ urlPath: string;
20
+ method: string;
21
+ headers: IDict;
22
+ requestBody?: ArrayBuffer;
23
+ params?: string;
24
+ }): Promise<IDict | null> {
25
+ const { urlPath, kernelClientId, method, params, requestBody, headers } =
26
+ options;
27
+ const executor = this._kernelExecutors.get(kernelClientId);
28
+ if (!executor) {
29
+ return null;
30
+ }
31
+
32
+ const response = await executor.getResponse({
33
+ urlPath,
34
+ method,
35
+ params,
36
+ headers,
37
+ requestBody
38
+ });
39
+ return response;
40
+ }
41
+
42
+ private _kernelExecutors = new Map<string, IKernelExecutor>();
43
+ }
@@ -0,0 +1,140 @@
1
+ import { PageConfig, URLExt } from '@jupyterlab/coreutils';
2
+ import { KernelMessage, Session } from '@jupyterlab/services';
3
+
4
+ import { arrayBufferToBase64 } from '../tools';
5
+ import { IDict, IKernelExecutor } from '../type';
6
+
7
+ export class KernelExecutor implements IKernelExecutor {
8
+ constructor(options: KernelExecutor.IOptions) {
9
+ this._sessionConnection = options.sessionConnection;
10
+ }
11
+
12
+ get isDisposed(): boolean {
13
+ return this._isDisposed;
14
+ }
15
+ async init(options: {
16
+ initCode?: string;
17
+ instanceId: string;
18
+ kernelClientId: string;
19
+ }) {
20
+ const { initCode, instanceId, kernelClientId } = options;
21
+ const labBaseUrl = PageConfig.getOption('baseUrl');
22
+ const baseURL = URLExt.join(
23
+ labBaseUrl,
24
+ 'extensions/jupyterpack/static',
25
+ instanceId,
26
+ 'dash',
27
+ kernelClientId,
28
+ '/'
29
+ );
30
+
31
+ const osCode = `
32
+ import os
33
+ os.environ['DASH_URL_BASE_PATHNAME'] = '${baseURL}'
34
+ `;
35
+ await this.executeCode({ code: osCode });
36
+ if (initCode) {
37
+ await this.executeCode({ code: initCode });
38
+ }
39
+ const serverCode = `
40
+ import httpx, json, base64
41
+ __monstra_transport = httpx.WSGITransport(app=app.server)
42
+ def __monstra_get_response(method, url, headers, content=None, params=None):
43
+ decoded_content = None
44
+ if content is not None:
45
+ content = base64.b64decode(content)
46
+ decoded_content = content.decode()
47
+ with httpx.Client(transport=__monstra_transport, base_url="http://testserver") as client:
48
+ r = client.request(method, url, headers=headers, content=content, params=params)
49
+ response = {
50
+ "headers": dict(r.headers),
51
+ "content": r.text,
52
+ "status_code": r.status_code,
53
+ "original_request": {"method": method, "url": url, "content": decoded_content, "params": params, "headers": headers},
54
+ }
55
+ json_str = json.dumps(response)
56
+ b64_str = base64.b64encode(json_str.encode("utf-8")).decode("utf-8")
57
+ return b64_str
58
+ `;
59
+ await this.executeCode({ code: serverCode });
60
+ }
61
+ async getResponse(options: {
62
+ method: string;
63
+ urlPath: string;
64
+ headers: IDict;
65
+ requestBody?: ArrayBuffer;
66
+ params?: string;
67
+ }): Promise<IDict> {
68
+ const { method, urlPath, requestBody, params, headers } = options;
69
+ const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
70
+ const code = `__monstra_get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${content ? `"${content}"` : 'None'}, params=${params ? `"${params}"` : 'None'})`;
71
+ const raw = await this.executeCode({ code });
72
+ const jsonStr = atob(raw.slice(1, -1));
73
+ const obj = JSON.parse(jsonStr);
74
+
75
+ return obj;
76
+ }
77
+ async executeCode(
78
+ code: KernelMessage.IExecuteRequestMsg['content']
79
+ ): Promise<string> {
80
+ const kernel = this._sessionConnection?.kernel;
81
+ if (!kernel) {
82
+ throw new Error('Session has no kernel.');
83
+ }
84
+ return new Promise<string>((resolve, reject) => {
85
+ const future = kernel.requestExecute(code, false, undefined);
86
+ const parentMsgid = future.msg.header.msg_id;
87
+ let executeResult = '';
88
+ future.onIOPub = (msg: KernelMessage.IIOPubMessage): void => {
89
+ const msgType = msg.header.msg_type;
90
+ switch (msgType) {
91
+ case 'execute_result': {
92
+ const content = (msg as KernelMessage.IExecuteResultMsg).content
93
+ .data['text/plain'] as string;
94
+ executeResult += content;
95
+ break;
96
+ }
97
+ case 'status': {
98
+ if (
99
+ (msg as KernelMessage.IStatusMsg).content.execution_state ===
100
+ 'idle' &&
101
+ msg.parent_header.msg_id === parentMsgid
102
+ ) {
103
+ resolve(executeResult);
104
+ }
105
+ break;
106
+ }
107
+ case 'stream': {
108
+ const content = (msg as KernelMessage.IStreamMsg).content.text;
109
+ executeResult += content;
110
+ break;
111
+ }
112
+ case 'error': {
113
+ console.error('Kernel operation failed', msg.content);
114
+ reject(msg.content);
115
+ break;
116
+ }
117
+ default:
118
+ break;
119
+ }
120
+ };
121
+ });
122
+ }
123
+
124
+ dispose(): void {
125
+ if (this._isDisposed) {
126
+ return;
127
+ }
128
+ this._isDisposed = true;
129
+ this._sessionConnection.dispose();
130
+ }
131
+
132
+ private _isDisposed: boolean = false;
133
+ private _sessionConnection: Session.ISessionConnection;
134
+ }
135
+
136
+ export namespace KernelExecutor {
137
+ export interface IOptions {
138
+ sessionConnection: Session.ISessionConnection;
139
+ }
140
+ }
@@ -0,0 +1,34 @@
1
+ import { PythonWidgetModel } from './pythonWidgetModel';
2
+ import { PageConfig } from '@jupyterlab/coreutils';
3
+ import { IFramePanel } from '../document/iframePanel';
4
+
5
+ export class PythonWidget extends IFramePanel {
6
+ constructor(options: PythonWidget.IOptions) {
7
+ super();
8
+ this._model = options.model;
9
+ this._model.initialize().then(connectionData => {
10
+ if (!connectionData) {
11
+ return;
12
+ }
13
+ const iframe = this._iframe;
14
+ const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
15
+ iframe.src = `${fullLabextensionsUrl}/jupyterpack/static/${connectionData.instanceId}/dash/${connectionData.kernelClientId}/`;
16
+ iframe.addEventListener('load', () => {
17
+ this.toggleSpinner(false);
18
+ });
19
+ });
20
+ }
21
+
22
+ get model(): PythonWidgetModel {
23
+ return this._model;
24
+ }
25
+
26
+ private _model: PythonWidgetModel;
27
+ }
28
+
29
+ export namespace PythonWidget {
30
+ export interface IOptions {
31
+ id: string;
32
+ model: PythonWidgetModel;
33
+ }
34
+ }