variable-explorer 0.1.0__py3-none-any.whl

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 (49) hide show
  1. variable_explorer/__init__.py +7 -0
  2. variable_explorer/_version.py +4 -0
  3. variable_explorer/kernel/__init__.py +5 -0
  4. variable_explorer/kernel/comm_handler.py +255 -0
  5. variable_explorer/kernel/data_provider.py +235 -0
  6. variable_explorer/kernel/editor.py +88 -0
  7. variable_explorer/kernel/introspection.py +186 -0
  8. variable_explorer/kernel/serialization.py +73 -0
  9. variable_explorer/kernel/sorter.py +34 -0
  10. variable_explorer/kernel/statistics.py +101 -0
  11. variable_explorer/labextension/build_log.json +726 -0
  12. variable_explorer/labextension/package.json +96 -0
  13. variable_explorer/labextension/schemas/variable-explorer/package.json.orig +91 -0
  14. variable_explorer/labextension/schemas/variable-explorer/plugin.json +75 -0
  15. variable_explorer/labextension/static/lib_index_js.88a2cd3be0f2bf49f0eb.js +1417 -0
  16. variable_explorer/labextension/static/lib_index_js.88a2cd3be0f2bf49f0eb.js.map +1 -0
  17. variable_explorer/labextension/static/remoteEntry.a8ed3dcc7548f0b68f93.js +576 -0
  18. variable_explorer/labextension/static/remoteEntry.a8ed3dcc7548f0b68f93.js.map +1 -0
  19. variable_explorer/labextension/static/style.js +4 -0
  20. variable_explorer/labextension/static/style_index_js-data_font_woff2_charset_utf-8_base64_d09GMgABAAAAABmsAAsAAAAANbQAABlcAAEAAAAAA-5c9677.c69a59632d259bde8f84.js +785 -0
  21. variable_explorer/labextension/static/style_index_js-data_font_woff2_charset_utf-8_base64_d09GMgABAAAAABmsAAsAAAAANbQAABlcAAEAAAAAA-5c9677.c69a59632d259bde8f84.js.map +1 -0
  22. variable_explorer/labextension/static/vendors-node_modules_ag-grid-community_dist_package_main_esm_mjs.c38425b170e91e5db052.js +50347 -0
  23. variable_explorer/labextension/static/vendors-node_modules_ag-grid-community_dist_package_main_esm_mjs.c38425b170e91e5db052.js.map +1 -0
  24. variable_explorer/labextension/static/vendors-node_modules_ag-grid-community_styles_ag-grid_css-node_modules_ag-grid-community_styl-7d25f0.7424d30423d9f1c112f6.js +8124 -0
  25. variable_explorer/labextension/static/vendors-node_modules_ag-grid-community_styles_ag-grid_css-node_modules_ag-grid-community_styl-7d25f0.7424d30423d9f1c112f6.js.map +1 -0
  26. variable_explorer/labextension/static/vendors-node_modules_ag-grid-react_dist_package_index_esm_mjs.ca52d36c364e6562240a.js +2917 -0
  27. variable_explorer/labextension/static/vendors-node_modules_ag-grid-react_dist_package_index_esm_mjs.ca52d36c364e6562240a.js.map +1 -0
  28. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/build_log.json +726 -0
  29. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/install.json +5 -0
  30. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/package.json +96 -0
  31. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/schemas/variable-explorer/package.json.orig +91 -0
  32. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/schemas/variable-explorer/plugin.json +75 -0
  33. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/lib_index_js.88a2cd3be0f2bf49f0eb.js +1417 -0
  34. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/lib_index_js.88a2cd3be0f2bf49f0eb.js.map +1 -0
  35. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/remoteEntry.a8ed3dcc7548f0b68f93.js +576 -0
  36. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/remoteEntry.a8ed3dcc7548f0b68f93.js.map +1 -0
  37. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/style.js +4 -0
  38. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/style_index_js-data_font_woff2_charset_utf-8_base64_d09GMgABAAAAABmsAAsAAAAANbQAABlcAAEAAAAAA-5c9677.c69a59632d259bde8f84.js +785 -0
  39. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/style_index_js-data_font_woff2_charset_utf-8_base64_d09GMgABAAAAABmsAAsAAAAANbQAABlcAAEAAAAAA-5c9677.c69a59632d259bde8f84.js.map +1 -0
  40. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/vendors-node_modules_ag-grid-community_dist_package_main_esm_mjs.c38425b170e91e5db052.js +50347 -0
  41. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/vendors-node_modules_ag-grid-community_dist_package_main_esm_mjs.c38425b170e91e5db052.js.map +1 -0
  42. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/vendors-node_modules_ag-grid-community_styles_ag-grid_css-node_modules_ag-grid-community_styl-7d25f0.7424d30423d9f1c112f6.js +8124 -0
  43. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/vendors-node_modules_ag-grid-community_styles_ag-grid_css-node_modules_ag-grid-community_styl-7d25f0.7424d30423d9f1c112f6.js.map +1 -0
  44. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/vendors-node_modules_ag-grid-react_dist_package_index_esm_mjs.ca52d36c364e6562240a.js +2917 -0
  45. variable_explorer-0.1.0.data/data/share/jupyter/labextensions/variable-explorer/static/vendors-node_modules_ag-grid-react_dist_package_index_esm_mjs.ca52d36c364e6562240a.js.map +1 -0
  46. variable_explorer-0.1.0.dist-info/METADATA +80 -0
  47. variable_explorer-0.1.0.dist-info/RECORD +49 -0
  48. variable_explorer-0.1.0.dist-info/WHEEL +4 -0
  49. variable_explorer-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1417 @@
