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.
Files changed (94) hide show
  1. package/README.md +97 -19
  2. package/lib/document/commands.d.ts +14 -2
  3. package/lib/document/commands.js +66 -5
  4. package/lib/document/plugin.d.ts +1 -0
  5. package/lib/document/plugin.js +89 -107
  6. package/lib/document/templates/fasthtml.d.ts +1 -0
  7. package/lib/document/templates/fasthtml.js +11 -0
  8. package/lib/document/templates/index.d.ts +8 -0
  9. package/lib/document/templates/index.js +39 -4
  10. package/lib/document/templates/spk.d.ts +5 -0
  11. package/lib/document/templates/spk.js +20 -0
  12. package/lib/document/templates/textual.d.ts +1 -0
  13. package/lib/document/templates/textual.js +7 -0
  14. package/lib/document/templates/vizro.d.ts +1 -0
  15. package/lib/document/templates/vizro.js +26 -0
  16. package/lib/document/toolbar.js +4 -0
  17. package/lib/document/widgetFactory.js +5 -1
  18. package/lib/index.js +2 -2
  19. package/lib/pythonServer/baseServer.d.ts +4 -1
  20. package/lib/pythonServer/baseServer.js +21 -22
  21. package/lib/pythonServer/dash/dashServer.d.ts +2 -1
  22. package/lib/pythonServer/dash/dashServer.js +13 -6
  23. package/lib/pythonServer/fastapi/deps.d.ts +2 -0
  24. package/lib/pythonServer/fastapi/deps.js +4 -0
  25. package/lib/pythonServer/fastapi/fastapiServer.d.ts +6 -0
  26. package/lib/pythonServer/fastapi/fastapiServer.js +16 -0
  27. package/lib/pythonServer/fasthtml/deps.d.ts +2 -0
  28. package/lib/pythonServer/fasthtml/deps.js +11 -0
  29. package/lib/pythonServer/fasthtml/fasthtmlServer.d.ts +6 -0
  30. package/lib/pythonServer/fasthtml/fasthtmlServer.js +16 -0
  31. package/lib/pythonServer/index.js +9 -1
  32. package/lib/pythonServer/kernelExecutor.js +3 -3
  33. package/lib/pythonServer/panel/panelServer.d.ts +2 -1
  34. package/lib/pythonServer/panel/panelServer.js +5 -2
  35. package/lib/pythonServer/shiny/shinyServer.d.ts +2 -1
  36. package/lib/pythonServer/shiny/shinyServer.js +5 -2
  37. package/lib/pythonServer/starlette/starletteServer.d.ts +2 -0
  38. package/lib/pythonServer/starlette/starletteServer.js +7 -4
  39. package/lib/pythonServer/streamlit/streamlitServer.d.ts +2 -1
  40. package/lib/pythonServer/streamlit/streamlitServer.js +5 -2
  41. package/lib/pythonServer/textual/deps.d.ts +2 -0
  42. package/lib/pythonServer/textual/deps.js +4 -0
  43. package/lib/pythonServer/textual/textualServer.d.ts +11 -0
  44. package/lib/pythonServer/textual/textualServer.js +52 -0
  45. package/lib/pythonServer/tornado/tornadoServer.d.ts +2 -0
  46. package/lib/pythonServer/tornado/tornadoServer.js +5 -2
  47. package/lib/pythonServer/vizro/deps.d.ts +2 -0
  48. package/lib/pythonServer/vizro/deps.js +4 -0
  49. package/lib/pythonServer/vizro/vizroServer.d.ts +10 -0
  50. package/lib/pythonServer/vizro/vizroServer.js +25 -0
  51. package/lib/tools.d.ts +15 -1
  52. package/lib/tools.js +49 -5
  53. package/lib/type.d.ts +6 -1
  54. package/lib/type.js +4 -0
  55. package/package.json +3 -1
  56. package/src/document/commands.ts +90 -7
  57. package/src/document/plugin.ts +103 -112
  58. package/src/document/templates/fasthtml.ts +11 -0
  59. package/src/document/templates/index.ts +48 -4
  60. package/src/document/templates/spk.ts +25 -0
  61. package/src/document/templates/textual.ts +7 -0
  62. package/src/document/templates/vizro.ts +26 -0
  63. package/src/document/toolbar.ts +7 -0
  64. package/src/document/widgetFactory.ts +5 -1
  65. package/src/index.ts +2 -2
  66. package/src/pythonServer/baseServer.ts +23 -27
  67. package/src/pythonServer/dash/dashServer.ts +11 -9
  68. package/src/pythonServer/fastapi/deps.ts +6 -0
  69. package/src/pythonServer/fastapi/fastapiServer.ts +14 -0
  70. package/src/pythonServer/fasthtml/deps.ts +13 -0
  71. package/src/pythonServer/fasthtml/fasthtmlServer.ts +15 -0
  72. package/src/pythonServer/index.ts +9 -1
  73. package/src/pythonServer/kernelExecutor.ts +3 -4
  74. package/src/pythonServer/panel/panelServer.ts +2 -2
  75. package/src/pythonServer/shiny/shinyServer.ts +2 -2
  76. package/src/pythonServer/starlette/starletteServer.ts +5 -4
  77. package/src/pythonServer/streamlit/streamlitServer.ts +2 -2
  78. package/src/pythonServer/textual/deps.ts +6 -0
  79. package/src/pythonServer/textual/textualServer.ts +62 -0
  80. package/src/pythonServer/tornado/tornadoServer.ts +2 -2
  81. package/src/pythonServer/vizro/deps.ts +5 -0
  82. package/src/pythonServer/vizro/vizroServer.ts +28 -0
  83. package/src/tools.ts +78 -6
  84. package/src/type.ts +6 -1
  85. package/src/websocket/websocket.ts +0 -1
  86. package/style/base.css +4 -0
  87. package/style/icons/Plotly-Logo-Black.svg +1 -1
  88. package/style/icons/copy.svg +1 -0
  89. package/style/icons/fasthtml.svg +62 -0
  90. package/style/icons/panel_logo_stacked.svg +1 -1
  91. package/style/icons/shiny-for-python.svg +1 -1
  92. package/style/icons/streamlit-logo-primary.svg +1 -1
  93. package/style/icons/textual.svg +12 -0
  94. package/style/icons/vizro.svg +18 -0
