marimo-jupyter-extension 0.1.0__tar.gz → 0.1.1__tar.gz
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.
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/PKG-INFO +13 -5
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/README.md +12 -4
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/docs/index.md +9 -1
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/docs/installation.md +4 -4
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/docs/jupyterhub.md +1 -1
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/docs/troubleshooting.md +2 -2
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/src/icons.ts +9 -0
- marimo_jupyter_extension-0.1.1/labextension/src/iframe-widget.ts +377 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/src/index.ts +186 -54
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/src/widget-factory.ts +11 -13
- marimo_jupyter_extension-0.1.1/labextension/style/marimo-file-icon.svg +3 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/__init__.py +1 -1
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/executable.py +1 -1
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/handlers.py +1 -1
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/labextension/package.json +1 -1
- marimo_jupyter_extension-0.1.1/marimo_jupyter_extension/labextension/static/71.4d6949ab423600535682.js +1 -0
- marimo_jupyter_extension-0.1.1/marimo_jupyter_extension/labextension/static/remoteEntry.6ae67aa952d37b1fa69f.js +1 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/pyproject.toml +1 -1
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/scripts/test-environments.sh +87 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_executable.py +7 -3
- marimo_jupyter_extension-0.1.0/labextension/src/iframe-widget.ts +0 -84
- marimo_jupyter_extension-0.1.0/marimo_jupyter_extension/labextension/static/510.5a6289689ce7a0453fc4.js +0 -1
- marimo_jupyter_extension-0.1.0/marimo_jupyter_extension/labextension/static/remoteEntry.705371d091b7d314fd9e.js +0 -1
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/.gitignore +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/LICENSE +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/NOTICE +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/biome.jsonc +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/docs/configuration.md +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/README.md +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/bin/README.md +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/jupyterhub/README.md +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/jupyterhub/jupyterhub.service +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/jupyterhub/jupyterhub_config.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/jupyterhub/nginx.conf +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/jupyterhub/package.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/jupyterhub/pyproject.toml +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/notebooks/README.md +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/example/opt/notebooks/demo/.gitkeep +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/install.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/.eslintrc.cjs +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/.npmrc +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/package-lock.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/package.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/pnpm-lock.yaml +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/schema/plugin.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/scripts/jupyter +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/src/sidebar.ts +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/src/svg.d.ts +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/style/base.css +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/style/marimo.svg +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/tsconfig.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/labextension/yarn.lock +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/config.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/convert.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/etc/jupyter_server_config.d/marimo_jupyter_extension.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/exporter.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/icon.svg +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/labextension/schemas/@marimo-team/jupyter-extension/package.json.orig +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/labextension/schemas/@marimo-team/jupyter-extension/plugin.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/labextension/static/style.js +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/marimo_jupyter_extension/labextension/static/third-party-licenses.json +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/mkdocs.yml +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/screenshot.png +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/scripts/release.sh +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/conftest.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_config.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_convert.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_entry_point.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_exporter.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_handlers.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_integration.py +0 -0
- {marimo_jupyter_extension-0.1.0 → marimo_jupyter_extension-0.1.1}/tests/test_setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: marimo-jupyter-extension
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Jupyter extension to proxy marimo with JupyterLab integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/marimo-team/marimo-jupyter-extension
|
|
6
6
|
Project-URL: Documentation, https://marimo-team.github.io/marimo-jupyter-extension/
|
|
@@ -59,6 +59,7 @@ Description-Content-Type: text/markdown
|
|
|
59
59
|
**Highlights**
|
|
60
60
|
|
|
61
61
|
- 🚀 **Launcher Integration** - marimo appears in the JupyterLab launcher with its own icon
|
|
62
|
+
- 🍃 **First-Class Marimo Notebook Support** - Double-click `_mo.py` files to open directly in marimo
|
|
62
63
|
- 📊 **Sidebar Panel** - Monitor server status, view running sessions, and quick actions
|
|
63
64
|
- 🐍 **Venv Selection** - Choose Python environment when creating new notebooks (with PEP 723 metadata)
|
|
64
65
|
- 📁 **Context Menus** - Right-click `.py` files to edit with marimo, `.ipynb` files to convert
|
|
@@ -69,7 +70,7 @@ Description-Content-Type: text/markdown
|
|
|
69
70
|
## Quick Start
|
|
70
71
|
|
|
71
72
|
```bash
|
|
72
|
-
uv pip install 'marimo[sandbox]>=0.19.
|
|
73
|
+
uv pip install 'marimo[sandbox]>=0.19.8' marimo-jupyter-extension
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
Launch JupyterLab and click the marimo icon in the launcher, or use the sidebar panel.
|
|
@@ -90,9 +91,16 @@ Create new marimo notebooks from the launcher. The sidebar shows server status,
|
|
|
90
91
|
|
|
91
92
|
When creating a new notebook, select from available Python environments. The extension discovers Jupyter kernel specs and embeds the venv path using PEP 723 script metadata.
|
|
92
93
|
|
|
94
|
+
### File Type Handling
|
|
95
|
+
|
|
96
|
+
| File Type | Double-click Behavior | "Open With" Menu |
|
|
97
|
+
|-----------|----------------------|------------------|
|
|
98
|
+
| `_mo.py` | Opens in marimo | marimo available |
|
|
99
|
+
| `.py` | Opens in standard editor | marimo available |
|
|
100
|
+
|
|
93
101
|
### Context Menu Actions
|
|
94
102
|
|
|
95
|
-
- **Edit with marimo**: Right-click any `.py` file to open it in the marimo editor
|
|
103
|
+
- **Edit with marimo**: Right-click any `.py` or `_mo.py` file to open it in the marimo editor
|
|
96
104
|
- **Convert to marimo**: Right-click any `.ipynb` file to convert it to marimo format
|
|
97
105
|
|
|
98
106
|
## Installation
|
|
@@ -102,7 +110,7 @@ See [Installation Guide](https://marimo-team.github.io/marimo-jupyter-extension/
|
|
|
102
110
|
### Single Environment
|
|
103
111
|
|
|
104
112
|
```bash
|
|
105
|
-
uv pip install 'marimo[sandbox]>=0.19.
|
|
113
|
+
uv pip install 'marimo[sandbox]>=0.19.8' marimo-jupyter-extension
|
|
106
114
|
```
|
|
107
115
|
|
|
108
116
|
### Multiple Environments (JupyterHub)
|
|
@@ -160,7 +168,7 @@ See [Troubleshooting Guide](https://marimo-team.github.io/marimo-jupyter-extensi
|
|
|
160
168
|
| marimo icon missing | Install `marimo-jupyter-extension` in Jupyter's environment |
|
|
161
169
|
| marimo fails to launch | Ensure marimo is in PATH or configure `MarimoProxyConfig.marimo_path` |
|
|
162
170
|
| Modules not found | Install marimo in the same environment as your packages |
|
|
163
|
-
| Sandbox features not working | Upgrade to `marimo[sandbox]>=0.19.
|
|
171
|
+
| Sandbox features not working | Upgrade to `marimo[sandbox]>=0.19.8` |
|
|
164
172
|
|
|
165
173
|
## Community
|
|
166
174
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
**Highlights**
|
|
20
20
|
|
|
21
21
|
- 🚀 **Launcher Integration** - marimo appears in the JupyterLab launcher with its own icon
|
|
22
|
+
- 🍃 **First-Class Marimo Notebook Support** - Double-click `_mo.py` files to open directly in marimo
|
|
22
23
|
- 📊 **Sidebar Panel** - Monitor server status, view running sessions, and quick actions
|
|
23
24
|
- 🐍 **Venv Selection** - Choose Python environment when creating new notebooks (with PEP 723 metadata)
|
|
24
25
|
- 📁 **Context Menus** - Right-click `.py` files to edit with marimo, `.ipynb` files to convert
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
## Quick Start
|
|
30
31
|
|
|
31
32
|
```bash
|
|
32
|
-
uv pip install 'marimo[sandbox]>=0.19.
|
|
33
|
+
uv pip install 'marimo[sandbox]>=0.19.8' marimo-jupyter-extension
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
Launch JupyterLab and click the marimo icon in the launcher, or use the sidebar panel.
|
|
@@ -50,9 +51,16 @@ Create new marimo notebooks from the launcher. The sidebar shows server status,
|
|
|
50
51
|
|
|
51
52
|
When creating a new notebook, select from available Python environments. The extension discovers Jupyter kernel specs and embeds the venv path using PEP 723 script metadata.
|
|
52
53
|
|
|
54
|
+
### File Type Handling
|
|
55
|
+
|
|
56
|
+
| File Type | Double-click Behavior | "Open With" Menu |
|
|
57
|
+
|-----------|----------------------|------------------|
|
|
58
|
+
| `_mo.py` | Opens in marimo | marimo available |
|
|
59
|
+
| `.py` | Opens in standard editor | marimo available |
|
|
60
|
+
|
|
53
61
|
### Context Menu Actions
|
|
54
62
|
|
|
55
|
-
- **Edit with marimo**: Right-click any `.py` file to open it in the marimo editor
|
|
63
|
+
- **Edit with marimo**: Right-click any `.py` or `_mo.py` file to open it in the marimo editor
|
|
56
64
|
- **Convert to marimo**: Right-click any `.ipynb` file to convert it to marimo format
|
|
57
65
|
|
|
58
66
|
## Installation
|
|
@@ -62,7 +70,7 @@ See [Installation Guide](https://marimo-team.github.io/marimo-jupyter-extension/
|
|
|
62
70
|
### Single Environment
|
|
63
71
|
|
|
64
72
|
```bash
|
|
65
|
-
uv pip install 'marimo[sandbox]>=0.19.
|
|
73
|
+
uv pip install 'marimo[sandbox]>=0.19.8' marimo-jupyter-extension
|
|
66
74
|
```
|
|
67
75
|
|
|
68
76
|
### Multiple Environments (JupyterHub)
|
|
@@ -120,7 +128,7 @@ See [Troubleshooting Guide](https://marimo-team.github.io/marimo-jupyter-extensi
|
|
|
120
128
|
| marimo icon missing | Install `marimo-jupyter-extension` in Jupyter's environment |
|
|
121
129
|
| marimo fails to launch | Ensure marimo is in PATH or configure `MarimoProxyConfig.marimo_path` |
|
|
122
130
|
| Modules not found | Install marimo in the same environment as your packages |
|
|
123
|
-
| Sandbox features not working | Upgrade to `marimo[sandbox]>=0.19.
|
|
131
|
+
| Sandbox features not working | Upgrade to `marimo[sandbox]>=0.19.8` |
|
|
124
132
|
|
|
125
133
|
## Community
|
|
126
134
|
|
|
@@ -11,7 +11,7 @@ On JupyterHub deployments, this leverages existing authentication and spawning i
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
pip install 'marimo>=0.19.
|
|
14
|
+
pip install 'marimo>=0.19.8' marimo-jupyter-extension
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Launch JupyterLab and click the marimo icon in the launcher.
|
|
@@ -19,6 +19,7 @@ Launch JupyterLab and click the marimo icon in the launcher.
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
21
|
- **JupyterLab Integration**: marimo appears in the launcher with its own icon
|
|
22
|
+
- **First-Class Marimo Notebook Support**: `_mo.py` files are recognized as Marimo notebooks and open in marimo by default on double-click
|
|
22
23
|
- **Sidebar Panel**: Server status, running sessions, and quick actions
|
|
23
24
|
- **Venv Selection**: Choose Python environment when creating new notebooks
|
|
24
25
|
- **Context Menus**: Right-click to edit .py files or convert .ipynb files
|
|
@@ -27,6 +28,13 @@ Launch JupyterLab and click the marimo icon in the launcher.
|
|
|
27
28
|
- **Sandbox Mode**: Run marimo in isolated environments with uvx
|
|
28
29
|
- **Flexible PATH**: Configure executable search paths via environment variables or config files
|
|
29
30
|
|
|
31
|
+
## File Type Handling
|
|
32
|
+
|
|
33
|
+
| File Type | Double-click Behavior | "Open With" Menu |
|
|
34
|
+
|-----------|----------------------|------------------|
|
|
35
|
+
| `_mo.py` | Opens in marimo | marimo available |
|
|
36
|
+
| `.py` | Opens in standard editor | marimo available |
|
|
37
|
+
|
|
30
38
|
## Next Steps
|
|
31
39
|
|
|
32
40
|
- [Installation](installation.md) - Detailed setup instructions
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Requirements
|
|
4
4
|
|
|
5
5
|
- Python 3.10+
|
|
6
|
-
- marimo >= 0.19.
|
|
6
|
+
- marimo >= 0.19.8
|
|
7
7
|
- jupyter-server-proxy (installed automatically)
|
|
8
8
|
|
|
9
9
|
## Basic Installation
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
`marimo-jupyter-extension` requires marimo but does not declare it as a dependency, allowing flexible installation scenarios.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
pip install 'marimo>=0.19.
|
|
14
|
+
pip install 'marimo>=0.19.8' marimo-jupyter-extension
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Or with uv:
|
|
@@ -29,7 +29,7 @@ FROM quay.io/jupyterhub/jupyterhub:latest
|
|
|
29
29
|
RUN cd /srv/jupyterhub && jupyterhub --generate-config && \
|
|
30
30
|
echo "c.JupyterHub.authenticator_class = 'dummy'" >> jupyterhub_config.py && \
|
|
31
31
|
echo "c.DummyAuthenticator.password = 'demo'" >> jupyterhub_config.py && \
|
|
32
|
-
pip install --no-cache-dir notebook 'marimo>=0.19.
|
|
32
|
+
pip install --no-cache-dir notebook 'marimo>=0.19.8' marimo-jupyter-extension
|
|
33
33
|
RUN useradd -ms /bin/bash demo
|
|
34
34
|
```
|
|
35
35
|
|
|
@@ -57,7 +57,7 @@ RUN curl -fsSL https://github.com/conda-forge/miniforge/releases/latest/download
|
|
|
57
57
|
bash /root/miniforge.sh -b -p /opt/conda && rm /root/miniforge.sh
|
|
58
58
|
|
|
59
59
|
# marimo in conda environment (user packages available)
|
|
60
|
-
RUN /opt/conda/bin/pip install --no-cache-dir 'marimo>=0.19.
|
|
60
|
+
RUN /opt/conda/bin/pip install --no-cache-dir 'marimo>=0.19.8'
|
|
61
61
|
|
|
62
62
|
# marimo-jupyter-extension in Jupyter's environment
|
|
63
63
|
RUN /usr/bin/pip install --no-cache-dir marimo-jupyter-extension
|
|
@@ -52,10 +52,10 @@ which jupyter
|
|
|
52
52
|
|
|
53
53
|
**Cause**: marimo version is too old.
|
|
54
54
|
|
|
55
|
-
**Solution**: Upgrade to marimo 0.19.
|
|
55
|
+
**Solution**: Upgrade to marimo 0.19.8 or newer:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
pip install 'marimo>=0.19.
|
|
58
|
+
pip install 'marimo>=0.19.8'
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### JupyterHub Issues
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { LabIcon } from '@jupyterlab/ui-components';
|
|
2
2
|
import marimoIconSvg from '../style/marimo.svg';
|
|
3
|
+
import marimoFileIconSvg from '../style/marimo-file-icon.svg';
|
|
3
4
|
|
|
4
5
|
export const marimoIcon = new LabIcon({
|
|
5
6
|
name: '@marimo-team/jupyter-extension:marimo',
|
|
6
7
|
svgstr: marimoIconSvg,
|
|
7
8
|
});
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Small marimo icon for file browser items
|
|
12
|
+
*/
|
|
13
|
+
export const marimoFileIcon = new LabIcon({
|
|
14
|
+
name: '@marimo-team/jupyter-extension:marimo-file',
|
|
15
|
+
svgstr: marimoFileIconSvg,
|
|
16
|
+
});
|
|
17
|
+
|
|
9
18
|
/**
|
|
10
19
|
* SVG string as a data URI for use with kernelIconUrl
|
|
11
20
|
*/
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { MainAreaWidget, IFrame } from '@jupyterlab/apputils';
|
|
2
|
+
import { UUID } from '@lumino/coreutils';
|
|
3
|
+
import type { ISignal } from '@lumino/signaling';
|
|
4
|
+
import { leafIcon } from './icons';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sandbox settings for marimo IFrames.
|
|
8
|
+
* Centralized here to ensure consistency across all IFrame creation paths.
|
|
9
|
+
*/
|
|
10
|
+
const IFRAME_SANDBOX_SETTINGS: IFrame.SandboxExceptions[] = [
|
|
11
|
+
'allow-same-origin',
|
|
12
|
+
'allow-scripts',
|
|
13
|
+
'allow-forms',
|
|
14
|
+
'allow-modals',
|
|
15
|
+
'allow-popups',
|
|
16
|
+
'allow-downloads',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Tracked widget with metadata for session management.
|
|
21
|
+
*/
|
|
22
|
+
interface TrackedWidget {
|
|
23
|
+
widget: MainAreaWidget<IFrame>;
|
|
24
|
+
originalUrl: string;
|
|
25
|
+
widgetId: string;
|
|
26
|
+
/** Whether this widget has been seen in the active sessions list at least once */
|
|
27
|
+
wasConnected: boolean;
|
|
28
|
+
/** Whether this is a new notebook (not file-based) - controls title modification on disconnect */
|
|
29
|
+
isNewNotebook: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Map of initializationId to tracked widget for managing new notebooks.
|
|
34
|
+
* Used to update tab titles and handle disconnection states.
|
|
35
|
+
*/
|
|
36
|
+
const widgetsByInitId = new Map<string, TrackedWidget>();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Map of filePath to tracked widget for managing file-based notebooks.
|
|
40
|
+
* Used to handle disconnection states for notebooks opened from files.
|
|
41
|
+
*/
|
|
42
|
+
const widgetsByFilePath = new Map<string, TrackedWidget>();
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get an existing widget for a file path, if one exists.
|
|
46
|
+
*/
|
|
47
|
+
export function getWidgetByFilePath(
|
|
48
|
+
filePath: string,
|
|
49
|
+
): MainAreaWidget<IFrame> | undefined {
|
|
50
|
+
return widgetsByFilePath.get(filePath)?.widget;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Refresh a widget's iframe by resetting its URL.
|
|
55
|
+
* This forces marimo to reconnect.
|
|
56
|
+
*/
|
|
57
|
+
export function refreshWidgetByFilePath(filePath: string): void {
|
|
58
|
+
const tracked = widgetsByFilePath.get(filePath);
|
|
59
|
+
if (tracked) {
|
|
60
|
+
// Reset URL to force iframe reload
|
|
61
|
+
tracked.widget.content.url = tracked.originalUrl;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate a data URL containing the disconnected page HTML.
|
|
67
|
+
*/
|
|
68
|
+
function createDisconnectedPageUrl(widgetId: string): string {
|
|
69
|
+
const html = `
|
|
70
|
+
<!DOCTYPE html>
|
|
71
|
+
<html>
|
|
72
|
+
<head>
|
|
73
|
+
<style>
|
|
74
|
+
body {
|
|
75
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
height: 100vh;
|
|
81
|
+
margin: 0;
|
|
82
|
+
background: #f5f5f5;
|
|
83
|
+
color: #333;
|
|
84
|
+
}
|
|
85
|
+
.icon {
|
|
86
|
+
font-size: 64px;
|
|
87
|
+
margin-bottom: 16px;
|
|
88
|
+
}
|
|
89
|
+
h2 {
|
|
90
|
+
margin: 0 0 8px 0;
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
}
|
|
93
|
+
p {
|
|
94
|
+
margin: 0 0 24px 0;
|
|
95
|
+
color: #666;
|
|
96
|
+
}
|
|
97
|
+
button {
|
|
98
|
+
background: #2196f3;
|
|
99
|
+
color: white;
|
|
100
|
+
border: none;
|
|
101
|
+
padding: 12px 24px;
|
|
102
|
+
font-size: 14px;
|
|
103
|
+
border-radius: 4px;
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
transition: background 0.2s;
|
|
106
|
+
}
|
|
107
|
+
button:hover {
|
|
108
|
+
background: #1976d2;
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
111
|
+
</head>
|
|
112
|
+
<body>
|
|
113
|
+
<div class="icon">🌿</div>
|
|
114
|
+
<h2>Session Disconnected</h2>
|
|
115
|
+
<p>The marimo session has been closed.</p>
|
|
116
|
+
<button onclick="reconnect()">Reconnect</button>
|
|
117
|
+
<script>
|
|
118
|
+
function reconnect() {
|
|
119
|
+
window.parent.postMessage({ type: 'marimo-reconnect', widgetId: '${widgetId}' }, '*');
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
</body>
|
|
123
|
+
</html>
|
|
124
|
+
`.trim();
|
|
125
|
+
return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Initialize the message listener for reconnect requests.
|
|
130
|
+
* Should be called once when the extension loads.
|
|
131
|
+
*/
|
|
132
|
+
let messageListenerInitialized = false;
|
|
133
|
+
|
|
134
|
+
function initializeMessageListener(): void {
|
|
135
|
+
if (messageListenerInitialized) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
messageListenerInitialized = true;
|
|
139
|
+
|
|
140
|
+
window.addEventListener('message', (event: MessageEvent<unknown>) => {
|
|
141
|
+
const data = event.data as { type?: string; widgetId?: string } | null;
|
|
142
|
+
if (data?.type === 'marimo-reconnect' && data?.widgetId) {
|
|
143
|
+
const widgetId = data.widgetId;
|
|
144
|
+
// Find the tracked widget by its widgetId in both maps
|
|
145
|
+
for (const tracked of [
|
|
146
|
+
...widgetsByInitId.values(),
|
|
147
|
+
...widgetsByFilePath.values(),
|
|
148
|
+
]) {
|
|
149
|
+
if (tracked.widgetId === widgetId) {
|
|
150
|
+
// Restore the original URL to reconnect
|
|
151
|
+
tracked.widget.content.url = tracked.originalUrl;
|
|
152
|
+
// Remove "(disconnected)" from title if present (only for new notebooks)
|
|
153
|
+
if (
|
|
154
|
+
tracked.isNewNotebook &&
|
|
155
|
+
tracked.widget.title.label.endsWith(' (disconnected)')
|
|
156
|
+
) {
|
|
157
|
+
tracked.widget.title.label = tracked.widget.title.label.replace(
|
|
158
|
+
' (disconnected)',
|
|
159
|
+
'',
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Result from createMarimoIFrame containing the IFrame and tracking metadata.
|
|
171
|
+
*/
|
|
172
|
+
export interface MarimoIFrameResult {
|
|
173
|
+
/** The configured IFrame ready for use */
|
|
174
|
+
iframe: IFrame;
|
|
175
|
+
/** The final URL set on the IFrame */
|
|
176
|
+
url: string;
|
|
177
|
+
/** The initialization ID for new notebooks, or null for file-based */
|
|
178
|
+
initId: string | null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Create a marimo IFrame with proper sandbox settings and URL configuration.
|
|
183
|
+
* This is the single entry point for creating marimo IFrames - ensures
|
|
184
|
+
* consistent sandbox settings and URL building across all code paths.
|
|
185
|
+
*
|
|
186
|
+
* Note: Callers are responsible for registering the widget for tracking
|
|
187
|
+
* using registerWidgetForTracking() after wrapping in a widget.
|
|
188
|
+
*/
|
|
189
|
+
export function createMarimoIFrame(
|
|
190
|
+
baseUrl: string,
|
|
191
|
+
options: { filePath?: string; initId?: string } = {},
|
|
192
|
+
): MarimoIFrameResult {
|
|
193
|
+
const iframe = new IFrame({
|
|
194
|
+
sandbox: IFRAME_SANDBOX_SETTINGS,
|
|
195
|
+
});
|
|
196
|
+
iframe.addClass('jp-MarimoWidget');
|
|
197
|
+
|
|
198
|
+
// Generate initId if not file-based (and not provided)
|
|
199
|
+
const initId = options.filePath
|
|
200
|
+
? null
|
|
201
|
+
: (options.initId ?? `__new__${UUID.uuid4()}`);
|
|
202
|
+
|
|
203
|
+
// Build URL: file-based uses encoded filePath, new notebooks use initId
|
|
204
|
+
const url = options.filePath
|
|
205
|
+
? `${baseUrl}?file=${encodeURIComponent(options.filePath)}`
|
|
206
|
+
: `${baseUrl}?file=${initId}`;
|
|
207
|
+
|
|
208
|
+
iframe.url = url;
|
|
209
|
+
return { iframe, url, initId };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Create a marimo widget that embeds the editor in an iframe.
|
|
214
|
+
* Uses createMarimoIFrame internally for consistent IFrame creation.
|
|
215
|
+
*/
|
|
216
|
+
export function createMarimoWidget(
|
|
217
|
+
baseUrl: string,
|
|
218
|
+
options: { filePath?: string; label?: string } = {},
|
|
219
|
+
): MainAreaWidget<IFrame> {
|
|
220
|
+
const { filePath, label } = options;
|
|
221
|
+
|
|
222
|
+
// Use centralized IFrame creation
|
|
223
|
+
const {
|
|
224
|
+
iframe: content,
|
|
225
|
+
url: finalUrl,
|
|
226
|
+
initId,
|
|
227
|
+
} = createMarimoIFrame(baseUrl, { filePath });
|
|
228
|
+
|
|
229
|
+
const widget = new MainAreaWidget({ content });
|
|
230
|
+
widget.id = `marimo-${UUID.uuid4()}`;
|
|
231
|
+
|
|
232
|
+
if (label) {
|
|
233
|
+
widget.title.label = label;
|
|
234
|
+
} else if (filePath) {
|
|
235
|
+
const parts = filePath.split('/');
|
|
236
|
+
widget.title.label = parts[parts.length - 1] || 'marimo';
|
|
237
|
+
} else {
|
|
238
|
+
widget.title.label = 'marimo';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
widget.title.closable = true;
|
|
242
|
+
widget.title.icon = leafIcon;
|
|
243
|
+
widget.title.caption = filePath ? `marimo: ${filePath}` : 'marimo Editor';
|
|
244
|
+
|
|
245
|
+
// Track widgets for disconnection handling
|
|
246
|
+
if (initId) {
|
|
247
|
+
// New notebook - track by initializationId
|
|
248
|
+
const widgetId = `marimo-widget-${UUID.uuid4()}`;
|
|
249
|
+
widgetsByInitId.set(initId, {
|
|
250
|
+
widget,
|
|
251
|
+
originalUrl: finalUrl,
|
|
252
|
+
widgetId,
|
|
253
|
+
wasConnected: false,
|
|
254
|
+
isNewNotebook: true,
|
|
255
|
+
});
|
|
256
|
+
widget.disposed.connect(() => {
|
|
257
|
+
widgetsByInitId.delete(initId);
|
|
258
|
+
});
|
|
259
|
+
initializeMessageListener();
|
|
260
|
+
} else if (filePath) {
|
|
261
|
+
// File-based notebook - track by filePath
|
|
262
|
+
registerWidgetForTracking(widget, filePath, finalUrl);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return widget;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Update widget titles based on running session data.
|
|
270
|
+
* Called by the sidebar when it polls for running notebooks.
|
|
271
|
+
* Also detects disconnected sessions and shows reconnect UI.
|
|
272
|
+
*/
|
|
273
|
+
export function updateWidgetTitles(
|
|
274
|
+
sessions: { initializationId: string; name: string; path: string }[],
|
|
275
|
+
): void {
|
|
276
|
+
// Build sets of active identifiers for quick lookup
|
|
277
|
+
const activeInitIds = new Set(sessions.map((s) => s.initializationId));
|
|
278
|
+
const activePaths = new Set(sessions.map((s) => s.path));
|
|
279
|
+
|
|
280
|
+
// Update titles for active sessions (initId-based widgets)
|
|
281
|
+
for (const session of sessions) {
|
|
282
|
+
const tracked = widgetsByInitId.get(session.initializationId);
|
|
283
|
+
if (tracked && session.name && session.path) {
|
|
284
|
+
const { widget } = tracked;
|
|
285
|
+
// Mark as connected now that we've seen it in the sessions list
|
|
286
|
+
tracked.wasConnected = true;
|
|
287
|
+
// Remove "(disconnected)" suffix if session is back (only for new notebooks)
|
|
288
|
+
let currentLabel = widget.title.label;
|
|
289
|
+
if (tracked.isNewNotebook && currentLabel.endsWith(' (disconnected)')) {
|
|
290
|
+
currentLabel = currentLabel.replace(' (disconnected)', '');
|
|
291
|
+
widget.title.label = currentLabel;
|
|
292
|
+
}
|
|
293
|
+
// Update title if it differs
|
|
294
|
+
if (currentLabel !== session.name) {
|
|
295
|
+
widget.title.label = session.name;
|
|
296
|
+
widget.title.caption = `marimo: ${session.path}`;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Mark file-based widgets as connected when their path appears in sessions
|
|
302
|
+
for (const session of sessions) {
|
|
303
|
+
const tracked = widgetsByFilePath.get(session.path);
|
|
304
|
+
if (tracked) {
|
|
305
|
+
tracked.wasConnected = true;
|
|
306
|
+
// Note: file-based widgets (isNewNotebook: false) don't add "(disconnected)"
|
|
307
|
+
// suffix, so we don't need to remove it here
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Check for disconnected widgets (initId-based)
|
|
312
|
+
// Only show disconnected if the widget was previously connected
|
|
313
|
+
for (const [initId, tracked] of widgetsByInitId.entries()) {
|
|
314
|
+
if (!activeInitIds.has(initId) && tracked.wasConnected) {
|
|
315
|
+
showDisconnectedPage(tracked);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Check for disconnected widgets (filePath-based)
|
|
320
|
+
// Only show disconnected if the widget was previously connected
|
|
321
|
+
for (const [filePath, tracked] of widgetsByFilePath.entries()) {
|
|
322
|
+
if (!activePaths.has(filePath) && tracked.wasConnected) {
|
|
323
|
+
showDisconnectedPage(tracked);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Show the disconnected page for a tracked widget.
|
|
330
|
+
*/
|
|
331
|
+
function showDisconnectedPage(tracked: TrackedWidget): void {
|
|
332
|
+
const { widget, widgetId, isNewNotebook } = tracked;
|
|
333
|
+
const disconnectedUrl = createDisconnectedPageUrl(widgetId);
|
|
334
|
+
|
|
335
|
+
// Only update if not already showing disconnected page
|
|
336
|
+
if (widget.content.url !== disconnectedUrl) {
|
|
337
|
+
widget.content.url = disconnectedUrl;
|
|
338
|
+
// Only add "(disconnected)" suffix for new notebooks
|
|
339
|
+
// File-based notebooks shouldn't modify title (causes file rename in DocumentWidget)
|
|
340
|
+
if (isNewNotebook && !widget.title.label.endsWith(' (disconnected)')) {
|
|
341
|
+
widget.title.label = `${widget.title.label} (disconnected)`;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Widget-like interface for registration.
|
|
348
|
+
* Allows MarimoWidgetFactory widgets to be registered for tracking.
|
|
349
|
+
*/
|
|
350
|
+
interface WidgetLike {
|
|
351
|
+
content: IFrame;
|
|
352
|
+
title: { label: string; caption: string };
|
|
353
|
+
disposed: ISignal<unknown, void>;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Register an externally-created widget for disconnection tracking.
|
|
358
|
+
* Used by MarimoWidgetFactory for widgets created via "Open With" menu.
|
|
359
|
+
*/
|
|
360
|
+
export function registerWidgetForTracking(
|
|
361
|
+
widget: WidgetLike,
|
|
362
|
+
filePath: string,
|
|
363
|
+
originalUrl: string,
|
|
364
|
+
): void {
|
|
365
|
+
const widgetId = `marimo-widget-${UUID.uuid4()}`;
|
|
366
|
+
widgetsByFilePath.set(filePath, {
|
|
367
|
+
widget: widget as unknown as MainAreaWidget<IFrame>,
|
|
368
|
+
originalUrl,
|
|
369
|
+
widgetId,
|
|
370
|
+
wasConnected: false,
|
|
371
|
+
isNewNotebook: false,
|
|
372
|
+
});
|
|
373
|
+
widget.disposed.connect(() => {
|
|
374
|
+
widgetsByFilePath.delete(filePath);
|
|
375
|
+
});
|
|
376
|
+
initializeMessageListener();
|
|
377
|
+
}
|