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.
Files changed (57) hide show
  1. variable_explorer-0.7.0/.claude/settings.local.json +41 -0
  2. variable_explorer-0.7.0/.gitignore +11 -0
  3. variable_explorer-0.7.0/.yarnrc.yml +1 -0
  4. variable_explorer-0.7.0/LICENSE +21 -0
  5. variable_explorer-0.7.0/MANIFEST.in +15 -0
  6. variable_explorer-0.7.0/PKG-INFO +82 -0
  7. variable_explorer-0.7.0/README.md +55 -0
  8. variable_explorer-0.7.0/fix_frontend.py +52 -0
  9. variable_explorer-0.7.0/install.json +5 -0
  10. variable_explorer-0.7.0/package.json +91 -0
  11. variable_explorer-0.7.0/pyproject.toml +71 -0
  12. variable_explorer-0.7.0/schema/plugin.json +75 -0
  13. variable_explorer-0.7.0/src/comm/CommManager.ts +121 -0
  14. variable_explorer-0.7.0/src/comm/protocol.ts +206 -0
  15. variable_explorer-0.7.0/src/components/Breadcrumb.tsx +39 -0
  16. variable_explorer-0.7.0/src/components/CellReferenceBar.tsx +22 -0
  17. variable_explorer-0.7.0/src/components/DataGrid.tsx +489 -0
  18. variable_explorer-0.7.0/src/components/HistogramHeader.tsx +187 -0
  19. variable_explorer-0.7.0/src/components/MetadataSidebar.tsx +195 -0
  20. variable_explorer-0.7.0/src/components/ResizeHandle.tsx +49 -0
  21. variable_explorer-0.7.0/src/components/SqlPanel.tsx +335 -0
  22. variable_explorer-0.7.0/src/components/StatusBar.tsx +40 -0
  23. variable_explorer-0.7.0/src/components/VariableExplorerApp.tsx +486 -0
  24. variable_explorer-0.7.0/src/components/VariableList.tsx +114 -0
  25. variable_explorer-0.7.0/src/index.ts +204 -0
  26. variable_explorer-0.7.0/src/utils/colorScales.ts +27 -0
  27. variable_explorer-0.7.0/src/utils/histogramRenderer.ts +122 -0
  28. variable_explorer-0.7.0/src/utils/stylesheetCloner.ts +54 -0
  29. variable_explorer-0.7.0/src/widgets/DetachedWindow.ts +156 -0
  30. variable_explorer-0.7.0/style/ag-grid-overrides.css +98 -0
  31. variable_explorer-0.7.0/style/base.css +803 -0
  32. variable_explorer-0.7.0/style/index.css +2 -0
  33. variable_explorer-0.7.0/style/index.js +4 -0
  34. variable_explorer-0.7.0/tsconfig.json +24 -0
  35. variable_explorer-0.7.0/variable_explorer/__init__.py +7 -0
  36. variable_explorer-0.7.0/variable_explorer/_version.py +4 -0
  37. variable_explorer-0.7.0/variable_explorer/kernel/__init__.py +5 -0
  38. variable_explorer-0.7.0/variable_explorer/kernel/comm_handler.py +287 -0
  39. variable_explorer-0.7.0/variable_explorer/kernel/data_provider.py +287 -0
  40. variable_explorer-0.7.0/variable_explorer/kernel/editor.py +88 -0
  41. variable_explorer-0.7.0/variable_explorer/kernel/introspection.py +235 -0
  42. variable_explorer-0.7.0/variable_explorer/kernel/serialization.py +81 -0
  43. variable_explorer-0.7.0/variable_explorer/kernel/sorter.py +51 -0
  44. variable_explorer-0.7.0/variable_explorer/kernel/sql_handler.py +144 -0
  45. variable_explorer-0.7.0/variable_explorer/kernel/statistics.py +117 -0
  46. variable_explorer-0.7.0/variable_explorer/labextension/package.json +96 -0
  47. variable_explorer-0.7.0/variable_explorer/labextension/schemas/variable-explorer/package.json.orig +91 -0
  48. variable_explorer-0.7.0/variable_explorer/labextension/schemas/variable-explorer/plugin.json +75 -0
  49. variable_explorer-0.7.0/variable_explorer/labextension/static/140.0326e0e0e77185d73e92.js +1 -0
  50. variable_explorer-0.7.0/variable_explorer/labextension/static/719.b1e5abbcc7b2adac64e7.js +1 -0
  51. variable_explorer-0.7.0/variable_explorer/labextension/static/746.58a99d2f32f8834ed814.js +1 -0
  52. variable_explorer-0.7.0/variable_explorer/labextension/static/924.5e98cac11804e1a59d7e.js +1 -0
  53. variable_explorer-0.7.0/variable_explorer/labextension/static/927.d2072f9536bbda11eb79.js +1 -0
  54. variable_explorer-0.7.0/variable_explorer/labextension/static/remoteEntry.d175bc820329257b7c49.js +1 -0
  55. variable_explorer-0.7.0/variable_explorer/labextension/static/style.js +4 -0
  56. variable_explorer-0.7.0/variable_explorer/labextension/static/third-party-licenses.json +28 -0
  57. 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,11 @@
1
+ node_modules/
2
+ lib/
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .yarn/
7
+ *.tgz
8
+ *.tsbuildinfo
9
+ __pycache__/
10
+ *.pyc
11
+ .ipynb_checkpoints/
@@ -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)' }}>&#x26a0;</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,5 @@
1
+ {
2
+ "packageManager": "python",
3
+ "packageName": "variable_explorer",
4
+ "uninstallInstructions": "pip uninstall variable_explorer"
5
+ }
@@ -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
+ }