1
+ "use strict";
2
+ (self["webpackChunkvariable_explorer"] = self["webpackChunkvariable_explorer"] || []).push([["lib_index_js"],{
3
+
4
+ /***/ "./lib/comm/CommManager.js"
5
+ /*!*********************************!*\
6
+ !*** ./lib/comm/CommManager.js ***!
7
+ \*********************************/
8
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
9
+
10
+ __webpack_require__.r(__webpack_exports__);
11
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
12
+ /* harmony export */ CommManager: () => (/* binding */ CommManager)
13
+ /* harmony export */ });
14
+ /* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @lumino/signaling */ "webpack/sharing/consume/default/@lumino/signaling");
15
+ /* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_lumino_signaling__WEBPACK_IMPORTED_MODULE_0__);
16
+
17
+ const COMM_TARGET = 'variable_explorer';
18
+ const INIT_CODE = `
19
+ try:
20
+ from variable_explorer.kernel import init_comm as _ve_init
21
+ _ve_init()
22
+ del _ve_init
23
+ except ImportError as e:
24
+ print(f"Variable Explorer: kernel package not found: {e}")
25
+ except Exception as e:
26
+ print(f"Variable Explorer: init error: {e}")
27
+ `.trim();
28
+ class CommManager {
29
+ constructor() {
30
+ this._comm = null;
31
+ this._kernel = null;
32
+ this._messageReceived = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_0__.Signal(this);
33
+ this._connectionChanged = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_0__.Signal(this);
34
+ this._isConnected = false;
35
+ }
36
+ get messageReceived() {
37
+ return this._messageReceived;
38
+ }
39
+ get connectionChanged() {
40
+ return this._connectionChanged;
41
+ }
42
+ get isConnected() {
43
+ return this._isConnected;
44
+ }
45
+ async connect(kernel) {
46
+ // Disconnect existing
47
+ this.disconnect();
48
+ this._kernel = kernel;
49
+ // Inject the Python init code
50
+ const future = kernel.requestExecute({
51
+ code: INIT_CODE,
52
+ silent: true,
53
+ store_history: false
54
+ });
55
+ await future.done;
56
+ // Open a comm from the frontend
57
+ this._comm = kernel.createComm(COMM_TARGET);
58
+ // Handle incoming messages
59
+ this._comm.onMsg = (msg) => {
60
+ const data = msg.content.data;
61
+ if (data && data.type) {
62
+ this._messageReceived.emit(data);
63
+ }
64
+ };
65
+ this._comm.onClose = () => {
66
+ this._setConnected(false);
67
+ };
68
+ // Open the comm channel
69
+ await this._comm.open({}).done;
70
+ this._setConnected(true);
71
+ // Listen for kernel shutdown/restart
72
+ kernel.statusChanged.connect(this._onKernelStatus, this);
73
+ }
74
+ send(msg) {
75
+ if (this._comm && this._isConnected) {
76
+ this._comm.send(msg);
77
+ }
78
+ }
79
+ refresh() {
80
+ this.send({ type: 'refresh' });
81
+ }
82
+ disconnect() {
83
+ if (this._kernel) {
84
+ this._kernel.statusChanged.disconnect(this._onKernelStatus, this);
85
+ }
86
+ if (this._comm) {
87
+ try {
88
+ this._comm.close({});
89
+ }
90
+ catch (_a) {
91
+ // Comm may already be closed
92
+ }
93
+ this._comm = null;
94
+ }
95
+ this._kernel = null;
96
+ this._setConnected(false);
97
+ }
98
+ _setConnected(connected) {
99
+ if (this._isConnected !== connected) {
100
+ this._isConnected = connected;
101
+ this._connectionChanged.emit(connected);
102
+ }
103
+ }
104
+ _onKernelStatus(kernel, status) {
105
+ if (status === 'restarting' || status === 'dead') {
106
+ this._setConnected(false);
107
+ this._comm = null;
108
+ }
109
+ if (status === 'idle' && !this._isConnected && this._kernel) {
110
+ // Kernel restarted — try to reconnect
111
+ this.connect(this._kernel).catch(e => {
112
+ console.error('Variable Explorer: reconnect failed', e);
113
+ });
114
+ }
115
+ }
116
+ }
117
+
118
+
119
+ /***/ },
120
+
121
+ /***/ "./lib/components/CellReferenceBar.js"
122
+ /*!********************************************!*\
123
+ !*** ./lib/components/CellReferenceBar.js ***!
124
+ \********************************************/
125
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
126
+
127
+ __webpack_require__.r(__webpack_exports__);
128
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
129
+ /* harmony export */ CellReferenceBar: () => (/* binding */ CellReferenceBar)
130
+ /* harmony export */ });
131
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
132
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
133
+
134
+ const CellReferenceBar = ({ rowIndex, colName, value }) => {
135
+ const displayValue = value == null ? 'null' : String(value);
136
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-cell-reference-bar" },
137
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-cell-location" },
138
+ "Row ",
139
+ rowIndex.toLocaleString(),
140
+ " / Col: ",
141
+ colName),
142
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-cell-value", title: displayValue }, displayValue)));
143
+ };
144
+
145
+
146
+ /***/ },
147
+
148
+ /***/ "./lib/components/DataGrid.js"
149
+ /*!************************************!*\
150
+ !*** ./lib/components/DataGrid.js ***!
151
+ \************************************/
152
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
153
+
154
+ __webpack_require__.r(__webpack_exports__);
155
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
156
+ /* harmony export */ DataGrid: () => (/* binding */ DataGrid)
157
+ /* harmony export */ });
158
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
159
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
160
+ /* harmony import */ var ag_grid_react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ag-grid-react */ "webpack/sharing/consume/default/ag-grid-react/ag-grid-react");
161
+ /* harmony import */ var _HistogramHeader__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./HistogramHeader */ "./lib/components/HistogramHeader.js");
162
+ /* harmony import */ var _utils_colorScales__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../utils/colorScales */ "./lib/utils/colorScales.js");
163
+
164
+
165
+
166
+
167
+ const DataGrid = ({ rows, columns, columnStats, totalRows, sortModel, hiddenColumns, loading, onSortChanged, onLoadMore, onCellSelected, onCellEdit }) => {
168
+ const gridRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);
169
+ // Build a stats lookup map
170
+ const statsMap = react__WEBPACK_IMPORTED_MODULE_0__.useMemo(() => {
171
+ const map = {};
172
+ for (const s of columnStats) {
173
+ map[s.name] = s;
174
+ }
175
+ return map;
176
+ }, [columnStats]);
177
+ // Build AG Grid column definitions
178
+ const colDefs = react__WEBPACK_IMPORTED_MODULE_0__.useMemo(() => {
179
+ // Row index column
180
+ const indexCol = {
181
+ headerName: '#',
182
+ colId: '__index__',
183
+ valueGetter: (params) => {
184
+ var _a;
185
+ return ((_a = params.node) === null || _a === void 0 ? void 0 : _a.rowIndex) != null ? params.node.rowIndex : '';
186
+ },
187
+ width: 70,
188
+ pinned: 'left',
189
+ sortable: true,
190
+ resizable: false,
191
+ cellStyle: {
192
+ color: 'var(--jp-ui-font-color2)',
193
+ fontWeight: '500',
194
+ fontSize: '11px'
195
+ }
196
+ };
197
+ const dataCols = columns
198
+ .filter(c => !hiddenColumns.has(c.name))
199
+ .map(col => {
200
+ const stats = statsMap[col.name];
201
+ const def = {
202
+ headerName: col.name,
203
+ field: col.name,
204
+ sortable: true,
205
+ resizable: true,
206
+ editable: true,
207
+ minWidth: 80,
208
+ headerComponent: _HistogramHeader__WEBPACK_IMPORTED_MODULE_2__.HistogramHeader,
209
+ headerComponentParams: {
210
+ displayName: col.name,
211
+ stats: stats
212
+ }
213
+ };
214
+ // Conditional formatting
215
+ if (col.isNumeric && (stats === null || stats === void 0 ? void 0 : stats.histogram) && stats.histogram.type === 'numeric') {
216
+ const h = stats.histogram;
217
+ def.cellStyle = (params) => {
218
+ if (params.value == null) {
219
+ return { backgroundColor: 'var(--jp-layout-color2)', opacity: 0.5 };
220
+ }
221
+ return {
222
+ backgroundColor: (0,_utils_colorScales__WEBPACK_IMPORTED_MODULE_3__.numericHeatmap)(params.value, h.min, h.max),
223
+ color: 'var(--jp-ui-font-color0)'
224
+ };
225
+ };
226
+ }
227
+ else if (col.isBool) {
228
+ def.cellStyle = (params) => {
229
+ if (params.value == null) {
230
+ return { backgroundColor: 'var(--jp-layout-color2)', opacity: 0.5 };
231
+ }
232
+ return {
233
+ backgroundColor: (0,_utils_colorScales__WEBPACK_IMPORTED_MODULE_3__.booleanColor)(params.value),
234
+ color: '#fff',
235
+ fontWeight: '600',
236
+ textAlign: 'center'
237
+ };
238
+ };
239
+ }
240
+ // Format numbers
241
+ if (col.isNumeric) {
242
+ def.valueFormatter = (params) => {
243
+ if (params.value == null)
244
+ return '';
245
+ const val = Number(params.value);
246
+ if (Number.isInteger(val))
247
+ return val.toLocaleString();
248
+ return val.toLocaleString(undefined, { maximumFractionDigits: 4 });
249
+ };
250
+ }
251
+ // Format dates
252
+ if (col.isDatetime) {
253
+ def.valueFormatter = (params) => {
254
+ if (params.value == null)
255
+ return '';
256
+ return new Date(params.value).toLocaleString();
257
+ };
258
+ }
259
+ return def;
260
+ });
261
+ return [indexCol, ...dataCols];
262
+ }, [columns, statsMap, hiddenColumns]);
263
+ // Handle sort
264
+ const handleSortChanged = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((event) => {
265
+ const colState = event.api.getColumnState();
266
+ const sorted = colState.filter((c) => c.sort);
267
+ // If the index column (#) is being sorted, clear all sorts → reset to original order
268
+ if (sorted.some((c) => c.colId === '__index__')) {
269
+ event.api.applyColumnState({ defaultState: { sort: null } });
270
+ onSortChanged([]);
271
+ return;
272
+ }
273
+ const newSortModel = sorted
274
+ .sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
275
+ .map((c) => ({
276
+ colId: c.colId,
277
+ sort: c.sort
278
+ }));
279
+ onSortChanged(newSortModel);
280
+ }, [onSortChanged]);
281
+ // Handle cell click
282
+ const handleCellClicked = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((event) => {
283
+ if (event.colDef.field && event.rowIndex != null) {
284
+ onCellSelected(event.rowIndex, event.colDef.field, event.value);
285
+ }
286
+ }, [onCellSelected]);
287
+ // Handle cell edit
288
+ const handleCellEditRequest = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((event) => {
289
+ if (event.colDef.field && event.rowIndex != null) {
290
+ onCellEdit(event.rowIndex, event.colDef.field, String(event.newValue));
291
+ // Optimistic update
292
+ const rowNode = event.api.getRowNode(String(event.rowIndex));
293
+ if (rowNode) {
294
+ rowNode.setDataValue(event.colDef.field, event.newValue);
295
+ }
296
+ }
297
+ }, [onCellEdit]);
298
+ // Infinite scroll: load more rows when scrolled near bottom
299
+ const handleBodyScrollEnd = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((event) => {
300
+ if (loading)
301
+ return;
302
+ const lastRow = event.api.getLastDisplayedRow();
303
+ if (lastRow >= rows.length - 200 && rows.length < totalRows) {
304
+ onLoadMore(rows.length);
305
+ }
306
+ }, [loading, rows.length, totalRows, onLoadMore]);
307
+ // Detect JupyterLab dark theme
308
+ const isDark = document.body.getAttribute('data-jp-theme-light') === 'false';
309
+ const themeClass = isDark ? 'ag-theme-quartz-dark' : 'ag-theme-quartz';
310
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-grid-wrapper" },
311
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: themeClass, style: { width: '100%', height: '100%' } },
312
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement(ag_grid_react__WEBPACK_IMPORTED_MODULE_1__.AgGridReact, { ref: gridRef, rowData: rows, columnDefs: colDefs, defaultColDef: {
313
+ sortable: true,
314
+ resizable: true,
315
+ minWidth: 60
316
+ }, headerHeight: 64, rowHeight: 28, animateRows: false, suppressMovableColumns: false, readOnlyEdit: true, onSortChanged: handleSortChanged, onCellClicked: handleCellClicked, onCellEditRequest: handleCellEditRequest, onBodyScrollEnd: handleBodyScrollEnd, getRowId: (params) => { var _a, _b, _c; return String((_c = (_a = params.data.__row_index__) !== null && _a !== void 0 ? _a : (_b = params.node) === null || _b === void 0 ? void 0 : _b.rowIndex) !== null && _c !== void 0 ? _c : 0); }, loading: loading }))));
317
+ };
318
+
319
+
320
+ /***/ },
321
+
322
+ /***/ "./lib/components/HistogramHeader.js"
323
+ /*!*******************************************!*\
324
+ !*** ./lib/components/HistogramHeader.js ***!
325
+ \*******************************************/
326
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
327
+
328
+ __webpack_require__.r(__webpack_exports__);
329
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
330
+ /* harmony export */ HistogramHeader: () => (/* binding */ HistogramHeader)
331
+ /* harmony export */ });
332
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
333
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
334
+ /* harmony import */ var _utils_histogramRenderer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/histogramRenderer */ "./lib/utils/histogramRenderer.js");
335
+
336
+
337
+ function getTooltipText(histogram, mouseX, mouseY, canvasWidth, canvasHeight) {
338
+ const fmt = (v) => {
339
+ if (Number.isInteger(v) && Math.abs(v) < 1e6)
340
+ return v.toLocaleString();
341
+ return v.toLocaleString(undefined, { maximumFractionDigits: 2 });
342
+ };
343
+ if (histogram.type === 'numeric') {
344
+ const { counts, edges } = histogram;
345
+ const binIndex = Math.floor((mouseX / canvasWidth) * counts.length);
346
+ if (binIndex < 0 || binIndex >= counts.length)
347
+ return null;
348
+ return `${fmt(edges[binIndex])} \u2013 ${fmt(edges[binIndex + 1])}: ${counts[binIndex].toLocaleString()}`;
349
+ }
350
+ if (histogram.type === 'categorical') {
351
+ const { labels, counts } = histogram;
352
+ const barCount = Math.min(labels.length, 8);
353
+ // Bars are drawn horizontally, stacked vertically
354
+ const barHeight = canvasHeight / barCount;
355
+ const barIndex = Math.floor(mouseY / barHeight);
356
+ if (barIndex < 0 || barIndex >= barCount)
357
+ return null;
358
+ return `${labels[barIndex]}: ${counts[barIndex].toLocaleString()}`;
359
+ }
360
+ if (histogram.type === 'boolean') {
361
+ const total = histogram.trueCount + histogram.falseCount + histogram.nullCount;
362
+ if (total === 0)
363
+ return null;
364
+ const trueWidth = (histogram.trueCount / total) * canvasWidth;
365
+ const falseWidth = (histogram.falseCount / total) * canvasWidth;
366
+ if (mouseX < trueWidth) {
367
+ return `True: ${histogram.trueCount.toLocaleString()} (${Math.round(100 * histogram.trueCount / total)}%)`;
368
+ }
369
+ else if (mouseX < trueWidth + falseWidth) {
370
+ return `False: ${histogram.falseCount.toLocaleString()} (${Math.round(100 * histogram.falseCount / total)}%)`;
371
+ }
372
+ else {
373
+ return `Null: ${histogram.nullCount.toLocaleString()} (${Math.round(100 * histogram.nullCount / total)}%)`;
374
+ }
375
+ }
376
+ return null;
377
+ }
378
+ const HistogramHeader = ({ displayName, stats, column, setSort, api }) => {
379
+ const canvasRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);
380
+ const tooltipRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);
381
+ const [sortState, setSortState] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);
382
+ const [tooltip, setTooltip] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);
383
+ // Draw histogram when stats change
384
+ react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {
385
+ const canvas = canvasRef.current;
386
+ if (!canvas || !(stats === null || stats === void 0 ? void 0 : stats.histogram))
387
+ return;
388
+ (0,_utils_histogramRenderer__WEBPACK_IMPORTED_MODULE_1__.drawHistogram)(canvas, stats.histogram);
389
+ }, [stats]);
390
+ // Track sort state
391
+ react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {
392
+ var _a;
393
+ if (!column || !api)
394
+ return;
395
+ const onSortChanged = () => {
396
+ var _a;
397
+ const colState = (_a = api.getColumnState) === null || _a === void 0 ? void 0 : _a.call(api);
398
+ if (colState) {
399
+ const myState = colState.find((s) => s.colId === column.getColId());
400
+ setSortState((myState === null || myState === void 0 ? void 0 : myState.sort) || null);
401
+ }
402
+ };
403
+ (_a = api.addEventListener) === null || _a === void 0 ? void 0 : _a.call(api, 'sortChanged', onSortChanged);
404
+ return () => {
405
+ var _a;
406
+ (_a = api.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(api, 'sortChanged', onSortChanged);
407
+ };
408
+ }, [column, api]);
409
+ const handleClick = (e) => {
410
+ if (!setSort)
411
+ return;
412
+ let nextSort;
413
+ if (sortState === null) {
414
+ nextSort = 'asc';
415
+ }
416
+ else if (sortState === 'asc') {
417
+ nextSort = 'desc';
418
+ }
419
+ else {
420
+ nextSort = '';
421
+ }
422
+ setSort(nextSort, e.shiftKey);
423
+ };
424
+ const handleCanvasMouseMove = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((e) => {
425
+ if (!(stats === null || stats === void 0 ? void 0 : stats.histogram) || !canvasRef.current)
426
+ return;
427
+ const rect = canvasRef.current.getBoundingClientRect();
428
+ const mouseX = e.clientX - rect.left;
429
+ const mouseY = e.clientY - rect.top;
430
+ const text = getTooltipText(stats.histogram, mouseX, mouseY, rect.width, rect.height);
431
+ if (text) {
432
+ setTooltip({ text, x: e.clientX, y: rect.top - 4 });
433
+ }
434
+ else {
435
+ setTooltip(null);
436
+ }
437
+ }, [stats]);
438
+ const handleCanvasMouseLeave = react__WEBPACK_IMPORTED_MODULE_0__.useCallback(() => {
439
+ setTooltip(null);
440
+ }, []);
441
+ const sortIcon = sortState === 'asc' ? ' \u25b2' : sortState === 'desc' ? ' \u25bc' : '';
442
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-histogram-header", style: { width: '100%' } },
443
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-header-label", onClick: handleClick, title: `Click to sort by ${displayName}` },
444
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, displayName),
445
+ sortIcon && react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-sort-icon" }, sortIcon)),
446
+ (stats === null || stats === void 0 ? void 0 : stats.histogram) && (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { style: { position: 'relative', width: '100%' } },
447
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("canvas", { ref: canvasRef, width: 120, height: 30, style: { width: '100%', height: '30px', cursor: 'crosshair' }, onMouseMove: handleCanvasMouseMove, onMouseLeave: handleCanvasMouseLeave }),
448
+ tooltip && (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { ref: tooltipRef, className: "ve-histogram-tooltip", style: { left: tooltip.x, top: tooltip.y, transform: 'translate(-50%, -100%)' } }, tooltip.text.split('\n').map((line, i) => (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { key: i }, line)))))))));
449
+ };
450
+
451
+
452
+ /***/ },
453
+
454
+ /***/ "./lib/components/MetadataSidebar.js"
455
+ /*!*******************************************!*\
456
+ !*** ./lib/components/MetadataSidebar.js ***!
457
+ \*******************************************/
458
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
459
+
460
+ __webpack_require__.r(__webpack_exports__);
461
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
462
+ /* harmony export */ MetadataSidebar: () => (/* binding */ MetadataSidebar)
463
+ /* harmony export */ });
464
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
465
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
466
+
467
+ function formatBytes(bytes) {
468
+ if (bytes < 1024)
469
+ return `${bytes} B`;
470
+ if (bytes < 1024 * 1024)
471
+ return `${(bytes / 1024).toFixed(1)} KB`;
472
+ if (bytes < 1024 * 1024 * 1024)
473
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
474
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
475
+ }
476
+ const MetadataSidebar = ({ columns, columnStats, hiddenColumns, onToggleColumn, selectedVarInfo }) => {
477
+ var _a, _b, _c, _d;
478
+ const [filter, setFilter] = react__WEBPACK_IMPORTED_MODULE_0__.useState('');
479
+ const [selectedCol, setSelectedCol] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);
480
+ const [propertiesHeight, setPropertiesHeight] = react__WEBPACK_IMPORTED_MODULE_0__.useState(220);
481
+ const handleRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);
482
+ const statsMap = react__WEBPACK_IMPORTED_MODULE_0__.useMemo(() => {
483
+ const map = {};
484
+ for (const s of columnStats) {
485
+ map[s.name] = s;
486
+ }
487
+ return map;
488
+ }, [columnStats]);
489
+ const filtered = react__WEBPACK_IMPORTED_MODULE_0__.useMemo(() => {
490
+ if (!filter)
491
+ return columns;
492
+ const lower = filter.toLowerCase();
493
+ return columns.filter(c => c.name.toLowerCase().includes(lower));
494
+ }, [columns, filter]);
495
+ const selectedStats = selectedCol ? statsMap[selectedCol] : null;
496
+ const selectedColDef = selectedCol ? columns.find(c => c.name === selectedCol) : null;
497
+ // Horizontal resize for properties panel
498
+ const onHandleMouseDown = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((e) => {
499
+ var _a;
500
+ e.preventDefault();
501
+ e.stopPropagation();
502
+ const startY = e.clientY;
503
+ const baseHeight = propertiesHeight;
504
+ const doc = ((_a = handleRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) || document;
505
+ const onMouseMove = (moveEvent) => {
506
+ moveEvent.preventDefault();
507
+ const delta = startY - moveEvent.clientY; // dragging up = increase height
508
+ setPropertiesHeight(Math.max(80, Math.min(500, baseHeight + delta)));
509
+ };
510
+ const onMouseUp = () => {
511
+ doc.removeEventListener('mousemove', onMouseMove);
512
+ doc.removeEventListener('mouseup', onMouseUp);
513
+ doc.body.style.cursor = '';
514
+ doc.body.style.userSelect = '';
515
+ };
516
+ doc.addEventListener('mousemove', onMouseMove);
517
+ doc.addEventListener('mouseup', onMouseUp);
518
+ doc.body.style.cursor = 'row-resize';
519
+ doc.body.style.userSelect = 'none';
520
+ }, [propertiesHeight]);
521
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-metadata-sidebar" },
522
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-metadata-header" },
523
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", null, "Columns"),
524
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { style: { fontSize: '11px', fontWeight: 'normal', opacity: 0.6 } }, columns.length)),
525
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("input", { className: "ve-metadata-search", type: "text", placeholder: "Filter columns...", value: filter, onChange: e => setFilter(e.target.value) }),
526
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-metadata-columns" }, filtered.map(col => {
527
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { key: col.name, className: "ve-metadata-column-item", onClick: () => setSelectedCol(col.name), style: {
528
+ cursor: 'pointer',
529
+ background: col.name === selectedCol ? 'var(--jp-layout-color2)' : undefined
530
+ } },
531
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("input", { type: "checkbox", checked: !hiddenColumns.has(col.name), onChange: () => onToggleColumn(col.name), onClick: e => e.stopPropagation() }),
532
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-col-name", title: col.name }, col.name),
533
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-col-type" }, col.dtype)));
534
+ })),
535
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { ref: handleRef, className: "ve-resize-handle-horizontal", onMouseDown: onHandleMouseDown }),
536
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-properties-panel", style: { height: propertiesHeight, minHeight: 80 } }, selectedStats && selectedColDef ? (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
537
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("h4", null,
538
+ "Column: ",
539
+ selectedCol),
540
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("table", { className: "ve-properties-table" },
541
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tbody", null,
542
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
543
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Name"),
544
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedColDef.name)),
545
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
546
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Type"),
547
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedColDef.dtype)),
548
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
549
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Nulls"),
550
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.nullCount.toLocaleString())),
551
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
552
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Unique"),
553
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.uniqueCount.toLocaleString())),
554
+ ((_a = selectedStats.histogram) === null || _a === void 0 ? void 0 : _a.type) === 'numeric' && (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
555
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
556
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Min"),
557
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.histogram.min.toLocaleString(undefined, { maximumFractionDigits: 4 }))),
558
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
559
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Max"),
560
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.histogram.max.toLocaleString(undefined, { maximumFractionDigits: 4 }))),
561
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
562
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Mean"),
563
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.histogram.mean.toLocaleString(undefined, { maximumFractionDigits: 4 }))),
564
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
565
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Std"),
566
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.histogram.std.toLocaleString(undefined, { maximumFractionDigits: 4 }))))),
567
+ ((_b = selectedStats.histogram) === null || _b === void 0 ? void 0 : _b.type) === 'boolean' && (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
568
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
569
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "True"),
570
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.histogram.trueCount.toLocaleString())),
571
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
572
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "False"),
573
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.histogram.falseCount.toLocaleString())))),
574
+ ((_c = selectedStats.histogram) === null || _c === void 0 ? void 0 : _c.type) === 'categorical' && (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
575
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
576
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Top values"),
577
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedStats.histogram.labels.slice(0, 5).map((label, i) => {
578
+ var _a;
579
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { key: i, style: { fontSize: '11px' } },
580
+ label,
581
+ ": ",
582
+ selectedStats.histogram.type === 'categorical'
583
+ ? (_a = selectedStats.histogram.counts[i]) === null || _a === void 0 ? void 0 : _a.toLocaleString()
584
+ : ''));
585
+ }))))))))) : selectedVarInfo ? (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
586
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("h4", null, "Dataset"),
587
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("table", { className: "ve-properties-table" },
588
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tbody", null,
589
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
590
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Name"),
591
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedVarInfo.name)),
592
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
593
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Type"),
594
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedVarInfo.typeName)),
595
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
596
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Shape"),
597
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, selectedVarInfo.shape.map(s => s.toLocaleString()).join(' \u00d7 '))),
598
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
599
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Memory"),
600
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, formatBytes(selectedVarInfo.memoryBytes))),
601
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
602
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Variables"),
603
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, columns.length)),
604
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("tr", null,
605
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, "Observations"),
606
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("td", null, (_d = selectedVarInfo.shape[0]) === null || _d === void 0 ? void 0 : _d.toLocaleString())))))) : (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { style: { fontSize: '12px', opacity: 0.5, textAlign: 'center', paddingTop: 12 } }, "Click a column for details")))));
607
+ };
608
+
609
+
610
+ /***/ },
611
+
612
+ /***/ "./lib/components/ResizeHandle.js"
613
+ /*!****************************************!*\
614
+ !*** ./lib/components/ResizeHandle.js ***!
615
+ \****************************************/
616
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
617
+
618
+ __webpack_require__.r(__webpack_exports__);
619
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
620
+ /* harmony export */ ResizeHandle: () => (/* binding */ ResizeHandle)
621
+ /* harmony export */ });
622
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
623
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
624
+
625
+ const ResizeHandle = ({ onResizeStart, onResize, direction, invert }) => {
626
+ const handleRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef(null);
627
+ const onMouseDown = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((e) => {
628
+ var _a;
629
+ e.preventDefault();
630
+ e.stopPropagation();
631
+ const startX = e.clientX;
632
+ const baseWidth = onResizeStart();
633
+ // Use the ownerDocument so this works in detached popup windows
634
+ const doc = ((_a = handleRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) || document;
635
+ const onMouseMove = (moveEvent) => {
636
+ moveEvent.preventDefault();
637
+ const delta = moveEvent.clientX - startX;
638
+ const newWidth = invert ? baseWidth - delta : baseWidth + delta;
639
+ onResize(newWidth);
640
+ };
641
+ const onMouseUp = () => {
642
+ doc.removeEventListener('mousemove', onMouseMove);
643
+ doc.removeEventListener('mouseup', onMouseUp);
644
+ doc.body.style.cursor = '';
645
+ doc.body.style.userSelect = '';
646
+ };
647
+ doc.addEventListener('mousemove', onMouseMove);
648
+ doc.addEventListener('mouseup', onMouseUp);
649
+ doc.body.style.cursor = 'col-resize';
650
+ doc.body.style.userSelect = 'none';
651
+ }, [onResizeStart, onResize, invert]);
652
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { ref: handleRef, className: "ve-resize-handle", onMouseDown: onMouseDown }));
653
+ };
654
+
655
+
656
+ /***/ },
657
+
658
+ /***/ "./lib/components/StatusBar.js"
659
+ /*!*************************************!*\
660
+ !*** ./lib/components/StatusBar.js ***!
661
+ \*************************************/
662
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
663
+
664
+ __webpack_require__.r(__webpack_exports__);
665
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
666
+ /* harmony export */ StatusBar: () => (/* binding */ StatusBar)
667
+ /* harmony export */ });
668
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
669
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
670
+
671
+ const StatusBar = ({ selectedVar, totalRows, totalCols, sortModel, connected }) => {
672
+ const sortDisplay = sortModel.length > 0
673
+ ? sortModel.map(s => `${s.colId} ${s.sort}`).join(', ')
674
+ : 'none';
675
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-status-bar" },
676
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-status-item", style: { color: connected ? 'var(--jp-success-color1)' : 'var(--jp-error-color1)' } }, connected ? '\u25cf Connected' : '\u25cb Disconnected'),
677
+ selectedVar && (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
678
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-status-separator" }, "|"),
679
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-status-item" },
680
+ "Vars: ",
681
+ totalCols),
682
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-status-separator" }, "|"),
683
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-status-item" },
684
+ "Obs: ",
685
+ totalRows.toLocaleString()),
686
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-status-separator" }, "|"),
687
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-status-item" },
688
+ "Sorted: ",
689
+ sortDisplay)))));
690
+ };
691
+
692
+
693
+ /***/ },
694
+
695
+ /***/ "./lib/components/VariableExplorerApp.js"
696
+ /*!***********************************************!*\
697
+ !*** ./lib/components/VariableExplorerApp.js ***!
698
+ \***********************************************/
699
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
700
+
701
+ __webpack_require__.r(__webpack_exports__);
702
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
703
+ /* harmony export */ VariableExplorerApp: () => (/* binding */ VariableExplorerApp)
704
+ /* harmony export */ });
705
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
706
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
707
+ /* harmony import */ var _VariableList__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./VariableList */ "./lib/components/VariableList.js");
708
+ /* harmony import */ var _DataGrid__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./DataGrid */ "./lib/components/DataGrid.js");
709
+ /* harmony import */ var _MetadataSidebar__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./MetadataSidebar */ "./lib/components/MetadataSidebar.js");
710
+ /* harmony import */ var _CellReferenceBar__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./CellReferenceBar */ "./lib/components/CellReferenceBar.js");
711
+ /* harmony import */ var _StatusBar__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./StatusBar */ "./lib/components/StatusBar.js");
712
+ /* harmony import */ var _ResizeHandle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ResizeHandle */ "./lib/components/ResizeHandle.js");
713
+
714
+
715
+
716
+
717
+
718
+
719
+
720
+ const VariableExplorerApp = ({ commManager }) => {
721
+ const [connected, setConnected] = react__WEBPACK_IMPORTED_MODULE_0__.useState(commManager.isConnected);
722
+ const [variables, setVariables] = react__WEBPACK_IMPORTED_MODULE_0__.useState([]);
723
+ const [selectedVar, setSelectedVar] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);
724
+ const [rows, setRows] = react__WEBPACK_IMPORTED_MODULE_0__.useState([]);
725
+ const [totalRows, setTotalRows] = react__WEBPACK_IMPORTED_MODULE_0__.useState(0);
726
+ const [columns, setColumns] = react__WEBPACK_IMPORTED_MODULE_0__.useState([]);
727
+ const [columnStats, setColumnStats] = react__WEBPACK_IMPORTED_MODULE_0__.useState([]);
728
+ const [sortModel, setSortModel] = react__WEBPACK_IMPORTED_MODULE_0__.useState([]);
729
+ const [cellSelection, setCellSelection] = react__WEBPACK_IMPORTED_MODULE_0__.useState(null);
730
+ const [showSidebar, setShowSidebar] = react__WEBPACK_IMPORTED_MODULE_0__.useState(true);
731
+ const [loading, setLoading] = react__WEBPACK_IMPORTED_MODULE_0__.useState(false);
732
+ const [hiddenColumns, setHiddenColumns] = react__WEBPACK_IMPORTED_MODULE_0__.useState(new Set());
733
+ // Panel sizing
734
+ const [leftWidth, setLeftWidth] = react__WEBPACK_IMPORTED_MODULE_0__.useState(220);
735
+ const [rightWidth, setRightWidth] = react__WEBPACK_IMPORTED_MODULE_0__.useState(280);
736
+ const onLeftResizeStart = react__WEBPACK_IMPORTED_MODULE_0__.useCallback(() => leftWidth, [leftWidth]);
737
+ const onLeftResize = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((newWidth) => {
738
+ setLeftWidth(Math.max(120, Math.min(500, newWidth)));
739
+ }, []);
740
+ // For the right panel, dragging right = panel shrinks, so invert
741
+ const onRightResizeStart = react__WEBPACK_IMPORTED_MODULE_0__.useCallback(() => rightWidth, [rightWidth]);
742
+ const onRightResize = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((newWidth) => {
743
+ // ResizeHandle sends base + delta. For the right side,
744
+ // we want: base - delta = base - (newWidth - base) = 2*base - newWidth
745
+ // But we don't have base here. Instead, let's just negate in the component.
746
+ setRightWidth(Math.max(150, Math.min(500, newWidth)));
747
+ }, []);
748
+ // Listen for connection changes
749
+ react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {
750
+ const onConnectionChanged = (_, isConnected) => {
751
+ setConnected(isConnected);
752
+ if (!isConnected) {
753
+ setVariables([]);
754
+ setSelectedVar(null);
755
+ setRows([]);
756
+ }
757
+ };
758
+ commManager.connectionChanged.connect(onConnectionChanged);
759
+ return () => {
760
+ commManager.connectionChanged.disconnect(onConnectionChanged);
761
+ };
762
+ }, [commManager]);
763
+ // Listen for kernel messages
764
+ react__WEBPACK_IMPORTED_MODULE_0__.useEffect(() => {
765
+ const onMessage = (_, msg) => {
766
+ switch (msg.type) {
767
+ case 'variable_list':
768
+ setVariables(msg.variables);
769
+ break;
770
+ case 'data_page': {
771
+ const dp = msg;
772
+ if (dp.startRow === 0) {
773
+ setRows(dp.rows);
774
+ }
775
+ else {
776
+ setRows(prev => [...prev, ...dp.rows]);
777
+ }
778
+ setTotalRows(dp.totalRows);
779
+ setColumns(dp.columns);
780
+ setLoading(false);
781
+ break;
782
+ }
783
+ case 'column_stats': {
784
+ const cs = msg;
785
+ setColumnStats(cs.stats);
786
+ break;
787
+ }
788
+ case 'error':
789
+ console.error('Variable Explorer kernel error:', msg.message);
790
+ setLoading(false);
791
+ break;
792
+ }
793
+ };
794
+ commManager.messageReceived.connect(onMessage);
795
+ return () => {
796
+ commManager.messageReceived.disconnect(onMessage);
797
+ };
798
+ }, [commManager]);
799
+ const onSelectVariable = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((name, childKey) => {
800
+ setSelectedVar(name);
801
+ setRows([]);
802
+ setSortModel([]);
803
+ setCellSelection(null);
804
+ setHiddenColumns(new Set());
805
+ setLoading(true);
806
+ commManager.send({
807
+ type: 'get_data',
808
+ variable: name,
809
+ startRow: 0,
810
+ endRow: 1000,
811
+ childKey
812
+ });
813
+ commManager.send({
814
+ type: 'get_stats',
815
+ variable: name
816
+ });
817
+ }, [commManager]);
818
+ const onLoadMore = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((startRow) => {
819
+ if (selectedVar) {
820
+ commManager.send({
821
+ type: 'get_data',
822
+ variable: selectedVar,
823
+ startRow,
824
+ endRow: startRow + 1000,
825
+ sortModel: sortModel.length > 0 ? sortModel : undefined
826
+ });
827
+ }
828
+ }, [commManager, selectedVar, sortModel]);
829
+ const onSortChanged = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((newSortModel) => {
830
+ setSortModel(newSortModel);
831
+ setRows([]);
832
+ setLoading(true);
833
+ if (selectedVar) {
834
+ commManager.send({
835
+ type: 'get_data',
836
+ variable: selectedVar,
837
+ startRow: 0,
838
+ endRow: 1000,
839
+ sortModel: newSortModel.length > 0 ? newSortModel : undefined
840
+ });
841
+ }
842
+ }, [commManager, selectedVar]);
843
+ const onCellSelected = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((rowIndex, colName, value) => {
844
+ setCellSelection({ rowIndex, colName, value });
845
+ }, []);
846
+ const onCellEdit = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((rowIndex, column, newValue) => {
847
+ if (selectedVar) {
848
+ commManager.send({
849
+ type: 'edit_cell',
850
+ variable: selectedVar,
851
+ rowIndex,
852
+ column,
853
+ newValue
854
+ });
855
+ }
856
+ }, [commManager, selectedVar]);
857
+ const onToggleColumn = react__WEBPACK_IMPORTED_MODULE_0__.useCallback((colName) => {
858
+ setHiddenColumns(prev => {
859
+ const next = new Set(prev);
860
+ if (next.has(colName)) {
861
+ next.delete(colName);
862
+ }
863
+ else {
864
+ next.add(colName);
865
+ }
866
+ return next;
867
+ });
868
+ }, []);
869
+ const onRefresh = react__WEBPACK_IMPORTED_MODULE_0__.useCallback(() => {
870
+ commManager.refresh();
871
+ if (selectedVar) {
872
+ onSelectVariable(selectedVar);
873
+ }
874
+ }, [commManager, selectedVar, onSelectVariable]);
875
+ const selectedVarInfo = variables.find(v => v.name === selectedVar);
876
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-main-container" },
877
+ cellSelection && (react__WEBPACK_IMPORTED_MODULE_0__.createElement(_CellReferenceBar__WEBPACK_IMPORTED_MODULE_4__.CellReferenceBar, { rowIndex: cellSelection.rowIndex, colName: cellSelection.colName, value: cellSelection.value })),
878
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-content-area" },
879
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { style: { width: leftWidth, minWidth: 120, flexShrink: 0 } },
880
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement(_VariableList__WEBPACK_IMPORTED_MODULE_1__.VariableList, { variables: variables, selectedVar: selectedVar, onSelect: onSelectVariable })),
881
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement(_ResizeHandle__WEBPACK_IMPORTED_MODULE_6__.ResizeHandle, { direction: "horizontal", onResizeStart: onLeftResizeStart, onResize: onLeftResize }),
882
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-grid-container" },
883
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-grid-toolbar" },
884
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-toolbar-title" },
885
+ selectedVar || 'Select a variable',
886
+ selectedVarInfo && (react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { style: { fontWeight: 'normal', opacity: 0.6, marginLeft: 8, fontSize: '12px' } },
887
+ selectedVarInfo.typeName,
888
+ " (",
889
+ selectedVarInfo.shape.map(s => s.toLocaleString()).join(' \u00d7 '),
890
+ ")"))),
891
+ selectedVar && (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
892
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("button", { onClick: onRefresh, title: "Refresh data" }, "\u21BB Refresh"),
893
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("button", { onClick: () => setShowSidebar(!showSidebar), title: "Toggle metadata sidebar" },
894
+ showSidebar ? 'Hide' : 'Show',
895
+ " Metadata")))),
896
+ selectedVar && columns.length > 0 ? (react__WEBPACK_IMPORTED_MODULE_0__.createElement(_DataGrid__WEBPACK_IMPORTED_MODULE_2__.DataGrid, { rows: rows, columns: columns, columnStats: columnStats, totalRows: totalRows, sortModel: sortModel, hiddenColumns: hiddenColumns, loading: loading, onSortChanged: onSortChanged, onLoadMore: onLoadMore, onCellSelected: onCellSelected, onCellEdit: onCellEdit })) : (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-empty-state" }, loading ? (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-loading" },
897
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-spinner" }))) : !connected ? (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
898
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-empty-icon" }, "\uD83D\uDD0C"),
899
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", null, "Not connected to a kernel"),
900
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { style: { fontSize: '12px', marginTop: 8, opacity: 0.7 } }, "Open a notebook and run a cell to connect"))) : (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
901
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-empty-icon" }, "\uD83D\uDCCA"),
902
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", null, variables.length === 0
903
+ ? 'No variables in kernel namespace'
904
+ : 'Select a DataFrame to view its contents')))))),
905
+ showSidebar && selectedVar && columnStats.length > 0 && (react__WEBPACK_IMPORTED_MODULE_0__.createElement(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null,
906
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement(_ResizeHandle__WEBPACK_IMPORTED_MODULE_6__.ResizeHandle, { direction: "horizontal", onResizeStart: onRightResizeStart, onResize: onRightResize, invert: true }),
907
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { style: { width: rightWidth, minWidth: 150, flexShrink: 0 } },
908
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement(_MetadataSidebar__WEBPACK_IMPORTED_MODULE_3__.MetadataSidebar, { columns: columns, columnStats: columnStats, hiddenColumns: hiddenColumns, onToggleColumn: onToggleColumn, selectedVarInfo: selectedVarInfo || null }))))),
909
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement(_StatusBar__WEBPACK_IMPORTED_MODULE_5__.StatusBar, { selectedVar: selectedVar, totalRows: totalRows, totalCols: columns.length, sortModel: sortModel, connected: connected })));
910
+ };
911
+
912
+
913
+ /***/ },
914
+
915
+ /***/ "./lib/components/VariableList.js"
916
+ /*!****************************************!*\
917
+ !*** ./lib/components/VariableList.js ***!
918
+ \****************************************/
919
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
920
+
921
+ __webpack_require__.r(__webpack_exports__);
922
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
923
+ /* harmony export */ VariableList: () => (/* binding */ VariableList)
924
+ /* harmony export */ });
925
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
926
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
927
+
928
+ function formatBytes(bytes) {
929
+ if (bytes < 1024)
930
+ return `${bytes} B`;
931
+ if (bytes < 1024 * 1024)
932
+ return `${(bytes / 1024).toFixed(1)} KB`;
933
+ if (bytes < 1024 * 1024 * 1024)
934
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
935
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
936
+ }
937
+ function formatShape(shape) {
938
+ if (shape.length === 0)
939
+ return 'scalar';
940
+ if (shape.length === 1)
941
+ return `${shape[0].toLocaleString()} items`;
942
+ return shape.map(s => s.toLocaleString()).join(' \u00d7 ');
943
+ }
944
+ function getTypeBadgeClass(typeName) {
945
+ const lower = typeName.toLowerCase();
946
+ if (lower === 'dataframe')
947
+ return 've-type-dataframe';
948
+ if (lower === 'series')
949
+ return 've-type-series';
950
+ if (lower === 'ndarray')
951
+ return 've-type-ndarray';
952
+ if (lower === 'list' || lower === 'tuple')
953
+ return 've-type-list';
954
+ if (lower === 'dict')
955
+ return 've-type-dict';
956
+ return 've-type-other';
957
+ }
958
+ function getKindLabel(kind) {
959
+ switch (kind) {
960
+ case 'list_of_dicts': return 'records';
961
+ case 'list_of_lists': return 'matrix';
962
+ case 'list_of_dataframes': return 'container';
963
+ case 'dict_of_dataframes': return 'container';
964
+ case 'dict_of_lists': return 'table';
965
+ case 'dict_of_dicts': return 'nested';
966
+ case 'list_scalar': return 'values';
967
+ case 'dict_scalar': return 'record';
968
+ default: return '';
969
+ }
970
+ }
971
+ const VariableList = ({ variables, selectedVar, onSelect }) => {
972
+ const [filter, setFilter] = react__WEBPACK_IMPORTED_MODULE_0__.useState('');
973
+ const filtered = react__WEBPACK_IMPORTED_MODULE_0__.useMemo(() => {
974
+ if (!filter)
975
+ return variables;
976
+ const lower = filter.toLowerCase();
977
+ return variables.filter(v => v.name.toLowerCase().includes(lower) || v.typeName.toLowerCase().includes(lower));
978
+ }, [variables, filter]);
979
+ const sorted = react__WEBPACK_IMPORTED_MODULE_0__.useMemo(() => {
980
+ return [...filtered].sort((a, b) => {
981
+ if (a.isTabular !== b.isTabular)
982
+ return a.isTabular ? -1 : 1;
983
+ return a.name.localeCompare(b.name);
984
+ });
985
+ }, [filtered]);
986
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-variable-list" },
987
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-variable-list-header" },
988
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("input", { className: "ve-variable-search", type: "text", placeholder: "Filter objects...", value: filter, onChange: e => setFilter(e.target.value) })),
989
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-variable-items" }, sorted.length === 0 ? (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { style: { padding: '12px 8px', textAlign: 'center', opacity: 0.5, fontSize: '12px' } }, variables.length === 0 ? 'No variables' : 'No matches')) : (sorted.map(v => {
990
+ const kindLabel = getKindLabel(v.tabularKind);
991
+ return (react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { key: v.name, className: `ve-variable-item ${v.name === selectedVar ? 've-selected' : ''}`, onClick: () => v.isTabular && onSelect(v.name), style: { opacity: v.isTabular ? 1 : 0.6, cursor: v.isTabular ? 'pointer' : 'default' }, title: v.isTabular ? `Click to view ${v.name}` : `${v.name} (${v.typeName})` },
992
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' } },
993
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-var-name" }, v.name),
994
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: `ve-type-badge ${getTypeBadgeClass(v.typeName)}` }, v.typeName),
995
+ kindLabel && (react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", { className: "ve-kind-label" }, kindLabel))),
996
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("div", { className: "ve-var-meta" },
997
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", null, formatShape(v.shape)),
998
+ react__WEBPACK_IMPORTED_MODULE_0__.createElement("span", null, formatBytes(v.memoryBytes)))));
999
+ })))));
1000
+ };
1001
+
1002
+
1003
+ /***/ },
1004
+
1005
+ /***/ "./lib/index.js"
1006
+ /*!**********************!*\
1007
+ !*** ./lib/index.js ***!
1008
+ \**********************/
1009
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
1010
+
1011
+ __webpack_require__.r(__webpack_exports__);
1012
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1013
+ /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
1014
+ /* harmony export */ });
1015
+ /* harmony import */ var _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @jupyterlab/notebook */ "webpack/sharing/consume/default/@jupyterlab/notebook");
1016
+ /* harmony import */ var _jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_0__);
1017
+ /* harmony import */ var _jupyterlab_console__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @jupyterlab/console */ "webpack/sharing/consume/default/@jupyterlab/console");
1018
+ /* harmony import */ var _jupyterlab_console__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_console__WEBPACK_IMPORTED_MODULE_1__);
1019
+ /* harmony import */ var _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @jupyterlab/apputils */ "webpack/sharing/consume/default/@jupyterlab/apputils");
1020
+ /* harmony import */ var _jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_2__);
1021
+ /* harmony import */ var _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @jupyterlab/ui-components */ "webpack/sharing/consume/default/@jupyterlab/ui-components");
1022
+ /* harmony import */ var _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_3__);
1023
+ /* harmony import */ var _comm_CommManager__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./comm/CommManager */ "./lib/comm/CommManager.js");
1024
+ /* harmony import */ var _components_VariableExplorerApp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./components/VariableExplorerApp */ "./lib/components/VariableExplorerApp.js");
1025
+ /* harmony import */ var _utils_stylesheetCloner__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./utils/stylesheetCloner */ "./lib/utils/stylesheetCloner.js");
1026
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! react */ "webpack/sharing/consume/default/react");
1027
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_7__);
1028
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! react-dom */ "webpack/sharing/consume/default/react-dom");
1029
+ /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_8__);
1030
+
1031
+
1032
+
1033
+
1034
+
1035
+
1036
+
1037
+
1038
+
1039
+ const PLUGIN_ID = 'variable-explorer:plugin';
1040
+ const COMMAND_ID = 'variable-explorer:open';
1041
+ /**
1042
+ * Manages the detached Variable Explorer window and its React tree.
1043
+ */
1044
+ class VariableExplorerManager {
1045
+ constructor() {
1046
+ this._commManager = new _comm_CommManager__WEBPACK_IMPORTED_MODULE_4__.CommManager();
1047
+ this._externalWindow = null;
1048
+ this._root = null;
1049
+ this._pollTimer = null;
1050
+ }
1051
+ get commManager() {
1052
+ return this._commManager;
1053
+ }
1054
+ get isOpen() {
1055
+ return this._externalWindow !== null && !this._externalWindow.closed;
1056
+ }
1057
+ /**
1058
+ * Open the Variable Explorer in a detached window.
1059
+ */
1060
+ open() {
1061
+ if (this.isOpen) {
1062
+ this._externalWindow.focus();
1063
+ return;
1064
+ }
1065
+ const width = Math.min(1400, screen.availWidth * 0.8);
1066
+ const height = Math.min(900, screen.availHeight * 0.8);
1067
+ const left = Math.round((screen.availWidth - width) / 2);
1068
+ const top = Math.round((screen.availHeight - height) / 2);
1069
+ this._externalWindow = window.open('', 'variable-explorer', `width=${width},height=${height},left=${left},top=${top},menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=no`);
1070
+ if (!this._externalWindow) {
1071
+ console.error('Variable Explorer: Popup blocked.');
1072
+ return;
1073
+ }
1074
+ const doc = this._externalWindow.document;
1075
+ doc.write(`<!DOCTYPE html>
1076
+ <html>
1077
+ <head>
1078
+ <meta charset="utf-8">
1079
+ <title>Variable Explorer</title>
1080
+ <style>
1081
+ html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; }
1082
+ #ve-root { width: 100%; height: 100%; }
1083
+ </style>
1084
+ </head>
1085
+ <body>
1086
+ <div id="ve-root"></div>
1087
+ </body>
1088
+ </html>`);
1089
+ doc.close();
1090
+ (0,_utils_stylesheetCloner__WEBPACK_IMPORTED_MODULE_6__.cloneStylesheets)(document, doc);
1091
+ // Copy theme
1092
+ const parentBody = document.body;
1093
+ const childBody = doc.body;
1094
+ for (let i = 0; i < parentBody.attributes.length; i++) {
1095
+ const attr = parentBody.attributes[i];
1096
+ if (attr.name.startsWith('data-jp-')) {
1097
+ childBody.setAttribute(attr.name, attr.value);
1098
+ }
1099
+ }
1100
+ childBody.className = parentBody.className;
1101
+ const container = doc.getElementById('ve-root');
1102
+ react_dom__WEBPACK_IMPORTED_MODULE_8__.render(react__WEBPACK_IMPORTED_MODULE_7__.createElement(_components_VariableExplorerApp__WEBPACK_IMPORTED_MODULE_5__.VariableExplorerApp, {
1103
+ commManager: this._commManager,
1104
+ autoDetach: false
1105
+ }), container);
1106
+ this._root = container;
1107
+ this._pollTimer = window.setInterval(() => {
1108
+ if (this._externalWindow && this._externalWindow.closed) {
1109
+ this._cleanup();
1110
+ }
1111
+ }, 500);
1112
+ this._externalWindow.addEventListener('beforeunload', () => {
1113
+ this._cleanup();
1114
+ });
1115
+ }
1116
+ _cleanup() {
1117
+ if (this._pollTimer !== null) {
1118
+ window.clearInterval(this._pollTimer);
1119
+ this._pollTimer = null;
1120
+ }
1121
+ if (this._root) {
1122
+ try {
1123
+ react_dom__WEBPACK_IMPORTED_MODULE_8__.unmountComponentAtNode(this._root);
1124
+ }
1125
+ catch ( /* */_a) { /* */ }
1126
+ this._root = null;
1127
+ }
1128
+ this._externalWindow = null;
1129
+ }
1130
+ }
1131
+ const plugin = {
1132
+ id: PLUGIN_ID,
1133
+ description: 'A Spyder/Stata-grade variable explorer for JupyterLab',
1134
+ autoStart: true,
1135
+ requires: [_jupyterlab_notebook__WEBPACK_IMPORTED_MODULE_0__.INotebookTracker],
1136
+ optional: [_jupyterlab_apputils__WEBPACK_IMPORTED_MODULE_2__.ICommandPalette, _jupyterlab_console__WEBPACK_IMPORTED_MODULE_1__.IConsoleTracker],
1137
+ activate: (app, notebookTracker, palette, consoleTracker) => {
1138
+ console.log('Variable Explorer extension activated');
1139
+ const manager = new VariableExplorerManager();
1140
+ const connectToKernel = async (kernel) => {
1141
+ if (!kernel)
1142
+ return;
1143
+ try {
1144
+ await manager.commManager.connect(kernel);
1145
+ }
1146
+ catch (e) {
1147
+ console.error('Variable Explorer: Failed to connect to kernel', e);
1148
+ }
1149
+ };
1150
+ notebookTracker.currentChanged.connect(async (_, notebook) => {
1151
+ if (!notebook)
1152
+ return;
1153
+ const session = notebook.sessionContext;
1154
+ session.ready.then(() => {
1155
+ var _a;
1156
+ connectToKernel((_a = session.session) === null || _a === void 0 ? void 0 : _a.kernel);
1157
+ });
1158
+ session.kernelChanged.connect((_, args) => {
1159
+ if (args.newValue) {
1160
+ connectToKernel(args.newValue);
1161
+ }
1162
+ });
1163
+ });
1164
+ if (consoleTracker) {
1165
+ consoleTracker.currentChanged.connect(async (_, consolePanel) => {
1166
+ if (!consolePanel)
1167
+ return;
1168
+ const session = consolePanel.sessionContext;
1169
+ session.ready.then(() => {
1170
+ var _a;
1171
+ connectToKernel((_a = session.session) === null || _a === void 0 ? void 0 : _a.kernel);
1172
+ });
1173
+ session.kernelChanged.connect((_, args) => {
1174
+ if (args.newValue) {
1175
+ connectToKernel(args.newValue);
1176
+ }
1177
+ });
1178
+ });
1179
+ }
1180
+ // Register command — the schema declares this as a toolbar button + menu item
1181
+ app.commands.addCommand(COMMAND_ID, {
1182
+ label: 'Variable Explorer',
1183
+ caption: 'Open Variable Explorer in a separate window',
1184
+ icon: _jupyterlab_ui_components__WEBPACK_IMPORTED_MODULE_3__.spreadsheetIcon,
1185
+ execute: () => {
1186
+ var _a;
1187
+ manager.open();
1188
+ const currentNotebook = notebookTracker.currentWidget;
1189
+ if (currentNotebook) {
1190
+ const kernel = (_a = currentNotebook.sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel;
1191
+ connectToKernel(kernel);
1192
+ }
1193
+ }
1194
+ });
1195
+ if (palette) {
1196
+ palette.addItem({
1197
+ command: COMMAND_ID,
1198
+ category: 'Variable Explorer'
1199
+ });
1200
+ }
1201
+ }
1202
+ };
1203
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (plugin);
1204
+
1205
+
1206
+ /***/ },
1207
+
1208
+ /***/ "./lib/utils/colorScales.js"
1209
+ /*!**********************************!*\
1210
+ !*** ./lib/utils/colorScales.js ***!
1211
+ \**********************************/
1212
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
1213
+
1214
+ __webpack_require__.r(__webpack_exports__);
1215
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1216
+ /* harmony export */ booleanColor: () => (/* binding */ booleanColor),
1217
+ /* harmony export */ numericHeatmap: () => (/* binding */ numericHeatmap)
1218
+ /* harmony export */ });
1219
+ /** Color scale utilities for conditional formatting */
1220
+ function numericHeatmap(value, min, max) {
1221
+ if (value == null || isNaN(value))
1222
+ return 'transparent';
1223
+ const range = max - min;
1224
+ if (range === 0)
1225
+ return 'rgba(100, 149, 237, 0.15)';
1226
+ const t = Math.max(0, Math.min(1, (value - min) / range));
1227
+ // Blue (cold) → transparent (mid) → Red (hot)
1228
+ if (t < 0.5) {
1229
+ const s = 1 - t * 2; // 1..0
1230
+ return `rgba(66, 133, 244, ${(s * 0.25).toFixed(3)})`;
1231
+ }
1232
+ const s = (t - 0.5) * 2; // 0..1
1233
+ return `rgba(234, 67, 53, ${(s * 0.25).toFixed(3)})`;
1234
+ }
1235
+ function booleanColor(value) {
1236
+ if (value === true || value === 1) {
1237
+ return 'rgba(52, 168, 83, 0.7)'; // Green
1238
+ }
1239
+ if (value === false || value === 0) {
1240
+ return 'rgba(66, 133, 244, 0.7)'; // Blue
1241
+ }
1242
+ return 'rgba(128, 128, 128, 0.3)'; // Gray for null
1243
+ }
1244
+
1245
+
1246
+ /***/ },
1247
+
1248
+ /***/ "./lib/utils/histogramRenderer.js"
1249
+ /*!****************************************!*\
1250
+ !*** ./lib/utils/histogramRenderer.js ***!
1251
+ \****************************************/
1252
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
1253
+
1254
+ __webpack_require__.r(__webpack_exports__);
1255
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1256
+ /* harmony export */ drawHistogram: () => (/* binding */ drawHistogram)
1257
+ /* harmony export */ });
1258
+ /** Canvas-based mini histogram rendering for column headers */
1259
+ function drawHistogram(canvas, data) {
1260
+ const ctx = canvas.getContext('2d');
1261
+ if (!ctx)
1262
+ return;
1263
+ const dpr = window.devicePixelRatio || 1;
1264
+ const w = canvas.clientWidth;
1265
+ const h = canvas.clientHeight;
1266
+ canvas.width = w * dpr;
1267
+ canvas.height = h * dpr;
1268
+ ctx.scale(dpr, dpr);
1269
+ ctx.clearRect(0, 0, w, h);
1270
+ switch (data.type) {
1271
+ case 'numeric':
1272
+ drawNumericHistogram(ctx, w, h, data);
1273
+ break;
1274
+ case 'categorical':
1275
+ drawCategoricalHistogram(ctx, w, h, data);
1276
+ break;
1277
+ case 'boolean':
1278
+ drawBooleanBar(ctx, w, h, data);
1279
+ break;
1280
+ }
1281
+ }
1282
+ function drawNumericHistogram(ctx, w, h, data) {
1283
+ const { counts } = data;
1284
+ if (counts.length === 0)
1285
+ return;
1286
+ const maxCount = Math.max(...counts);
1287
+ if (maxCount === 0)
1288
+ return;
1289
+ const barWidth = w / counts.length;
1290
+ const padding = 1;
1291
+ // Get computed style for theming
1292
+ const style = getComputedStyle(document.documentElement);
1293
+ const barColor = style.getPropertyValue('--jp-brand-color1').trim() || '#1976d2';
1294
+ for (let i = 0; i < counts.length; i++) {
1295
+ const barHeight = (counts[i] / maxCount) * (h - 2);
1296
+ const x = i * barWidth + padding / 2;
1297
+ const y = h - barHeight - 1;
1298
+ ctx.fillStyle = barColor;
1299
+ ctx.globalAlpha = 0.6;
1300
+ ctx.fillRect(x, y, barWidth - padding, barHeight);
1301
+ }
1302
+ ctx.globalAlpha = 1.0;
1303
+ }
1304
+ function drawCategoricalHistogram(ctx, w, h, data) {
1305
+ const { counts } = data;
1306
+ if (counts.length === 0)
1307
+ return;
1308
+ const maxCount = Math.max(...counts);
1309
+ if (maxCount === 0)
1310
+ return;
1311
+ const barHeight = h / Math.min(counts.length, 8);
1312
+ const padding = 1;
1313
+ const colors = [
1314
+ '#1976d2', '#388e3c', '#f57c00', '#7b1fa2',
1315
+ '#c62828', '#00838f', '#4e342e', '#546e7a'
1316
+ ];
1317
+ for (let i = 0; i < Math.min(counts.length, 8); i++) {
1318
+ const barWidth = (counts[i] / maxCount) * (w - 2);
1319
+ const y = i * barHeight + padding / 2;
1320
+ ctx.fillStyle = colors[i % colors.length];
1321
+ ctx.globalAlpha = 0.6;
1322
+ ctx.fillRect(1, y, barWidth, barHeight - padding);
1323
+ }
1324
+ ctx.globalAlpha = 1.0;
1325
+ }
1326
+ function drawBooleanBar(ctx, w, h, data) {
1327
+ const total = data.trueCount + data.falseCount + data.nullCount;
1328
+ if (total === 0)
1329
+ return;
1330
+ const trueWidth = (data.trueCount / total) * w;
1331
+ const falseWidth = (data.falseCount / total) * w;
1332
+ const barY = h * 0.2;
1333
+ const barH = h * 0.6;
1334
+ // True (green)
1335
+ ctx.fillStyle = 'rgba(52, 168, 83, 0.7)';
1336
+ ctx.fillRect(0, barY, trueWidth, barH);
1337
+ // False (blue)
1338
+ ctx.fillStyle = 'rgba(66, 133, 244, 0.7)';
1339
+ ctx.fillRect(trueWidth, barY, falseWidth, barH);
1340
+ // Null (gray)
1341
+ if (data.nullCount > 0) {
1342
+ ctx.fillStyle = 'rgba(128, 128, 128, 0.4)';
1343
+ ctx.fillRect(trueWidth + falseWidth, barY, w - trueWidth - falseWidth, barH);
1344
+ }
1345
+ }
1346
+
1347
+
1348
+ /***/ },
1349
+
1350
+ /***/ "./lib/utils/stylesheetCloner.js"
1351
+ /*!***************************************!*\
1352
+ !*** ./lib/utils/stylesheetCloner.js ***!
1353
+ \***************************************/
1354
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
1355
+
1356
+ __webpack_require__.r(__webpack_exports__);
1357
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1358
+ /* harmony export */ cloneStylesheets: () => (/* binding */ cloneStylesheets)
1359
+ /* harmony export */ });
1360
+ /** Clone stylesheets from the main document to a detached window */
1361
+ function cloneStylesheets(source, target) {
1362
+ var _a;
1363
+ // Copy <link> stylesheets
1364
+ const links = source.querySelectorAll('link[rel="stylesheet"]');
1365
+ links.forEach(link => {
1366
+ const clone = target.createElement('link');
1367
+ clone.rel = 'stylesheet';
1368
+ clone.href = link.href;
1369
+ target.head.appendChild(clone);
1370
+ });
1371
+ // Copy <style> elements
1372
+ const styles = source.querySelectorAll('style');
1373
+ styles.forEach(style => {
1374
+ const clone = target.createElement('style');
1375
+ clone.textContent = style.textContent;
1376
+ target.head.appendChild(clone);
1377
+ });
1378
+ // Copy inline style sheets that might be injected by webpack
1379
+ for (let i = 0; i < source.styleSheets.length; i++) {
1380
+ const sheet = source.styleSheets[i];
1381
+ if (!sheet.href && ((_a = sheet.ownerNode) === null || _a === void 0 ? void 0 : _a.nodeName) === 'STYLE') {
1382
+ // Already copied above via querySelectorAll('style')
1383
+ continue;
1384
+ }
1385
+ try {
1386
+ // Try to access cssRules (may fail for cross-origin sheets)
1387
+ if (sheet.cssRules) {
1388
+ let cssText = '';
1389
+ for (let j = 0; j < sheet.cssRules.length; j++) {
1390
+ cssText += sheet.cssRules[j].cssText + '\n';
1391
+ }
1392
+ if (cssText && !sheet.href) {
1393
+ const styleEl = target.createElement('style');
1394
+ styleEl.textContent = cssText;
1395
+ target.head.appendChild(styleEl);
1396
+ }
1397
+ }
1398
+ }
1399
+ catch (_b) {
1400
+ // Cross-origin stylesheet — skip
1401
+ }
1402
+ }
1403
+ // Copy body data attributes for theme detection
1404
+ const bodyAttrs = source.body.attributes;
1405
+ for (let i = 0; i < bodyAttrs.length; i++) {
1406
+ const attr = bodyAttrs[i];
1407
+ if (attr.name.startsWith('data-jp-')) {
1408
+ target.body.setAttribute(attr.name, attr.value);
1409
+ }
1410
+ }
1411
+ }
1412
+
1413
+
1414
+ /***/ }
1415
+
1416
+ }]);
1417
+ //# sourceMappingURL=lib_index_js.88a2cd3be0f2bf49f0eb.js.map