jupyterpack 0.5.5 → 0.5.6
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.
- package/README.md +97 -19
- package/lib/document/commands.d.ts +14 -2
- package/lib/document/commands.js +66 -5
- package/lib/document/plugin.d.ts +1 -0
- package/lib/document/plugin.js +89 -107
- package/lib/document/templates/fasthtml.d.ts +1 -0
- package/lib/document/templates/fasthtml.js +11 -0
- package/lib/document/templates/index.d.ts +8 -0
- package/lib/document/templates/index.js +39 -4
- package/lib/document/templates/spk.d.ts +5 -0
- package/lib/document/templates/spk.js +20 -0
- package/lib/document/templates/textual.d.ts +1 -0
- package/lib/document/templates/textual.js +7 -0
- package/lib/document/templates/vizro.d.ts +1 -0
- package/lib/document/templates/vizro.js +26 -0
- package/lib/document/toolbar.js +4 -0
- package/lib/document/widgetFactory.js +5 -1
- package/lib/index.js +2 -2
- package/lib/pythonServer/baseServer.d.ts +4 -1
- package/lib/pythonServer/baseServer.js +21 -22
- package/lib/pythonServer/dash/dashServer.d.ts +2 -1
- package/lib/pythonServer/dash/dashServer.js +13 -6
- package/lib/pythonServer/fastapi/deps.d.ts +2 -0
- package/lib/pythonServer/fastapi/deps.js +4 -0
- package/lib/pythonServer/fastapi/fastapiServer.d.ts +6 -0
- package/lib/pythonServer/fastapi/fastapiServer.js +16 -0
- package/lib/pythonServer/fasthtml/deps.d.ts +2 -0
- package/lib/pythonServer/fasthtml/deps.js +11 -0
- package/lib/pythonServer/fasthtml/fasthtmlServer.d.ts +6 -0
- package/lib/pythonServer/fasthtml/fasthtmlServer.js +16 -0
- package/lib/pythonServer/index.js +9 -1
- package/lib/pythonServer/kernelExecutor.js +3 -3
- package/lib/pythonServer/panel/panelServer.d.ts +2 -1
- package/lib/pythonServer/panel/panelServer.js +5 -2
- package/lib/pythonServer/shiny/shinyServer.d.ts +2 -1
- package/lib/pythonServer/shiny/shinyServer.js +5 -2
- package/lib/pythonServer/starlette/starletteServer.d.ts +2 -0
- package/lib/pythonServer/starlette/starletteServer.js +7 -4
- package/lib/pythonServer/streamlit/streamlitServer.d.ts +2 -1
- package/lib/pythonServer/streamlit/streamlitServer.js +5 -2
- package/lib/pythonServer/textual/deps.d.ts +2 -0
- package/lib/pythonServer/textual/deps.js +4 -0
- package/lib/pythonServer/textual/textualServer.d.ts +11 -0
- package/lib/pythonServer/textual/textualServer.js +52 -0
- package/lib/pythonServer/tornado/tornadoServer.d.ts +2 -0
- package/lib/pythonServer/tornado/tornadoServer.js +5 -2
- package/lib/pythonServer/vizro/deps.d.ts +2 -0
- package/lib/pythonServer/vizro/deps.js +4 -0
- package/lib/pythonServer/vizro/vizroServer.d.ts +10 -0
- package/lib/pythonServer/vizro/vizroServer.js +25 -0
- package/lib/tools.d.ts +15 -1
- package/lib/tools.js +49 -5
- package/lib/type.d.ts +6 -1
- package/lib/type.js +4 -0
- package/package.json +3 -1
- package/src/document/commands.ts +90 -7
- package/src/document/plugin.ts +103 -112
- package/src/document/templates/fasthtml.ts +11 -0
- package/src/document/templates/index.ts +48 -4
- package/src/document/templates/spk.ts +25 -0
- package/src/document/templates/textual.ts +7 -0
- package/src/document/templates/vizro.ts +26 -0
- package/src/document/toolbar.ts +7 -0
- package/src/document/widgetFactory.ts +5 -1
- package/src/index.ts +2 -2
- package/src/pythonServer/baseServer.ts +23 -27
- package/src/pythonServer/dash/dashServer.ts +11 -9
- package/src/pythonServer/fastapi/deps.ts +6 -0
- package/src/pythonServer/fastapi/fastapiServer.ts +14 -0
- package/src/pythonServer/fasthtml/deps.ts +13 -0
- package/src/pythonServer/fasthtml/fasthtmlServer.ts +15 -0
- package/src/pythonServer/index.ts +9 -1
- package/src/pythonServer/kernelExecutor.ts +3 -4
- package/src/pythonServer/panel/panelServer.ts +2 -2
- package/src/pythonServer/shiny/shinyServer.ts +2 -2
- package/src/pythonServer/starlette/starletteServer.ts +5 -4
- package/src/pythonServer/streamlit/streamlitServer.ts +2 -2
- package/src/pythonServer/textual/deps.ts +6 -0
- package/src/pythonServer/textual/textualServer.ts +62 -0
- package/src/pythonServer/tornado/tornadoServer.ts +2 -2
- package/src/pythonServer/vizro/deps.ts +5 -0
- package/src/pythonServer/vizro/vizroServer.ts +28 -0
- package/src/tools.ts +78 -6
- package/src/type.ts +6 -1
- package/src/websocket/websocket.ts +0 -1
- package/style/base.css +4 -0
- package/style/icons/Plotly-Logo-Black.svg +1 -1
- package/style/icons/copy.svg +1 -0
- package/style/icons/fasthtml.svg +62 -0
- package/style/icons/panel_logo_stacked.svg +1 -1
- package/style/icons/shiny-for-python.svg +1 -1
- package/style/icons/streamlit-logo-primary.svg +1 -1
- package/style/icons/textual.svg +12 -0
- package/style/icons/vizro.svg +18 -0
package/README.md
CHANGED
|
@@ -3,15 +3,32 @@
|
|
|
3
3
|
[](https://github.com/trungleduc/specta/actions/workflows/build.yml)
|
|
4
4
|
[](https://trungleduc.github.io/jupyterpack/lab/)
|
|
5
5
|
|
|
6
|
-
<h2 align="center">
|
|
6
|
+
<h2 align="center">In-browser Python and JavaScript web applications for JupyterLite</h2>
|
|
7
|
+
|
|
8
|
+
`jupyterpack` brings in-browser Python and JavaScript web applications to the JupyterLite ecosystem. Built as a JupyterLite extension, it allows applications to run, serve, and interact fully client-side, with no backend required.
|
|
9
|
+
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+

|
|
7
13
|
|
|
8
14
|
## Features
|
|
9
15
|
|
|
10
|
-
- **Python Web Apps**: Serve Python web applications directly in the browser using JupyterLite's in-browser Python kernel. `jupyterpack` currently supports
|
|
11
|
-
.
|
|
16
|
+
- **Python Web Apps**: Serve Python web applications directly in the browser using JupyterLite's in-browser Python kernel. `jupyterpack` currently supports:
|
|
17
|
+
- [**Dash**](https://github.com/plotly/dash)
|
|
18
|
+
- [**Streamlit**](https://github.com/streamlit/streamlit)
|
|
19
|
+
- [**Panel**](https://github.com/holoviz/panel)
|
|
20
|
+
- [**Shiny for Python**](https://github.com/posit-dev/py-shiny)
|
|
21
|
+
- [**Textual**](https://github.com/Textualize/textual)
|
|
22
|
+
- [**Vizro**](https://github.com/mckinsey/vizro)
|
|
23
|
+
- [**FastHTML**](https://github.com/AnswerDotAI/fasthtml)
|
|
24
|
+
|
|
25
|
+
You can also use `jupyterpack` to serve any [**Flask**](https://github.com/pallets/flask), [**Starlette**](https://github.com/Kludex/starlette) or [**Tornado**](https://github.com/tornadoweb/tornado) application.
|
|
26
|
+
|
|
12
27
|
- **JavaScript Web Apps**: Bundle and serve JavaScript web applications using in-browser bundlers.
|
|
13
28
|
|
|
14
|
-
|
|
29
|
+
Example of each framework can be found in the [demo](https://github.com/trungleduc/jupyterpack/tree/main/demo/files) folder.
|
|
30
|
+
|
|
31
|
+
- ** Direct link to your app**: Share your app with others by generating a direct link to your app. This link can be shared with anyone and will open your app in the browser (see the [toolbar buttons](https://github.com/trungleduc/jupyterpack/edit/main/README.md#toolbar-buttons)).
|
|
15
32
|
|
|
16
33
|
## Installation
|
|
17
34
|
|
|
@@ -25,7 +42,13 @@ pip install jupyterpack
|
|
|
25
42
|
conda install -c conda-forge jupyterpack
|
|
26
43
|
```
|
|
27
44
|
|
|
28
|
-
##
|
|
45
|
+
## Try it online!
|
|
46
|
+
|
|
47
|
+
You can try it online by clicking on this badge:
|
|
48
|
+
|
|
49
|
+
[](https://trungleduc.github.io/jupyterpack/lab/)
|
|
50
|
+
|
|
51
|
+
## Setting up JupyterLite deployment
|
|
29
52
|
|
|
30
53
|
`jupyterpack` currently supports only `xeus-python` kernel and does **not** support `pyodide-kernel`. You can refer to the `xeus-python` [official documentation](https://jupyterlite-xeus.readthedocs.io/en/stable/deploy.html) for the base setup of JupyterLite with `xeus-python` kernel.
|
|
31
54
|
|
|
@@ -105,8 +128,44 @@ dependencies:
|
|
|
105
128
|
- pyodide_http
|
|
106
129
|
```
|
|
107
130
|
|
|
131
|
+
- **Texual**
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
name: xeus-kernels
|
|
135
|
+
channels:
|
|
136
|
+
- https://repo.prefix.dev/emscripten-forge-dev
|
|
137
|
+
- https://repo.prefix.dev/conda-forge
|
|
138
|
+
dependencies:
|
|
139
|
+
- xeus-python
|
|
140
|
+
- jupyterpack
|
|
141
|
+
- textual
|
|
142
|
+
- textual-serve
|
|
143
|
+
- pip:
|
|
144
|
+
- pyodide_http
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- **Vizro**
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
name: xeus-kernels
|
|
151
|
+
channels:
|
|
152
|
+
- https://repo.prefix.dev/emscripten-forge-dev
|
|
153
|
+
- https://repo.prefix.dev/conda-forge
|
|
154
|
+
dependencies:
|
|
155
|
+
- xeus-python
|
|
156
|
+
- jupyterpack
|
|
157
|
+
- werkzeug>=2.2,<3.0
|
|
158
|
+
- blinker>=1.5.0,<2
|
|
159
|
+
- cachetools>=4.0,<7
|
|
160
|
+
- vizro
|
|
161
|
+
- pip:
|
|
162
|
+
- pyodide_http
|
|
163
|
+
```
|
|
164
|
+
|
|
108
165
|
## Usage
|
|
109
166
|
|
|
167
|
+
### Create a `.spk` file
|
|
168
|
+
|
|
110
169
|
To use `jupyterpack`, you need to create a `.spk` file that defines your web application. Here's an example structure of a React application:
|
|
111
170
|
|
|
112
171
|
```bash
|
|
@@ -129,6 +188,15 @@ the `app.spk` is the entry point of your React app, it should contain the follow
|
|
|
129
188
|
|
|
130
189
|
Double-clicking the `spk` file to open the web app as a tab of JupyterLab.
|
|
131
190
|
|
|
191
|
+
### Toolbar buttons
|
|
192
|
+
|
|
193
|
+
Once the app is loaded, you can interact with it using the toolbar buttons:
|
|
194
|
+
|
|
195
|
+
- **Reload**: Reload the app manually after editing the code.
|
|
196
|
+
- **Toggle autoreload**: Enable or disable autoreloading of the app when files change.
|
|
197
|
+
- **Open in Specta**: Open the app in full-screen mode without JupyterLab UI elements
|
|
198
|
+
- **Copy link to clipboard**: Copy a shareable link to your application. Anyone with the link can access your app.
|
|
199
|
+
|
|
132
200
|
## `.spk` — Jupyter Pack File Format
|
|
133
201
|
|
|
134
202
|
A **`.spk`** file describes how an application should be loaded, executed, and rendered in JupyterLite and JupyterLab.
|
|
@@ -161,14 +229,16 @@ interface IJupyterPackFileFormat {
|
|
|
161
229
|
|
|
162
230
|
- `framework` (required): The framework used to run the application. Supported frameworks are:
|
|
163
231
|
|
|
164
|
-
| Value | Description
|
|
165
|
-
| ----------- |
|
|
166
|
-
| `react` | React-based frontend application
|
|
167
|
-
| `dash` | Plotly Dash application
|
|
168
|
-
| `streamlit` | Streamlit application
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
232
|
+
| Value | Description |
|
|
233
|
+
| ----------- | ------------------------------------------------------------------- |
|
|
234
|
+
| `react` | React-based frontend application |
|
|
235
|
+
| `dash` | [Plotly Dash](https://github.com/plotly/dash) application |
|
|
236
|
+
| `streamlit` | [Streamlit](https://github.com/streamlit/streamlit) application |
|
|
237
|
+
| `shiny` | [Shiny](https://github.com/posit-dev/py-shiny) application (Python) |
|
|
238
|
+
| `panel` | [Panel](https://github.com/holoviz/panel) application |
|
|
239
|
+
| `textual` | [Textual](https://github.com/Textualize/textual) application |
|
|
240
|
+
| `tornado` | [Tornado](https://github.com/tornadoweb/tornado) web application |
|
|
241
|
+
| `starlette` | [Starlette](https://github.com/Kludex/starlette) web application |
|
|
172
242
|
|
|
173
243
|
- `name` (optional): The name of the application. If not provided, the name will be the name of the .spk file.
|
|
174
244
|
|
|
@@ -240,14 +310,12 @@ As with React applications, double-clicking the `.spk` file will open the Dash a
|
|
|
240
310
|
|
|
241
311
|
### Streamlit application
|
|
242
312
|
|
|
243
|
-
Streamlit applications
|
|
244
|
-
Write your code as a standard Streamlit application and do **not** start the server manually — `jupyterpack` will handle execution and serving automatically.
|
|
313
|
+
There is no special requirement for Streamlit applications, just write your code as a standard Streamlit application and do **not** start the server manually — `jupyterpack` will handle execution and serving automatically.
|
|
245
314
|
|
|
246
315
|
Opening the `.spk` file will launch the Streamlit app in a new JupyterLab tab.
|
|
247
316
|
|
|
248
317
|
### Shiny application
|
|
249
318
|
|
|
250
|
-
Shiny applications also follow a structure similar to Dash apps.
|
|
251
319
|
`jupyterpack` supports both **Shiny Express** and **Shiny Core** applications.
|
|
252
320
|
|
|
253
321
|
- **Shiny Express**: no special requirements.
|
|
@@ -255,11 +323,21 @@ Shiny applications also follow a structure similar to Dash apps.
|
|
|
255
323
|
|
|
256
324
|
In both cases, the server is managed by `jupyterpack`, and opening the `.spk` file will launch the app in JupyterLab.
|
|
257
325
|
|
|
258
|
-
|
|
326
|
+
### Panel application
|
|
259
327
|
|
|
260
|
-
|
|
328
|
+
There is no special requirement for Panel applications, just write your code as a standard Panel application and call `.servable()` on the layout you want to serve.
|
|
261
329
|
|
|
262
|
-
|
|
330
|
+
### Textual application
|
|
331
|
+
|
|
332
|
+
You must define your Textual application as a variable named `app` and do not call `app.run()` yourself — `jupyterpack` is responsible for starting and managing the server lifecycle.
|
|
333
|
+
|
|
334
|
+
### Vizro application
|
|
335
|
+
|
|
336
|
+
There is no special requirement for Vizro applications, just write your code as a standard Vizro application and call `Vizro().build(...).run()` to serve your dashboard.
|
|
337
|
+
|
|
338
|
+
### FastHTML application
|
|
339
|
+
|
|
340
|
+
JupyterPack only supports async handlers with FastHTML. You must convert all synchronous handlers to async, and you should not call `serve()` yourself — jupyterpack is responsible for starting and managing the server lifecycle.
|
|
263
341
|
|
|
264
342
|
## License
|
|
265
343
|
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
+
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
2
|
+
import { ILauncher } from '@jupyterlab/launcher';
|
|
3
|
+
import { LabIcon } from '@jupyterlab/ui-components';
|
|
1
4
|
import { CommandRegistry } from '@lumino/commands';
|
|
2
|
-
import { IJupyterpackDocTracker } from '../type';
|
|
5
|
+
import { IJupyterpackDocTracker, JupyterPackFramework } from '../type';
|
|
6
|
+
import { Contents } from '@jupyterlab/services';
|
|
3
7
|
export declare const CommandIDs: {
|
|
4
8
|
RELOAD: string;
|
|
5
9
|
TOGGLE_AUTORELOAD: string;
|
|
6
10
|
OPEN_SPECTA: string;
|
|
11
|
+
COPY_LINK: string;
|
|
7
12
|
};
|
|
8
|
-
export declare function addCommands(commands: CommandRegistry, tracker: IJupyterpackDocTracker): void;
|
|
13
|
+
export declare function addCommands(commands: CommandRegistry, tracker: IJupyterpackDocTracker, contentsManager: Contents.IManager): void;
|
|
14
|
+
export declare function addLauncherCommands(options: {
|
|
15
|
+
app: JupyterFrontEnd;
|
|
16
|
+
launcher: ILauncher;
|
|
17
|
+
framework: JupyterPackFramework;
|
|
18
|
+
icon: LabIcon;
|
|
19
|
+
rank: number;
|
|
20
|
+
}): void;
|
package/lib/document/commands.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { PageConfig, PathExt, URLExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { copyIcon, refreshIcon } from '@jupyterlab/ui-components';
|
|
3
|
+
import { Notification } from '@jupyterlab/apputils';
|
|
4
|
+
import { autoReloadIcon, encodeSpk, linkIcon } from '../tools';
|
|
5
|
+
import { generateAppFiles } from './templates';
|
|
4
6
|
export const CommandIDs = {
|
|
5
7
|
RELOAD: 'jupyterpack:reload',
|
|
6
8
|
TOGGLE_AUTORELOAD: 'jupyterpack:toggleAutoreload',
|
|
7
|
-
OPEN_SPECTA: 'jupyterpack:openInSpecta'
|
|
9
|
+
OPEN_SPECTA: 'jupyterpack:openInSpecta',
|
|
10
|
+
COPY_LINK: 'jupyterpack:copyEncodedLink'
|
|
8
11
|
};
|
|
9
12
|
const labBaseUrl = PageConfig.getOption('baseUrl');
|
|
10
13
|
function getCurrentIframPanel(tracker) {
|
|
@@ -19,7 +22,7 @@ function getCurrentIframPanel(tracker) {
|
|
|
19
22
|
}
|
|
20
23
|
return widget;
|
|
21
24
|
}
|
|
22
|
-
export function addCommands(commands, tracker) {
|
|
25
|
+
export function addCommands(commands, tracker, contentsManager) {
|
|
23
26
|
commands.addCommand(CommandIDs.RELOAD, {
|
|
24
27
|
caption: 'Reload',
|
|
25
28
|
isEnabled: () => {
|
|
@@ -73,4 +76,62 @@ export function addCommands(commands, tracker) {
|
|
|
73
76
|
window.open(spectaUrl.toString(), '_blank');
|
|
74
77
|
}
|
|
75
78
|
});
|
|
79
|
+
commands.addCommand(CommandIDs.COPY_LINK, {
|
|
80
|
+
caption: 'Copy link to clipboard',
|
|
81
|
+
isEnabled: () => {
|
|
82
|
+
return tracker.currentWidget !== null;
|
|
83
|
+
},
|
|
84
|
+
icon: copyIcon,
|
|
85
|
+
execute: async () => {
|
|
86
|
+
var _a;
|
|
87
|
+
const context = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context;
|
|
88
|
+
if (!context) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await context.ready;
|
|
92
|
+
const filePath = context.localPath;
|
|
93
|
+
const spkContent = context.model.toJSON();
|
|
94
|
+
const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
|
|
95
|
+
const entryContent = await contentsManager.get(entryPath, {
|
|
96
|
+
content: true,
|
|
97
|
+
format: 'text'
|
|
98
|
+
});
|
|
99
|
+
const encodedData = encodeSpk({
|
|
100
|
+
spkContent: JSON.stringify(spkContent),
|
|
101
|
+
entryContent: entryContent.content
|
|
102
|
+
});
|
|
103
|
+
const spectaUrl = new URL(URLExt.join(labBaseUrl, 'specta'), window.location.origin);
|
|
104
|
+
spectaUrl.searchParams.set('no-tree', 'true');
|
|
105
|
+
spectaUrl.searchParams.set('spk-link', '');
|
|
106
|
+
spectaUrl.hash = encodedData;
|
|
107
|
+
await navigator.clipboard.writeText(spectaUrl.toString());
|
|
108
|
+
Notification.success('Link copied to clipboard', {
|
|
109
|
+
autoClose: 3000
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export function addLauncherCommands(options) {
|
|
115
|
+
const { app, launcher, framework, icon, rank } = options;
|
|
116
|
+
const { commands } = app;
|
|
117
|
+
const commandId = `jupyterpack:create-${framework}-app`;
|
|
118
|
+
const frameworkName = framework.charAt(0).toUpperCase() + framework.slice(1);
|
|
119
|
+
commands.addCommand(commandId, {
|
|
120
|
+
label: `${frameworkName} App`,
|
|
121
|
+
icon: icon,
|
|
122
|
+
caption: `Create a new ${frameworkName} Application`,
|
|
123
|
+
execute: async (args) => {
|
|
124
|
+
const cwd = args['cwd'];
|
|
125
|
+
await generateAppFiles({
|
|
126
|
+
contentsManager: app.serviceManager.contents,
|
|
127
|
+
cwd,
|
|
128
|
+
framework
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
launcher.add({
|
|
133
|
+
command: commandId,
|
|
134
|
+
category: 'JupyterPack',
|
|
135
|
+
rank
|
|
136
|
+
});
|
|
76
137
|
}
|
package/lib/document/plugin.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
|
2
2
|
import { IJupyterpackDocTracker } from '../type';
|
|
3
3
|
export declare const spkPlugin: JupyterFrontEndPlugin<IJupyterpackDocTracker>;
|
|
4
4
|
export declare const launcherPlugin: JupyterFrontEndPlugin<void>;
|
|
5
|
+
export declare const spkFromLink: JupyterFrontEndPlugin<void>;
|
package/lib/document/plugin.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { JupyterPackFramework } from './../type';
|
|
2
1
|
import { WidgetTracker } from '@jupyterlab/apputils';
|
|
3
2
|
import { ILauncher } from '@jupyterlab/launcher';
|
|
3
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
4
4
|
import { IConnectionManagerToken, IJupyterpackDocTrackerToken } from '../token';
|
|
5
|
-
import { dashIcon, logoIcon, panelIcon, shinyIcon, streamlitIcon } from '../tools';
|
|
6
|
-
import {
|
|
5
|
+
import { dashIcon, decodeSpk, fasthtmlIcon, logoIcon, panelIcon, shinyIcon, streamlitIcon, textualIcon, vizroIcon } from '../tools';
|
|
6
|
+
import { JupyterPackFramework } from '../type';
|
|
7
|
+
import { addCommands, addLauncherCommands } from './commands';
|
|
7
8
|
import { JupyterPackWidgetFactory } from './widgetFactory';
|
|
8
|
-
import {
|
|
9
|
+
import { spkFactory } from './templates/spk';
|
|
10
|
+
import { newFile } from './templates';
|
|
9
11
|
const FACTORY = 'jupyterpack';
|
|
10
12
|
const CONTENT_TYPE = 'jupyterpack';
|
|
11
13
|
export const spkPlugin = {
|
|
@@ -17,7 +19,7 @@ export const spkPlugin = {
|
|
|
17
19
|
const tracker = new WidgetTracker({
|
|
18
20
|
namespace: FACTORY
|
|
19
21
|
});
|
|
20
|
-
addCommands(app.commands, tracker);
|
|
22
|
+
addCommands(app.commands, tracker, app.serviceManager.contents);
|
|
21
23
|
const widgetFactory = new JupyterPackWidgetFactory({
|
|
22
24
|
name: FACTORY,
|
|
23
25
|
modelName: 'text',
|
|
@@ -50,6 +52,7 @@ export const spkPlugin = {
|
|
|
50
52
|
return tracker;
|
|
51
53
|
}
|
|
52
54
|
};
|
|
55
|
+
const CREATE_SPK_COMMAND_ID = 'jupyterpack:create-new-file';
|
|
53
56
|
export const launcherPlugin = {
|
|
54
57
|
id: 'jupyterpack:spklauncher',
|
|
55
58
|
optional: [ILauncher],
|
|
@@ -60,122 +63,101 @@ export const launcherPlugin = {
|
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
65
|
const { commands } = app;
|
|
63
|
-
|
|
64
|
-
const dashCommandId = 'jupyterpack:create-dash-app';
|
|
65
|
-
const streamlitCommandId = 'jupyterpack:create-streamlit-app';
|
|
66
|
-
const shinyCommandId = 'jupyterpack:create-shiny-app';
|
|
67
|
-
const panelCommandId = 'jupyterpack:create-panel-app';
|
|
68
|
-
commands.addCommand(commandId, {
|
|
66
|
+
commands.addCommand(CREATE_SPK_COMMAND_ID, {
|
|
69
67
|
label: 'New SPK File',
|
|
70
68
|
icon: logoIcon,
|
|
71
69
|
caption: 'Create a new SPK fike',
|
|
72
70
|
execute: async (args) => {
|
|
73
|
-
|
|
71
|
+
var _a, _b, _c;
|
|
72
|
+
const cwd = ((_a = args['cwd']) !== null && _a !== void 0 ? _a : '');
|
|
74
73
|
const contentsManager = app.serviceManager.contents;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ext: '.spk'
|
|
74
|
+
const spkContent = (_b = args['spkContent']) !== null && _b !== void 0 ? _b : spkFactory({
|
|
75
|
+
name: 'Untitled',
|
|
76
|
+
framework: ((_c = args['framework']) !== null && _c !== void 0 ? _c : '')
|
|
79
77
|
});
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
"name": "${model.name}",
|
|
83
|
-
"entry": "",
|
|
84
|
-
"framework": "",
|
|
85
|
-
"rootUrl": "/",
|
|
86
|
-
"metadata": {
|
|
87
|
-
"autoreload": true
|
|
88
|
-
},
|
|
89
|
-
"dependencies": {
|
|
90
|
-
"mamba": [],
|
|
91
|
-
"pip": []
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
`;
|
|
95
|
-
model = await contentsManager.save(model.path, {
|
|
96
|
-
...model,
|
|
97
|
-
format: 'text',
|
|
98
|
-
size: undefined,
|
|
99
|
-
content: spkContent
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
commands.addCommand(dashCommandId, {
|
|
104
|
-
label: 'Dash App',
|
|
105
|
-
icon: dashIcon,
|
|
106
|
-
caption: 'Create a new Dash Application',
|
|
107
|
-
execute: async (args) => {
|
|
108
|
-
const cwd = args['cwd'];
|
|
109
|
-
await generateAppFiles({
|
|
110
|
-
contentsManager: app.serviceManager.contents,
|
|
111
|
-
cwd,
|
|
112
|
-
framework: JupyterPackFramework.DASH
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
commands.addCommand(streamlitCommandId, {
|
|
117
|
-
label: 'Streamlit App',
|
|
118
|
-
icon: streamlitIcon,
|
|
119
|
-
caption: 'Create a new Streamlit Application',
|
|
120
|
-
execute: async (args) => {
|
|
121
|
-
const cwd = args['cwd'];
|
|
122
|
-
await generateAppFiles({
|
|
123
|
-
contentsManager: app.serviceManager.contents,
|
|
124
|
-
cwd,
|
|
125
|
-
framework: JupyterPackFramework.STREAMLIT
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
commands.addCommand(shinyCommandId, {
|
|
130
|
-
label: 'Shiny App',
|
|
131
|
-
icon: shinyIcon,
|
|
132
|
-
caption: 'Create a new Shiny Application',
|
|
133
|
-
execute: async (args) => {
|
|
134
|
-
const cwd = args['cwd'];
|
|
135
|
-
await generateAppFiles({
|
|
136
|
-
contentsManager: app.serviceManager.contents,
|
|
137
|
-
cwd,
|
|
138
|
-
framework: JupyterPackFramework.SHINY
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
commands.addCommand(panelCommandId, {
|
|
143
|
-
label: 'Panel App',
|
|
144
|
-
icon: panelIcon,
|
|
145
|
-
caption: 'Create a new Panel Application',
|
|
146
|
-
execute: async (args) => {
|
|
147
|
-
const cwd = args['cwd'];
|
|
148
|
-
await generateAppFiles({
|
|
149
|
-
contentsManager: app.serviceManager.contents,
|
|
78
|
+
await newFile({
|
|
150
79
|
cwd,
|
|
151
|
-
|
|
80
|
+
ext: '.spk',
|
|
81
|
+
name: args['name'],
|
|
82
|
+
content: spkContent,
|
|
83
|
+
contentsManager
|
|
152
84
|
});
|
|
153
85
|
}
|
|
154
86
|
});
|
|
155
87
|
launcher.add({
|
|
156
|
-
command:
|
|
88
|
+
command: CREATE_SPK_COMMAND_ID,
|
|
157
89
|
category: 'JupyterPack',
|
|
158
90
|
rank: 1
|
|
159
91
|
});
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
category: 'JupyterPack',
|
|
178
|
-
rank: 5
|
|
92
|
+
const iconMap = {
|
|
93
|
+
[JupyterPackFramework.DASH]: dashIcon,
|
|
94
|
+
[JupyterPackFramework.STREAMLIT]: streamlitIcon,
|
|
95
|
+
[JupyterPackFramework.SHINY]: shinyIcon,
|
|
96
|
+
[JupyterPackFramework.PANEL]: panelIcon,
|
|
97
|
+
[JupyterPackFramework.TEXTUAL]: textualIcon,
|
|
98
|
+
[JupyterPackFramework.VIZRO]: vizroIcon,
|
|
99
|
+
[JupyterPackFramework.FASTHTML]: fasthtmlIcon
|
|
100
|
+
};
|
|
101
|
+
Object.entries(iconMap).forEach(([framework, icon], index) => {
|
|
102
|
+
addLauncherCommands({
|
|
103
|
+
app,
|
|
104
|
+
launcher,
|
|
105
|
+
framework: framework,
|
|
106
|
+
icon,
|
|
107
|
+
rank: index + 2
|
|
108
|
+
});
|
|
179
109
|
});
|
|
180
110
|
}
|
|
181
111
|
};
|
|
112
|
+
export const spkFromLink = {
|
|
113
|
+
id: 'jupyterpack:spkFromLink',
|
|
114
|
+
requires: [IDocumentManager],
|
|
115
|
+
autoStart: true,
|
|
116
|
+
activate: (app, docManager) => {
|
|
117
|
+
const queryParams = new URLSearchParams(window.location.search);
|
|
118
|
+
const spkLink = queryParams.has('spk-link');
|
|
119
|
+
if (spkLink && window.location.hash) {
|
|
120
|
+
const linkData = window.location.hash.slice(1);
|
|
121
|
+
app.restored.then(async () => {
|
|
122
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
123
|
+
const { spk, entry } = decodeSpk(linkData);
|
|
124
|
+
const contentsManager = app.serviceManager.contents;
|
|
125
|
+
await newFile({
|
|
126
|
+
cwd: '',
|
|
127
|
+
name: '__entry_from_link__.py',
|
|
128
|
+
ext: '.py',
|
|
129
|
+
content: entry,
|
|
130
|
+
contentsManager,
|
|
131
|
+
overwrite: true
|
|
132
|
+
});
|
|
133
|
+
spk.name = '__spk_from_link__';
|
|
134
|
+
spk.entry = '__entry_from_link__.py';
|
|
135
|
+
const spkContent = JSON.stringify(spk, null, 2);
|
|
136
|
+
await newFile({
|
|
137
|
+
cwd: '',
|
|
138
|
+
name: '__spk_from_link__.spk',
|
|
139
|
+
ext: '.spk',
|
|
140
|
+
content: spkContent,
|
|
141
|
+
contentsManager,
|
|
142
|
+
overwrite: true
|
|
143
|
+
});
|
|
144
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
145
|
+
let count = 0;
|
|
146
|
+
const tryOpen = () => {
|
|
147
|
+
const widget = docManager.openOrReveal('__spk_from_link__.spk', 'default');
|
|
148
|
+
if (widget) {
|
|
149
|
+
app.shell.add(widget, 'main');
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
count++;
|
|
153
|
+
if (count > 10) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
setTimeout(tryOpen, 100);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
tryOpen();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const FASTHTML_APP = "\nfrom fasthtml.common import Div, P, fast_app\n\napp, rt = fast_app()\n\n\n@rt(\"/\")\nasync def get():\n return Div(P(\"Hello World!\"))\n\n";
|
|
@@ -5,3 +5,11 @@ export declare function generateAppFiles(options: {
|
|
|
5
5
|
framework: JupyterPackFramework;
|
|
6
6
|
cwd: string;
|
|
7
7
|
}): Promise<void>;
|
|
8
|
+
export declare function newFile(options: {
|
|
9
|
+
cwd: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
ext: string;
|
|
12
|
+
content: string;
|
|
13
|
+
contentsManager: Contents.IManager;
|
|
14
|
+
overwrite?: boolean;
|
|
15
|
+
}): Promise<void>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { PathExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { newDirectory, pathExists } from '../../tools';
|
|
1
3
|
import { JupyterPackFramework } from '../../type';
|
|
2
4
|
import { DASH_APP } from './dash';
|
|
3
|
-
import { STREAMLIT_APP } from './streamlit';
|
|
4
|
-
import { SHINY_APP } from './shiny';
|
|
5
|
-
import { newDirectory } from '../../tools';
|
|
6
|
-
import { PathExt } from '@jupyterlab/coreutils';
|
|
7
5
|
import { PANEL_APP } from './panel';
|
|
6
|
+
import { SHINY_APP } from './shiny';
|
|
7
|
+
import { STREAMLIT_APP } from './streamlit';
|
|
8
|
+
import { TEXTUAL_APP } from './textual';
|
|
9
|
+
import { VIZRO_APP } from './vizro';
|
|
10
|
+
import { FASTHTML_APP } from './fasthtml';
|
|
8
11
|
export async function generateAppFiles(options) {
|
|
9
12
|
const { contentsManager, framework, cwd } = options;
|
|
10
13
|
const newPath = await newDirectory({
|
|
@@ -35,6 +38,18 @@ export async function generateAppFiles(options) {
|
|
|
35
38
|
appContent = PANEL_APP;
|
|
36
39
|
break;
|
|
37
40
|
}
|
|
41
|
+
case JupyterPackFramework.TEXTUAL: {
|
|
42
|
+
appContent = TEXTUAL_APP;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case JupyterPackFramework.VIZRO: {
|
|
46
|
+
appContent = VIZRO_APP;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case JupyterPackFramework.FASTHTML: {
|
|
50
|
+
appContent = FASTHTML_APP;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
38
53
|
default:
|
|
39
54
|
break;
|
|
40
55
|
}
|
|
@@ -68,3 +83,23 @@ export async function generateAppFiles(options) {
|
|
|
68
83
|
});
|
|
69
84
|
await contentsManager.rename(model.path, PathExt.join(newPath, 'entrypoint.spk'));
|
|
70
85
|
}
|
|
86
|
+
export async function newFile(options) {
|
|
87
|
+
const { cwd, name, content, contentsManager, ext, overwrite } = options;
|
|
88
|
+
let model = await contentsManager.newUntitled({
|
|
89
|
+
path: cwd,
|
|
90
|
+
type: 'file',
|
|
91
|
+
ext
|
|
92
|
+
});
|
|
93
|
+
model = await contentsManager.save(model.path, {
|
|
94
|
+
...model,
|
|
95
|
+
format: 'text',
|
|
96
|
+
size: undefined,
|
|
97
|
+
content
|
|
98
|
+
});
|
|
99
|
+
if (name) {
|
|
100
|
+
if (overwrite && (await pathExists(cwd, name, contentsManager))) {
|
|
101
|
+
await contentsManager.delete(PathExt.join(cwd, name));
|
|
102
|
+
}
|
|
103
|
+
await contentsManager.rename(model.path, PathExt.join(cwd, name));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const spkFactory = (options) => {
|
|
2
|
+
const name = options.name ? `"${options.name}"` : '';
|
|
3
|
+
const entry = options.entry ? `"${options.entry}"` : '';
|
|
4
|
+
const framework = options.framework ? `"${options.framework}"` : '';
|
|
5
|
+
return `
|
|
6
|
+
{
|
|
7
|
+
"name": ${name},
|
|
8
|
+
"entry": ${entry},
|
|
9
|
+
"framework": ${framework},
|
|
10
|
+
"rootUrl": "/",
|
|
11
|
+
"metadata": {
|
|
12
|
+
"autoreload": true
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"mamba": [],
|
|
16
|
+
"pip": []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TEXTUAL_APP = "\n# This example is taken from the Textual official repository (https://github.com/Textualize/textual-demo/blob/main/src/textual_demo)\n\nfrom textual.demo.demo_app import DemoApp\n\napp = DemoApp()\n";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VIZRO_APP = "\n# This example is taken from the vizro official documentation (https://vizro.readthedocs.io/en/stable/pages/tutorials/quickstart-tutorial)\n\nimport vizro.plotly.express as px\nfrom vizro import Vizro\nimport vizro.models as vm\n\ndf = px.data.iris()\n\npage = vm.Page(\n title=\"My first dashboard\",\n components=[\n vm.Graph(\n figure=px.scatter(df, x=\"sepal_length\", y=\"petal_width\", color=\"species\")\n ),\n vm.Graph(figure=px.histogram(df, x=\"sepal_width\", color=\"species\")),\n ],\n controls=[\n vm.Filter(column=\"species\"),\n ],\n)\n\ndashboard = vm.Dashboard(pages=[page])\nVizro().build(dashboard).run()\n\n";
|