package/README.md CHANGED
@@ -3,15 +3,32 @@
3
3
  [![Github Actions Status](https://github.com/trungleduc/jupyterpack/workflows/Build/badge.svg)](https://github.com/trungleduc/specta/actions/workflows/build.yml)
4
4
  [![Try on lite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://trungleduc.github.io/jupyterpack/lab/)
5
5
 
6
- <h2 align="center"> A JupyterLite extension to serve in-browser Python and Javascript web application</h2>
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
+ ![Image](https://github.com/user-attachments/assets/22849fe8-199f-4d9f-ad45-055bccf88bad)
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 Dash, Streamlit and Shiny for Python. You can also use `jupyterpack` to serve any `Starlette` or `Tornado` application.
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
- ![Image](https://github.com/user-attachments/assets/22849fe8-199f-4d9f-ad45-055bccf88bad)
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
- ## Set up JupyterLite deployment
45
+ ## Try it online!
46
+
47
+ You can try it online by clicking on this badge:
48
+
49
+ [![Try on lite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](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
- | `tornado` | Tornado web application |
170
- | `shiny` | Shiny application (Python) |
171
- | `starlette` | Starlette ASGI application |
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 follow a similar structure to Dash apps.
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
- ## Try it online!
326
+ ### Panel application
259
327
 
260
- You can try it online by clicking on this badge:
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
- [![Try on lite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://trungleduc.github.io/jupyterpack/lab/)
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;
@@ -1,10 +1,13 @@
1
- import { refreshIcon } from '@jupyterlab/ui-components';
2
- import { autoReloadIcon, linkIcon } from '../tools';
3
- import { PageConfig, URLExt } from '@jupyterlab/coreutils';
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
  }
@@ -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>;
@@ -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 { addCommands } from './commands';
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 { generateAppFiles } from './templates';
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
- const commandId = 'jupyterpack:create-new-file';
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
- const cwd = args['cwd'];
71
+ var _a, _b, _c;
72
+ const cwd = ((_a = args['cwd']) !== null && _a !== void 0 ? _a : '');
74
73
  const contentsManager = app.serviceManager.contents;
75
- let model = await contentsManager.newUntitled({
76
- path: cwd,
77
- type: 'file',
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
- const spkContent = `
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
- framework: JupyterPackFramework.PANEL
80
+ ext: '.spk',
81
+ name: args['name'],
82
+ content: spkContent,
83
+ contentsManager
152
84
  });
153
85
  }
154
86
  });
155
87
  launcher.add({
156
- command: commandId,
88
+ command: CREATE_SPK_COMMAND_ID,
157
89
  category: 'JupyterPack',
158
90
  rank: 1
159
91
  });
160
- launcher.add({
161
- command: dashCommandId,
162
- category: 'JupyterPack',
163
- rank: 2
164
- });
165
- launcher.add({
166
- command: streamlitCommandId,
167
- category: 'JupyterPack',
168
- rank: 3
169
- });
170
- launcher.add({
171
- command: shinyCommandId,
172
- category: 'JupyterPack',
173
- rank: 4
174
- });
175
- launcher.add({
176
- command: panelCommandId,
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";
@@ -0,0 +1,11 @@
1
+ export const FASTHTML_APP = `
2
+ from fasthtml.common import Div, P, fast_app
3
+
4
+ app, rt = fast_app()
5
+
6
+
7
+ @rt("/")
8
+ async def get():
9
+ return Div(P("Hello World!"))
10
+
11
+ `;
@@ -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,5 @@
1
+ export declare const spkFactory: (options: {
2
+ name?: string;
3
+ entry?: string;
4
+ framework?: string;
5
+ }) => string;
@@ -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,7 @@
1
+ export const TEXTUAL_APP = `
2
+ # This example is taken from the Textual official repository (https://github.com/Textualize/textual-demo/blob/main/src/textual_demo)
3
+
4
+ from textual.demo.demo_app import DemoApp
5
+
6
+ app = DemoApp()
7
+ `;
@@ -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";