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/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Trung Le
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ <h1 align="center">jupyterpack</h1>
2
+
3
+ [![Github Actions Status](https://github.com/trungleduc/jupyterpack/workflows/Build/badge.svg)](https://github.com/trungleduc/specta/actions/workflows/build.yml)
4
+ [![Try on lite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://trungleduc.github.io/jupyterpack/lab/)
5
+
6
+ <h2 align="center"> A JupyterLite extension to serve in-browser Python and Javascript web application</h2>
7
+
8
+ ## Features
9
+
10
+ - **Python Web Apps**: Serve Python web applications directly in the browser using JupyterLite's in-browser Python kernel. `jupyterpack` currently supports Dash.
11
+ - **JavaScript Web Apps**: Bundle and serve JavaScript web applications using in-browser bundlers.
12
+
13
+ ## Installation
14
+
15
+ You can install `jupyterpack` using `pip` or `conda`
16
+
17
+ ```bash
18
+ # Install using pip
19
+ pip install jupyterpack
20
+
21
+ # Install using conda
22
+ conda install -c conda-forge jupyterpack
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ To use `jupyterpack`, you need to create a `.spk` file that defines your web application. Here's an example structure of a React application:
28
+
29
+ ```bash
30
+ my_app/
31
+ ├── app.spk
32
+ ├── App.js # Your JS code
33
+ ├── package.json # Your JS dependencies
34
+ └── index.html # HTML entry for JS apps
35
+ ```
36
+
37
+ the `app.spk` is the entry point of your React app, it should contain the following content:
38
+
39
+ ```json
40
+ {
41
+ "name": "React Example",
42
+ "entry": "/index.html",
43
+ "framework": "react"
44
+ }
45
+ ```
46
+
47
+ Double clicking the `spk` file to open the web app as a tab of JupyterLab.
48
+
49
+ ### Dash application
50
+
51
+ Same as the React application, here is the structure of a Dash application:
52
+
53
+ ```bash
54
+ my_app/
55
+ ├── app.spk
56
+ ├── server.py # Your Dash code
57
+ ```
58
+
59
+ the `app.spk` is the entry point of your Dash app, it should contain the following content:
60
+
61
+ ```json
62
+ {
63
+ "name": "Dash Example",
64
+ "entry": "server.py",
65
+ "framework": "dash"
66
+ }
67
+ ```
68
+
69
+ For the Dash code, you need to define your Dash app variable as `app` and do not call `app.run_server` directly, `jupyterpack` will handle the server for you. Just as the case of React app, double clicking the spk file will open the Dash app in a new JupyterLab tab.
70
+
71
+ ## Try it online!
72
+
73
+ You can try it online by clicking on this badge:
74
+
75
+ [![Try on lite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://trungleduc.github.io/jupyterpack/lab/)
76
+
77
+ ## License
78
+
79
+ jupyterpack is licensed under the BSD-3-Clause license.
@@ -0,0 +1,7 @@
1
+ import { Widget } from '@lumino/widgets';
2
+ export declare class IFramePanel extends Widget {
3
+ constructor();
4
+ toggleSpinner(show: boolean): void;
5
+ protected _iframe: HTMLIFrameElement;
6
+ protected _spinner: HTMLDivElement;
7
+ }
@@ -0,0 +1,20 @@
1
+ import { Widget } from '@lumino/widgets';
2
+ export class IFramePanel extends Widget {
3
+ constructor() {
4
+ super();
5
+ this.addClass('jupyterpack-iframe-panel');
6
+ this._iframe = document.createElement('iframe');
7
+ this._spinner = document.createElement('div');
8
+ this._spinner.classList.add('jupyterpack-spinner');
9
+ this.node.appendChild(this._spinner);
10
+ this.node.appendChild(this._iframe);
11
+ }
12
+ toggleSpinner(show) {
13
+ if (show) {
14
+ this._spinner.style.display = 'unset';
15
+ }
16
+ else {
17
+ this._spinner.style.display = 'none';
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,9 @@
1
+ import { DocumentWidget } from '@jupyterlab/docregistry';
2
+ export declare class JupyterPackDocWidget extends DocumentWidget {
3
+ constructor(options: DocumentWidget.IOptions);
4
+ /**
5
+ * Dispose of the resources held by the widget.
6
+ */
7
+ dispose(): void;
8
+ onResize: (msg: any) => void;
9
+ }
@@ -0,0 +1,16 @@
1
+ import { DocumentWidget } from '@jupyterlab/docregistry';
2
+ export class JupyterPackDocWidget extends DocumentWidget {
3
+ constructor(options) {
4
+ super(options);
5
+ this.onResize = (msg) => {
6
+ window.dispatchEvent(new Event('resize'));
7
+ };
8
+ }
9
+ /**
10
+ * Dispose of the resources held by the widget.
11
+ */
12
+ dispose() {
13
+ this.content.dispose();
14
+ super.dispose();
15
+ }
16
+ }
@@ -0,0 +1,2 @@
1
+ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ export declare const spkPlugin: JupyterFrontEndPlugin<void>;
@@ -0,0 +1,31 @@
1
+ import { JupyterPackWidgetFactory } from './widgetFactory';
2
+ import { IConnectionManagerToken } from '../token';
3
+ const FACTORY = 'jupyterpack';
4
+ const CONTENT_TYPE = 'jupyterpack';
5
+ export const spkPlugin = {
6
+ id: 'jupyterpack:spkplugin',
7
+ requires: [IConnectionManagerToken],
8
+ autoStart: true,
9
+ activate: (app, connectionManager) => {
10
+ const widgetFactory = new JupyterPackWidgetFactory({
11
+ name: FACTORY,
12
+ modelName: 'text',
13
+ fileTypes: [CONTENT_TYPE],
14
+ defaultFor: [CONTENT_TYPE],
15
+ commands: app.commands,
16
+ manager: app.serviceManager,
17
+ connectionManager
18
+ });
19
+ // Registering the widget factory
20
+ app.docRegistry.addWidgetFactory(widgetFactory);
21
+ // register the filetype
22
+ app.docRegistry.addFileType({
23
+ name: CONTENT_TYPE,
24
+ displayName: 'SPK',
25
+ mimeTypes: ['text/json'],
26
+ extensions: ['.spk', '.SPK'],
27
+ fileFormat: 'json',
28
+ contentType: CONTENT_TYPE
29
+ });
30
+ }
31
+ };
@@ -0,0 +1,23 @@
1
+ import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
2
+ import { ServiceManager } from '@jupyterlab/services';
3
+ import { CommandRegistry } from '@lumino/commands';
4
+ import { IConnectionManager } from '../type';
5
+ import { JupyterPackDocWidget } from './jupyterpackDocWidget';
6
+ interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
7
+ commands: CommandRegistry;
8
+ manager: ServiceManager.IManager;
9
+ connectionManager: IConnectionManager;
10
+ }
11
+ export declare class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWidget> {
12
+ private options;
13
+ constructor(options: IOptions);
14
+ /**
15
+ * Create a new widget given a context.
16
+ *
17
+ * @param context Contains the information of the file
18
+ * @returns The widget
19
+ */
20
+ protected createNewWidget(context: DocumentRegistry.IContext<DocumentRegistry.IModel>): JupyterPackDocWidget;
21
+ private _contentsManager;
22
+ }
23
+ export {};
@@ -0,0 +1,60 @@
1
+ import { ABCWidgetFactory } from '@jupyterlab/docregistry';
2
+ import { Panel } from '@lumino/widgets';
3
+ import { JupyterPackFramework } from '../type';
4
+ import { SandpackPanel } from '../sandpackWidget/sandpackPanel';
5
+ import { JupyterPackDocWidget } from './jupyterpackDocWidget';
6
+ import { PythonWidgetModel } from '../pythonWidget/pythonWidgetModel';
7
+ import { UUID } from '@lumino/coreutils';
8
+ import { PythonWidget } from '../pythonWidget/pythonWidget';
9
+ export class JupyterPackWidgetFactory extends ABCWidgetFactory {
10
+ constructor(options) {
11
+ super(options);
12
+ this.options = options;
13
+ this._contentsManager = options.manager.contents;
14
+ }
15
+ /**
16
+ * Create a new widget given a context.
17
+ *
18
+ * @param context Contains the information of the file
19
+ * @returns The widget
20
+ */
21
+ createNewWidget(context) {
22
+ const content = new Panel();
23
+ content.addClass('jp-jupyterpack-document-panel');
24
+ context.ready.then(() => {
25
+ const jpackModel = context.model.toJSON();
26
+ switch (jpackModel.framework) {
27
+ case JupyterPackFramework.REACT: {
28
+ const jpContent = new SandpackPanel({
29
+ context,
30
+ contentsManager: this._contentsManager
31
+ });
32
+ content.addWidget(jpContent);
33
+ break;
34
+ }
35
+ case JupyterPackFramework.DASH: {
36
+ const model = new PythonWidgetModel({
37
+ context,
38
+ manager: this.options.manager,
39
+ contentsManager: this._contentsManager,
40
+ connectionManager: this.options.connectionManager
41
+ });
42
+ const pythonWidget = new PythonWidget({
43
+ model,
44
+ id: UUID.uuid4()
45
+ });
46
+ content.addWidget(pythonWidget);
47
+ break;
48
+ }
49
+ default: {
50
+ console.error('Unsupported framework');
51
+ break;
52
+ }
53
+ }
54
+ });
55
+ return new JupyterPackDocWidget({
56
+ context,
57
+ content
58
+ });
59
+ }
60
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ declare const _default: (import("@jupyterlab/application").JupyterFrontEndPlugin<void> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IConnectionManager>)[];
2
+ export default _default;
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { spkPlugin } from './document/plugin';
2
+ import { swPlugin } from './swConnection';
3
+ export default [swPlugin, spkPlugin];
@@ -0,0 +1,18 @@
1
+ import { IConnectionManager, IDict, IKernelExecutor } from '../type';
2
+ export declare class ConnectionManager implements IConnectionManager {
3
+ instanceId: string;
4
+ constructor(instanceId: string);
5
+ registerConnection(kernelExecutor: IKernelExecutor): Promise<{
6
+ instanceId: string;
7
+ kernelClientId: string;
8
+ }>;
9
+ generateResponse(options: {
10
+ kernelClientId: string;
11
+ urlPath: string;
12
+ method: string;
13
+ headers: IDict;
14
+ requestBody?: ArrayBuffer;
15
+ params?: string;
16
+ }): Promise<IDict | null>;
17
+ private _kernelExecutors;
18
+ }
@@ -0,0 +1,27 @@
1
+ import { UUID } from '@lumino/coreutils';
2
+ export class ConnectionManager {
3
+ constructor(instanceId) {
4
+ this.instanceId = instanceId;
5
+ this._kernelExecutors = new Map();
6
+ }
7
+ async registerConnection(kernelExecutor) {
8
+ const uuid = UUID.uuid4();
9
+ this._kernelExecutors.set(uuid, kernelExecutor);
10
+ return { instanceId: this.instanceId, kernelClientId: uuid };
11
+ }
12
+ async generateResponse(options) {
13
+ const { urlPath, kernelClientId, method, params, requestBody, headers } = options;
14
+ const executor = this._kernelExecutors.get(kernelClientId);
15
+ if (!executor) {
16
+ return null;
17
+ }
18
+ const response = await executor.getResponse({
19
+ urlPath,
20
+ method,
21
+ params,
22
+ headers,
23
+ requestBody
24
+ });
25
+ return response;
26
+ }
27
+ }
@@ -0,0 +1,27 @@
1
+ import { KernelMessage, Session } from '@jupyterlab/services';
2
+ import { IDict, IKernelExecutor } from '../type';
3
+ export declare class KernelExecutor implements IKernelExecutor {
4
+ constructor(options: KernelExecutor.IOptions);
5
+ get isDisposed(): boolean;
6
+ init(options: {
7
+ initCode?: string;
8
+ instanceId: string;
9
+ kernelClientId: string;
10
+ }): Promise<void>;
11
+ getResponse(options: {
12
+ method: string;
13
+ urlPath: string;
14
+ headers: IDict;
15
+ requestBody?: ArrayBuffer;
16
+ params?: string;
17
+ }): Promise<IDict>;
18
+ executeCode(code: KernelMessage.IExecuteRequestMsg['content']): Promise<string>;
19
+ dispose(): void;
20
+ private _isDisposed;
21
+ private _sessionConnection;
22
+ }
23
+ export declare namespace KernelExecutor {
24
+ interface IOptions {
25
+ sessionConnection: Session.ISessionConnection;
26
+ }
27
+ }
@@ -0,0 +1,104 @@
1
+ import { PageConfig, URLExt } from '@jupyterlab/coreutils';
2
+ import { arrayBufferToBase64 } from '../tools';
3
+ export class KernelExecutor {
4
+ constructor(options) {
5
+ this._isDisposed = false;
6
+ this._sessionConnection = options.sessionConnection;
7
+ }
8
+ get isDisposed() {
9
+ return this._isDisposed;
10
+ }
11
+ async init(options) {
12
+ const { initCode, instanceId, kernelClientId } = options;
13
+ const labBaseUrl = PageConfig.getOption('baseUrl');
14
+ const baseURL = URLExt.join(labBaseUrl, 'extensions/jupyterpack/static', instanceId, 'dash', kernelClientId, '/');
15
+ const osCode = `
16
+ import os
17
+ os.environ['DASH_URL_BASE_PATHNAME'] = '${baseURL}'
18
+ `;
19
+ await this.executeCode({ code: osCode });
20
+ if (initCode) {
21
+ await this.executeCode({ code: initCode });
22
+ }
23
+ const serverCode = `
24
+ import httpx, json, base64
25
+ __monstra_transport = httpx.WSGITransport(app=app.server)
26
+ def __monstra_get_response(method, url, headers, content=None, params=None):
27
+ decoded_content = None
28
+ if content is not None:
29
+ content = base64.b64decode(content)
30
+ decoded_content = content.decode()
31
+ with httpx.Client(transport=__monstra_transport, base_url="http://testserver") as client:
32
+ r = client.request(method, url, headers=headers, content=content, params=params)
33
+ response = {
34
+ "headers": dict(r.headers),
35
+ "content": r.text,
36
+ "status_code": r.status_code,
37
+ "original_request": {"method": method, "url": url, "content": decoded_content, "params": params, "headers": headers},
38
+ }
39
+ json_str = json.dumps(response)
40
+ b64_str = base64.b64encode(json_str.encode("utf-8")).decode("utf-8")
41
+ return b64_str
42
+ `;
43
+ await this.executeCode({ code: serverCode });
44
+ }
45
+ async getResponse(options) {
46
+ const { method, urlPath, requestBody, params, headers } = options;
47
+ const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
48
+ const code = `__monstra_get_response("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${content ? `"${content}"` : 'None'}, params=${params ? `"${params}"` : 'None'})`;
49
+ const raw = await this.executeCode({ code });
50
+ const jsonStr = atob(raw.slice(1, -1));
51
+ const obj = JSON.parse(jsonStr);
52
+ return obj;
53
+ }
54
+ async executeCode(code) {
55
+ var _a;
56
+ const kernel = (_a = this._sessionConnection) === null || _a === void 0 ? void 0 : _a.kernel;
57
+ if (!kernel) {
58
+ throw new Error('Session has no kernel.');
59
+ }
60
+ return new Promise((resolve, reject) => {
61
+ const future = kernel.requestExecute(code, false, undefined);
62
+ const parentMsgid = future.msg.header.msg_id;
63
+ let executeResult = '';
64
+ future.onIOPub = (msg) => {
65
+ const msgType = msg.header.msg_type;
66
+ switch (msgType) {
67
+ case 'execute_result': {
68
+ const content = msg.content
69
+ .data['text/plain'];
70
+ executeResult += content;
71
+ break;
72
+ }
73
+ case 'status': {
74
+ if (msg.content.execution_state ===
75
+ 'idle' &&
76
+ msg.parent_header.msg_id === parentMsgid) {
77
+ resolve(executeResult);
78
+ }
79
+ break;
80
+ }
81
+ case 'stream': {
82
+ const content = msg.content.text;
83
+ executeResult += content;
84
+ break;
85
+ }
86
+ case 'error': {
87
+ console.error('Kernel operation failed', msg.content);
88
+ reject(msg.content);
89
+ break;
90
+ }
91
+ default:
92
+ break;
93
+ }
94
+ };
95
+ });
96
+ }
97
+ dispose() {
98
+ if (this._isDisposed) {
99
+ return;
100
+ }
101
+ this._isDisposed = true;
102
+ this._sessionConnection.dispose();
103
+ }
104
+ }
@@ -0,0 +1,13 @@
1
+ import { PythonWidgetModel } from './pythonWidgetModel';
2
+ import { IFramePanel } from '../document/iframePanel';
3
+ export declare class PythonWidget extends IFramePanel {
4
+ constructor(options: PythonWidget.IOptions);
5
+ get model(): PythonWidgetModel;
6
+ private _model;
7
+ }
8
+ export declare namespace PythonWidget {
9
+ interface IOptions {
10
+ id: string;
11
+ model: PythonWidgetModel;
12
+ }
13
+ }
@@ -0,0 +1,22 @@
1
+ import { PageConfig } from '@jupyterlab/coreutils';
2
+ import { IFramePanel } from '../document/iframePanel';
3
+ export class PythonWidget extends IFramePanel {
4
+ constructor(options) {
5
+ super();
6
+ this._model = options.model;
7
+ this._model.initialize().then(connectionData => {
8
+ if (!connectionData) {
9
+ return;
10
+ }
11
+ const iframe = this._iframe;
12
+ const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
13
+ iframe.src = `${fullLabextensionsUrl}/jupyterpack/static/${connectionData.instanceId}/dash/${connectionData.kernelClientId}/`;
14
+ iframe.addEventListener('load', () => {
15
+ this.toggleSpinner(false);
16
+ });
17
+ });
18
+ }
19
+ get model() {
20
+ return this._model;
21
+ }
22
+ }
@@ -0,0 +1,29 @@
1
+ import { DocumentRegistry } from '@jupyterlab/docregistry';
2
+ import { IDisposable } from '@lumino/disposable';
3
+ import { ServiceManager, Contents } from '@jupyterlab/services';
4
+ import { IConnectionManager } from '../type';
5
+ export declare class PythonWidgetModel implements IDisposable {
6
+ constructor(options: PythonWidgetModel.IOptions);
7
+ get isDisposed(): boolean;
8
+ get connectionManager(): IConnectionManager;
9
+ initialize(): Promise<{
10
+ instanceId: string;
11
+ kernelClientId: string;
12
+ } | null>;
13
+ dispose(): void;
14
+ private _isDisposed;
15
+ private _kernelStarted;
16
+ private _sessionConnection;
17
+ private _manager;
18
+ private _context;
19
+ private _connectionManager;
20
+ private _contentsManager;
21
+ }
22
+ export declare namespace PythonWidgetModel {
23
+ interface IOptions {
24
+ context: DocumentRegistry.IContext<DocumentRegistry.IModel>;
25
+ manager: ServiceManager.IManager;
26
+ connectionManager: IConnectionManager;
27
+ contentsManager: Contents.IManager;
28
+ }
29
+ }
@@ -0,0 +1,75 @@
1
+ import { PromiseDelegate } from '@lumino/coreutils';
2
+ import { KernelExecutor } from './kernelExecutor';
3
+ import { PathExt } from '@jupyterlab/coreutils';
4
+ export class PythonWidgetModel {
5
+ constructor(options) {
6
+ this._isDisposed = false;
7
+ this._kernelStarted = false;
8
+ this._context = options.context;
9
+ this._manager = options.manager;
10
+ this._connectionManager = options.connectionManager;
11
+ this._contentsManager = options.contentsManager;
12
+ }
13
+ get isDisposed() {
14
+ return this._isDisposed;
15
+ }
16
+ get connectionManager() {
17
+ return this._connectionManager;
18
+ }
19
+ async initialize() {
20
+ var _a;
21
+ if (this._kernelStarted) {
22
+ return null;
23
+ }
24
+ const filePath = this._context.localPath;
25
+ const spkContent = this._context.model.toJSON();
26
+ const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
27
+ const entryContent = await this._contentsManager.get(entryPath, {
28
+ content: true,
29
+ format: 'text'
30
+ });
31
+ const sessionManager = this._manager.sessions;
32
+ await sessionManager.ready;
33
+ await this._manager.kernelspecs.ready;
34
+ const specs = this._manager.kernelspecs.specs;
35
+ if (!specs) {
36
+ return null;
37
+ }
38
+ const { kernelspecs } = specs;
39
+ let kernelName = Object.keys(kernelspecs)[0];
40
+ if (kernelspecs[specs.default]) {
41
+ kernelName = specs.default;
42
+ }
43
+ this._sessionConnection = await sessionManager.startNew({
44
+ name: filePath,
45
+ path: filePath,
46
+ kernel: {
47
+ name: kernelName
48
+ },
49
+ type: 'notebook'
50
+ });
51
+ const executor = new KernelExecutor({
52
+ sessionConnection: this._sessionConnection
53
+ });
54
+ const data = await this._connectionManager.registerConnection(executor);
55
+ await executor.init({
56
+ initCode: entryContent.content,
57
+ ...data
58
+ });
59
+ const finish = new PromiseDelegate();
60
+ const cb = (_, status) => {
61
+ var _a;
62
+ if (status === 'idle') {
63
+ (_a = this._sessionConnection.kernel) === null || _a === void 0 ? void 0 : _a.statusChanged.disconnect(cb);
64
+ finish.resolve();
65
+ }
66
+ };
67
+ (_a = this._sessionConnection.kernel) === null || _a === void 0 ? void 0 : _a.statusChanged.connect(cb);
68
+ await finish.promise;
69
+ this._kernelStarted = true;
70
+ return data;
71
+ }
72
+ dispose() {
73
+ this._isDisposed = true;
74
+ }
75
+ }
@@ -0,0 +1,27 @@
1
+ import { Contents } from '@jupyterlab/services';
2
+ import { ISignal } from '@lumino/signaling';
3
+ import { IDict } from '../type';
4
+ export declare class SandpackFilesModel {
5
+ constructor(options: {
6
+ contentsManager: Contents.IManager;
7
+ path: string;
8
+ });
9
+ getAllFiles(): Promise<IDict<{
10
+ code: string;
11
+ }>>;
12
+ get fileChanged(): ISignal<SandpackFilesModel, {
13
+ allFiles: IDict<{
14
+ code: string;
15
+ }>;
16
+ }>;
17
+ flattenDirectory(dirContent: Contents.IModel): Promise<IDict<{
18
+ code: string;
19
+ }>>;
20
+ private _onFileChanged;
21
+ private _removeRoot;
22
+ private _getContent;
23
+ private _path;
24
+ private _fileChanged;
25
+ private _contentManager;
26
+ private _allFiles?;
27
+ }