variable-explorer 0.7.0__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.
- variable_explorer-0.7.0/.claude/settings.local.json +41 -0
- variable_explorer-0.7.0/.gitignore +11 -0
- variable_explorer-0.7.0/.yarnrc.yml +1 -0
- variable_explorer-0.7.0/LICENSE +21 -0
- variable_explorer-0.7.0/MANIFEST.in +15 -0
- variable_explorer-0.7.0/PKG-INFO +82 -0
- variable_explorer-0.7.0/README.md +55 -0
- variable_explorer-0.7.0/fix_frontend.py +52 -0
- variable_explorer-0.7.0/install.json +5 -0
- variable_explorer-0.7.0/package.json +91 -0
- variable_explorer-0.7.0/pyproject.toml +71 -0
- variable_explorer-0.7.0/schema/plugin.json +75 -0
- variable_explorer-0.7.0/src/comm/CommManager.ts +121 -0
- variable_explorer-0.7.0/src/comm/protocol.ts +206 -0
- variable_explorer-0.7.0/src/components/Breadcrumb.tsx +39 -0
- variable_explorer-0.7.0/src/components/CellReferenceBar.tsx +22 -0
- variable_explorer-0.7.0/src/components/DataGrid.tsx +489 -0
- variable_explorer-0.7.0/src/components/HistogramHeader.tsx +187 -0
- variable_explorer-0.7.0/src/components/MetadataSidebar.tsx +195 -0
- variable_explorer-0.7.0/src/components/ResizeHandle.tsx +49 -0
- variable_explorer-0.7.0/src/components/SqlPanel.tsx +335 -0
- variable_explorer-0.7.0/src/components/StatusBar.tsx +40 -0
- variable_explorer-0.7.0/src/components/VariableExplorerApp.tsx +486 -0
- variable_explorer-0.7.0/src/components/VariableList.tsx +114 -0
- variable_explorer-0.7.0/src/index.ts +204 -0
- variable_explorer-0.7.0/src/utils/colorScales.ts +27 -0
- variable_explorer-0.7.0/src/utils/histogramRenderer.ts +122 -0
- variable_explorer-0.7.0/src/utils/stylesheetCloner.ts +54 -0
- variable_explorer-0.7.0/src/widgets/DetachedWindow.ts +156 -0
- variable_explorer-0.7.0/style/ag-grid-overrides.css +98 -0
- variable_explorer-0.7.0/style/base.css +803 -0
- variable_explorer-0.7.0/style/index.css +2 -0
- variable_explorer-0.7.0/style/index.js +4 -0
- variable_explorer-0.7.0/tsconfig.json +24 -0
- variable_explorer-0.7.0/variable_explorer/__init__.py +7 -0
- variable_explorer-0.7.0/variable_explorer/_version.py +4 -0
- variable_explorer-0.7.0/variable_explorer/kernel/__init__.py +5 -0
- variable_explorer-0.7.0/variable_explorer/kernel/comm_handler.py +287 -0
- variable_explorer-0.7.0/variable_explorer/kernel/data_provider.py +287 -0
- variable_explorer-0.7.0/variable_explorer/kernel/editor.py +88 -0
- variable_explorer-0.7.0/variable_explorer/kernel/introspection.py +235 -0
- variable_explorer-0.7.0/variable_explorer/kernel/serialization.py +81 -0
- variable_explorer-0.7.0/variable_explorer/kernel/sorter.py +51 -0
- variable_explorer-0.7.0/variable_explorer/kernel/sql_handler.py +144 -0
- variable_explorer-0.7.0/variable_explorer/kernel/statistics.py +117 -0
- variable_explorer-0.7.0/variable_explorer/labextension/package.json +96 -0
- variable_explorer-0.7.0/variable_explorer/labextension/schemas/variable-explorer/package.json.orig +91 -0
- variable_explorer-0.7.0/variable_explorer/labextension/schemas/variable-explorer/plugin.json +75 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/140.0326e0e0e77185d73e92.js +1 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/719.b1e5abbcc7b2adac64e7.js +1 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/746.58a99d2f32f8834ed814.js +1 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/924.5e98cac11804e1a59d7e.js +1 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/927.d2072f9536bbda11eb79.js +1 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/remoteEntry.d175bc820329257b7c49.js +1 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/style.js +4 -0
- variable_explorer-0.7.0/variable_explorer/labextension/static/third-party-licenses.json +28 -0
- variable_explorer-0.7.0/yarn.lock +6134 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(pip show:*)",
|
|
5
|
+
"Bash(npm --version)",
|
|
6
|
+
"Bash(pip list:*)",
|
|
7
|
+
"Bash(python:*)",
|
|
8
|
+
"Bash(xargs ls:*)",
|
|
9
|
+
"Bash(jupyter labextension:*)",
|
|
10
|
+
"Bash(node -e \"console.log\\(require.resolve\\('@jupyterlab/services'\\)\\)\")",
|
|
11
|
+
"Bash(npm view:*)",
|
|
12
|
+
"Bash(npm list:*)",
|
|
13
|
+
"Bash(npm pack:*)",
|
|
14
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
15
|
+
"Bash(pip install:*)",
|
|
16
|
+
"Bash(jlpm install:*)",
|
|
17
|
+
"Bash(jlpm build:lib)",
|
|
18
|
+
"Bash(node -e \"const s = require\\('@jupyterlab/services'\\); console.log\\(Object.keys\\(s\\).filter\\(k => k.includes\\('Kernel'\\)\\).join\\(', '\\)\\)\")",
|
|
19
|
+
"Bash(jlpm build:labextension:dev)",
|
|
20
|
+
"Bash(mkdir -p Projects/ve_build)",
|
|
21
|
+
"Bash(cp -r \"Z:/Consulting/Projects/variable_explorer/\"* \"C:/Users/Owner/Projects/ve_build/\")",
|
|
22
|
+
"Bash(cp \"Z:/Consulting/Projects/variable_explorer/.yarnrc.yml\" \"C:/Users/Owner/Projects/ve_build/\")",
|
|
23
|
+
"Bash(cmd.exe:*)",
|
|
24
|
+
"Bash(node \"node_modules/@jupyterlab/builder/lib/build-labextension.js\" --core-path \"C:/Users/Owner/anaconda3/Lib/site-packages/jupyterlab/staging\" \".\" --development)",
|
|
25
|
+
"Bash(twine upload:*)",
|
|
26
|
+
"Bash(gh auth:*)",
|
|
27
|
+
"Bash(gh org:*)",
|
|
28
|
+
"Bash(gh repo:*)",
|
|
29
|
+
"Bash(jlpm build *)",
|
|
30
|
+
"Bash(where node *)",
|
|
31
|
+
"Bash(where npm *)",
|
|
32
|
+
"Bash(where jlpm *)",
|
|
33
|
+
"PowerShell(Get-Command *)",
|
|
34
|
+
"PowerShell(jlpm build *)",
|
|
35
|
+
"PowerShell(jlpm build:lib *)",
|
|
36
|
+
"PowerShell(jupyter labextension *)",
|
|
37
|
+
"PowerShell(Test-Path *)",
|
|
38
|
+
"Bash(curl -s \"https://pypi.org/pypi/variable-explorer/json\")"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nodeLinker: node-modules
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Variable Explorer Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
include LICENSE
|
|
2
|
+
include README.md
|
|
3
|
+
include install.json
|
|
4
|
+
include package.json
|
|
5
|
+
include tsconfig.json
|
|
6
|
+
include .yarnrc.yml
|
|
7
|
+
|
|
8
|
+
graft variable_explorer/labextension
|
|
9
|
+
graft src
|
|
10
|
+
graft style
|
|
11
|
+
graft schema
|
|
12
|
+
|
|
13
|
+
prune node_modules
|
|
14
|
+
prune .yarn
|
|
15
|
+
prune lib
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: variable-explorer
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: A Spyder/Stata-grade variable explorer for JupyterLab
|
|
5
|
+
Project-URL: Homepage, https://github.com/variable-explorer/variable-explorer
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Framework :: Jupyter
|
|
9
|
+
Classifier: Framework :: Jupyter :: JupyterLab
|
|
10
|
+
Classifier: Framework :: Jupyter :: JupyterLab :: 4
|
|
11
|
+
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions
|
|
12
|
+
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: jupyterlab<5,>=4.0.0
|
|
22
|
+
Requires-Dist: numpy
|
|
23
|
+
Requires-Dist: pandas
|
|
24
|
+
Provides-Extra: sql
|
|
25
|
+
Requires-Dist: duckdb>=0.9; extra == 'sql'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Variable Explorer for JupyterLab
|
|
29
|
+
|
|
30
|
+
A Spyder/Stata-grade variable explorer for JupyterLab 4. Opens in its own window so you can put it on a second monitor.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install variable-explorer
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That's it. Restart JupyterLab if it's running.
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
1. Open a notebook in JupyterLab
|
|
43
|
+
2. Click the **Variables** button in the notebook toolbar (spreadsheet icon)
|
|
44
|
+
3. The Variable Explorer opens in a **separate browser window**
|
|
45
|
+
4. Run cells — the variable list updates automatically
|
|
46
|
+
5. Click any DataFrame, list, or dict to view its contents
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Detached window** — lives outside JupyterLab, move it to another monitor
|
|
51
|
+
- **AG Grid** — fast virtual scrolling, handles large DataFrames
|
|
52
|
+
- **Column sorting** — click headers to sort (kernel-side, fast even on millions of rows)
|
|
53
|
+
- **Inline histograms** — distribution charts in every column header with hover tooltips
|
|
54
|
+
- **Conditional formatting** — numeric heatmaps, boolean color-coding
|
|
55
|
+
- **Metadata sidebar** — column types, null counts, stats (like Stata's Variable Manager)
|
|
56
|
+
- **Properties panel** — dataset shape, memory usage, dtypes
|
|
57
|
+
- **Cell editing** — double-click to edit values, synced back to the kernel
|
|
58
|
+
- **Resizable panels** — drag the dividers between panels
|
|
59
|
+
- **Auto-refresh** — variable list updates after every cell execution
|
|
60
|
+
- **Supports many types** — DataFrames, Series, numpy arrays, lists of dicts, dicts of lists, nested structures
|
|
61
|
+
|
|
62
|
+
## Requirements
|
|
63
|
+
|
|
64
|
+
- JupyterLab 4.x
|
|
65
|
+
- Python 3.10+
|
|
66
|
+
- pandas (for DataFrame viewing)
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Clone and install in development mode
|
|
72
|
+
git clone https://github.com/variable-explorer/variable-explorer.git
|
|
73
|
+
cd variable-explorer
|
|
74
|
+
pip install -e .
|
|
75
|
+
|
|
76
|
+
# Rebuild after making changes
|
|
77
|
+
jlpm install
|
|
78
|
+
jlpm build
|
|
79
|
+
pip install -e .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Requires Node.js 18+ for development builds.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Variable Explorer for JupyterLab
|
|
2
|
+
|
|
3
|
+
A Spyder/Stata-grade variable explorer for JupyterLab 4. Opens in its own window so you can put it on a second monitor.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install variable-explorer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That's it. Restart JupyterLab if it's running.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
1. Open a notebook in JupyterLab
|
|
16
|
+
2. Click the **Variables** button in the notebook toolbar (spreadsheet icon)
|
|
17
|
+
3. The Variable Explorer opens in a **separate browser window**
|
|
18
|
+
4. Run cells — the variable list updates automatically
|
|
19
|
+
5. Click any DataFrame, list, or dict to view its contents
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- **Detached window** — lives outside JupyterLab, move it to another monitor
|
|
24
|
+
- **AG Grid** — fast virtual scrolling, handles large DataFrames
|
|
25
|
+
- **Column sorting** — click headers to sort (kernel-side, fast even on millions of rows)
|
|
26
|
+
- **Inline histograms** — distribution charts in every column header with hover tooltips
|
|
27
|
+
- **Conditional formatting** — numeric heatmaps, boolean color-coding
|
|
28
|
+
- **Metadata sidebar** — column types, null counts, stats (like Stata's Variable Manager)
|
|
29
|
+
- **Properties panel** — dataset shape, memory usage, dtypes
|
|
30
|
+
- **Cell editing** — double-click to edit values, synced back to the kernel
|
|
31
|
+
- **Resizable panels** — drag the dividers between panels
|
|
32
|
+
- **Auto-refresh** — variable list updates after every cell execution
|
|
33
|
+
- **Supports many types** — DataFrames, Series, numpy arrays, lists of dicts, dicts of lists, nested structures
|
|
34
|
+
|
|
35
|
+
## Requirements
|
|
36
|
+
|
|
37
|
+
- JupyterLab 4.x
|
|
38
|
+
- Python 3.10+
|
|
39
|
+
- pandas (for DataFrame viewing)
|
|
40
|
+
|
|
41
|
+
## Development
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Clone and install in development mode
|
|
45
|
+
git clone https://github.com/variable-explorer/variable-explorer.git
|
|
46
|
+
cd variable-explorer
|
|
47
|
+
pip install -e .
|
|
48
|
+
|
|
49
|
+
# Rebuild after making changes
|
|
50
|
+
jlpm install
|
|
51
|
+
jlpm build
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Requires Node.js 18+ for development builds.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
content = open('src/components/VariableExplorerApp.tsx').read()
|
|
2
|
+
|
|
3
|
+
# 1. Add error state after loading state
|
|
4
|
+
content = content.replace(
|
|
5
|
+
'const [loading, setLoading] = React.useState(false);',
|
|
6
|
+
'const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);'
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
# 2. Clear error when fetching data
|
|
10
|
+
content = content.replace(
|
|
11
|
+
'setRows([]);\n setSortModel([]);\n setCellSelection(null);',
|
|
12
|
+
'setError(null);\n setRows([]);\n setSortModel([]);\n setCellSelection(null);'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# 3. Update error handler to show in UI
|
|
16
|
+
old_error = """ case 'error':
|
|
17
|
+
console.error('Variable Explorer kernel error:', msg.message);
|
|
18
|
+
setLoading(false);
|
|
19
|
+
break;"""
|
|
20
|
+
new_error = """ case 'error':
|
|
21
|
+
console.error('Variable Explorer kernel error:', msg.message);
|
|
22
|
+
setError(msg.message || 'Unknown error');
|
|
23
|
+
setLoading(false);
|
|
24
|
+
break;"""
|
|
25
|
+
content = content.replace(old_error, new_error)
|
|
26
|
+
|
|
27
|
+
# 4. Update empty state to show error
|
|
28
|
+
old_empty = """ ) : (
|
|
29
|
+
<div className="ve-empty-state">
|
|
30
|
+
{loading ? (
|
|
31
|
+
<div className="ve-loading">
|
|
32
|
+
<div className="ve-spinner" />
|
|
33
|
+
</div>
|
|
34
|
+
) : !connected ? ("""
|
|
35
|
+
|
|
36
|
+
new_empty = """ ) : (
|
|
37
|
+
<div className="ve-empty-state">
|
|
38
|
+
{error ? (
|
|
39
|
+
<>
|
|
40
|
+
<div className="ve-empty-icon" style={{ color: 'var(--jp-error-color1)' }}>⚠</div>
|
|
41
|
+
<div style={{ color: 'var(--jp-error-color1)' }}>{error}</div>
|
|
42
|
+
</>
|
|
43
|
+
) : loading ? (
|
|
44
|
+
<div className="ve-loading">
|
|
45
|
+
<div className="ve-spinner" />
|
|
46
|
+
</div>
|
|
47
|
+
) : !connected ? ("""
|
|
48
|
+
|
|
49
|
+
content = content.replace(old_empty, new_empty)
|
|
50
|
+
|
|
51
|
+
open('src/components/VariableExplorerApp.tsx', 'w').write(content)
|
|
52
|
+
print('Done')
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "variable-explorer",
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "A Spyder/Stata-grade variable explorer for JupyterLab",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jupyter",
|
|
7
|
+
"jupyterlab",
|
|
8
|
+
"jupyterlab-extension",
|
|
9
|
+
"variable-explorer",
|
|
10
|
+
"dataframe-viewer"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "Variable Explorer Contributors"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
|
|
18
|
+
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}"
|
|
19
|
+
],
|
|
20
|
+
"main": "lib/index.js",
|
|
21
|
+
"types": "lib/index.d.ts",
|
|
22
|
+
"style": "style/index.css",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": ""
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "jlpm build:lib && jlpm build:labextension:dev",
|
|
29
|
+
"build:prod": "jlpm clean && jlpm build:lib && jlpm build:labextension",
|
|
30
|
+
"build:labextension": "jupyter labextension build .",
|
|
31
|
+
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
32
|
+
"build:lib": "tsc",
|
|
33
|
+
"clean": "jlpm clean:lib && jlpm clean:labextension",
|
|
34
|
+
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
|
|
35
|
+
"clean:labextension": "rimraf variable_explorer/labextension",
|
|
36
|
+
"install:extension": "jlpm build",
|
|
37
|
+
"watch": "run-p watch:src watch:labextension",
|
|
38
|
+
"watch:src": "tsc -w --sourceMap",
|
|
39
|
+
"watch:labextension": "jupyter labextension watch ."
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@jupyterlab/application": "^4.0.0",
|
|
43
|
+
"@jupyterlab/apputils": "^4.0.0",
|
|
44
|
+
"@jupyterlab/console": "^4.0.0",
|
|
45
|
+
"@jupyterlab/notebook": "^4.0.0",
|
|
46
|
+
"@jupyterlab/services": "^7.0.0",
|
|
47
|
+
"@jupyterlab/statusbar": "^4.0.0",
|
|
48
|
+
"@jupyterlab/ui-components": "^4.0.0",
|
|
49
|
+
"@lumino/coreutils": "^2.0.0",
|
|
50
|
+
"@lumino/disposable": "^2.0.0",
|
|
51
|
+
"@lumino/messaging": "^2.0.0",
|
|
52
|
+
"@lumino/signaling": "^2.0.0",
|
|
53
|
+
"@lumino/widgets": "^2.0.0",
|
|
54
|
+
"ag-grid-community": "^32.0.0",
|
|
55
|
+
"ag-grid-react": "^32.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@jupyterlab/builder": "^4.0.0",
|
|
59
|
+
"@types/react": "^18.0.26",
|
|
60
|
+
"@types/react-dom": "^18.0.9",
|
|
61
|
+
"css-loader": "^6.7.1",
|
|
62
|
+
"npm-run-all2": "^7.0.1",
|
|
63
|
+
"rimraf": "^5.0.1",
|
|
64
|
+
"source-map-loader": "^1.0.2",
|
|
65
|
+
"style-loader": "^3.3.1",
|
|
66
|
+
"typescript": "~5.5.0"
|
|
67
|
+
},
|
|
68
|
+
"sideEffects": [
|
|
69
|
+
"style/*.css",
|
|
70
|
+
"style/index.js"
|
|
71
|
+
],
|
|
72
|
+
"styleModule": "style/index.js",
|
|
73
|
+
"publishConfig": {
|
|
74
|
+
"access": "public"
|
|
75
|
+
},
|
|
76
|
+
"jupyterlab": {
|
|
77
|
+
"extension": true,
|
|
78
|
+
"outputDir": "variable_explorer/labextension",
|
|
79
|
+
"schemaDir": "schema",
|
|
80
|
+
"sharedPackages": {
|
|
81
|
+
"ag-grid-community": {
|
|
82
|
+
"bundled": true,
|
|
83
|
+
"singleton": true
|
|
84
|
+
},
|
|
85
|
+
"ag-grid-react": {
|
|
86
|
+
"bundled": true,
|
|
87
|
+
"singleton": true
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version>=0.3.2"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "variable-explorer"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
license = { text = "MIT" }
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Framework :: Jupyter",
|
|
12
|
+
"Framework :: Jupyter :: JupyterLab",
|
|
13
|
+
"Framework :: Jupyter :: JupyterLab :: 4",
|
|
14
|
+
"Framework :: Jupyter :: JupyterLab :: Extensions",
|
|
15
|
+
"Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"jupyterlab>=4.0.0,<5",
|
|
26
|
+
"pandas",
|
|
27
|
+
"numpy",
|
|
28
|
+
]
|
|
29
|
+
dynamic = ["version", "description"]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
sql = ["duckdb>=0.9"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/variable-explorer/variable-explorer"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.version]
|
|
38
|
+
source = "nodejs"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.metadata.hooks.nodejs]
|
|
41
|
+
fields = ["description", "version"]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.sdist]
|
|
44
|
+
artifacts = ["variable_explorer/labextension"]
|
|
45
|
+
exclude = [".github", "binder", "node_modules", ".yarn", "*.tgz"]
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel.shared-data]
|
|
48
|
+
"variable_explorer/labextension" = "share/jupyter/labextensions/variable-explorer"
|
|
49
|
+
"install.json" = "share/jupyter/labextensions/variable-explorer/install.json"
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.hooks.version]
|
|
52
|
+
path = "variable_explorer/_version.py"
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.hooks.jupyter-builder]
|
|
55
|
+
dependencies = ["hatch-jupyter-builder>=0.5"]
|
|
56
|
+
build-function = "hatch_jupyter_builder.npm_builder"
|
|
57
|
+
ensured-targets = [
|
|
58
|
+
"variable_explorer/labextension/static/style.js",
|
|
59
|
+
"variable_explorer/labextension/package.json",
|
|
60
|
+
]
|
|
61
|
+
skip-if-exists = ["variable_explorer/labextension/static/style.js"]
|
|
62
|
+
|
|
63
|
+
[tool.hatch.build.hooks.jupyter-builder.build-kwargs]
|
|
64
|
+
build_cmd = "build:prod"
|
|
65
|
+
npm = ["jlpm"]
|
|
66
|
+
|
|
67
|
+
[tool.hatch.build.hooks.jupyter-builder.editable-build-kwargs]
|
|
68
|
+
build_cmd = "install:extension"
|
|
69
|
+
npm = ["jlpm"]
|
|
70
|
+
source_dir = "src"
|
|
71
|
+
build_dir = "variable_explorer/labextension"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jupyter.lab.setting-icon-label": "Variable Explorer",
|
|
3
|
+
"title": "Variable Explorer",
|
|
4
|
+
"description": "Variable Explorer settings.",
|
|
5
|
+
"jupyter.lab.toolbars": {
|
|
6
|
+
"Notebook": [
|
|
7
|
+
{
|
|
8
|
+
"name": "variable-explorer-open",
|
|
9
|
+
"command": "variable-explorer:open",
|
|
10
|
+
"rank": 50,
|
|
11
|
+
"label": "Variables"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"jupyter.lab.menus": {
|
|
16
|
+
"main": [
|
|
17
|
+
{
|
|
18
|
+
"id": "jp-mainmenu-view",
|
|
19
|
+
"items": [
|
|
20
|
+
{
|
|
21
|
+
"command": "variable-explorer:open",
|
|
22
|
+
"rank": 1
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"type": "object",
|
|
29
|
+
"properties": {
|
|
30
|
+
"toolbar": {
|
|
31
|
+
"title": "Notebook toolbar items",
|
|
32
|
+
"description": "Toolbar items for notebooks",
|
|
33
|
+
"items": {
|
|
34
|
+
"$ref": "#/definitions/toolbarItem"
|
|
35
|
+
},
|
|
36
|
+
"type": "array",
|
|
37
|
+
"default": []
|
|
38
|
+
},
|
|
39
|
+
"pageSize": {
|
|
40
|
+
"type": "number",
|
|
41
|
+
"title": "Page Size",
|
|
42
|
+
"description": "Number of rows to load per page in the data grid.",
|
|
43
|
+
"default": 1000
|
|
44
|
+
},
|
|
45
|
+
"maxHistogramBins": {
|
|
46
|
+
"type": "number",
|
|
47
|
+
"title": "Max Histogram Bins",
|
|
48
|
+
"description": "Maximum number of bins for column histograms.",
|
|
49
|
+
"default": 20
|
|
50
|
+
},
|
|
51
|
+
"enableConditionalFormatting": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"title": "Enable Conditional Formatting",
|
|
54
|
+
"description": "Enable heatmap coloring for numeric columns.",
|
|
55
|
+
"default": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"additionalProperties": false,
|
|
59
|
+
"definitions": {
|
|
60
|
+
"toolbarItem": {
|
|
61
|
+
"properties": {
|
|
62
|
+
"name": { "type": "string" },
|
|
63
|
+
"args": { "type": "object" },
|
|
64
|
+
"command": { "type": "string", "default": "" },
|
|
65
|
+
"disabled": { "type": "boolean", "default": false },
|
|
66
|
+
"icon": { "type": "string", "default": "" },
|
|
67
|
+
"label": { "type": "string", "default": "" },
|
|
68
|
+
"type": { "type": "string", "enum": ["command", "spacer"], "default": "command" },
|
|
69
|
+
"rank": { "type": "number", "default": 50 }
|
|
70
|
+
},
|
|
71
|
+
"additionalProperties": false,
|
|
72
|
+
"type": "object"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { KernelMessage as JupyterKernelMessage, Kernel } from '@jupyterlab/services';
|
|
2
|
+
import { Signal, ISignal } from '@lumino/signaling';
|
|
3
|
+
import { FrontendMessage, ICommMessage } from './protocol';
|
|
4
|
+
|
|
5
|
+
const COMM_TARGET = 'variable_explorer';
|
|
6
|
+
|
|
7
|
+
const INIT_CODE = `
|
|
8
|
+
try:
|
|
9
|
+
from variable_explorer.kernel import init_comm as _ve_init
|
|
10
|
+
_ve_init()
|
|
11
|
+
del _ve_init
|
|
12
|
+
except ImportError as e:
|
|
13
|
+
print(f"Variable Explorer: kernel package not found: {e}")
|
|
14
|
+
except Exception as e:
|
|
15
|
+
print(f"Variable Explorer: init error: {e}")
|
|
16
|
+
`.trim();
|
|
17
|
+
|
|
18
|
+
export class CommManager {
|
|
19
|
+
private _comm: any = null;
|
|
20
|
+
private _kernel: Kernel.IKernelConnection | null = null;
|
|
21
|
+
private _messageReceived = new Signal<CommManager, ICommMessage>(this);
|
|
22
|
+
private _connectionChanged = new Signal<CommManager, boolean>(this);
|
|
23
|
+
private _isConnected = false;
|
|
24
|
+
|
|
25
|
+
get messageReceived(): ISignal<CommManager, ICommMessage> {
|
|
26
|
+
return this._messageReceived;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get connectionChanged(): ISignal<CommManager, boolean> {
|
|
30
|
+
return this._connectionChanged;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get isConnected(): boolean {
|
|
34
|
+
return this._isConnected;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async connect(kernel: Kernel.IKernelConnection): Promise<void> {
|
|
38
|
+
// Disconnect existing
|
|
39
|
+
this.disconnect();
|
|
40
|
+
this._kernel = kernel;
|
|
41
|
+
|
|
42
|
+
// Inject the Python init code
|
|
43
|
+
const future = kernel.requestExecute({
|
|
44
|
+
code: INIT_CODE,
|
|
45
|
+
silent: true,
|
|
46
|
+
store_history: false
|
|
47
|
+
});
|
|
48
|
+
await future.done;
|
|
49
|
+
|
|
50
|
+
// Open a comm from the frontend
|
|
51
|
+
this._comm = kernel.createComm(COMM_TARGET);
|
|
52
|
+
|
|
53
|
+
// Handle incoming messages
|
|
54
|
+
this._comm.onMsg = (msg: JupyterKernelMessage.ICommMsgMsg) => {
|
|
55
|
+
const data = msg.content.data as unknown as ICommMessage;
|
|
56
|
+
if (data && data.type) {
|
|
57
|
+
this._messageReceived.emit(data);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
this._comm.onClose = () => {
|
|
62
|
+
this._setConnected(false);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Open the comm channel
|
|
66
|
+
await this._comm.open({}).done;
|
|
67
|
+
this._setConnected(true);
|
|
68
|
+
|
|
69
|
+
// Listen for kernel shutdown/restart
|
|
70
|
+
kernel.statusChanged.connect(this._onKernelStatus, this);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
send(msg: FrontendMessage): void {
|
|
74
|
+
if (this._comm && this._isConnected) {
|
|
75
|
+
this._comm.send(msg as any);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
refresh(): void {
|
|
80
|
+
this.send({ type: 'refresh' });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
disconnect(): void {
|
|
84
|
+
if (this._kernel) {
|
|
85
|
+
this._kernel.statusChanged.disconnect(this._onKernelStatus, this);
|
|
86
|
+
}
|
|
87
|
+
if (this._comm) {
|
|
88
|
+
try {
|
|
89
|
+
this._comm.close({});
|
|
90
|
+
} catch {
|
|
91
|
+
// Comm may already be closed
|
|
92
|
+
}
|
|
93
|
+
this._comm = null;
|
|
94
|
+
}
|
|
95
|
+
this._kernel = null;
|
|
96
|
+
this._setConnected(false);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private _setConnected(connected: boolean): void {
|
|
100
|
+
if (this._isConnected !== connected) {
|
|
101
|
+
this._isConnected = connected;
|
|
102
|
+
this._connectionChanged.emit(connected);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private _onKernelStatus(
|
|
107
|
+
kernel: Kernel.IKernelConnection,
|
|
108
|
+
status: JupyterKernelMessage.Status
|
|
109
|
+
): void {
|
|
110
|
+
if (status === 'restarting' || status === 'dead') {
|
|
111
|
+
this._setConnected(false);
|
|
112
|
+
this._comm = null;
|
|
113
|
+
}
|
|
114
|
+
if (status === 'idle' && !this._isConnected && this._kernel) {
|
|
115
|
+
// Kernel restarted — try to reconnect
|
|
116
|
+
this.connect(this._kernel).catch(e => {
|
|
117
|
+
console.error('Variable Explorer: reconnect failed', e);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|