jupyterlab_tabular_data_viewer_extension 1.1.20

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.
package/package.json ADDED
@@ -0,0 +1,217 @@
1
+ {
2
+ "name": "jupyterlab_tabular_data_viewer_extension",
3
+ "version": "1.1.20",
4
+ "description": "Jupyterlab extension to browse tabular data files (Parquet, Excel, CSV, TSV) with filtering and sorting capabilities",
5
+ "keywords": [
6
+ "jupyter",
7
+ "jupyterlab",
8
+ "jupyterlab-extension",
9
+ "parquet",
10
+ "excel",
11
+ "csv",
12
+ "tsv",
13
+ "tabular-data"
14
+ ],
15
+ "homepage": "https://github.com/stellarshenson/jupyterlab_tabular_data_viewer_extension",
16
+ "bugs": {
17
+ "url": "https://github.com/stellarshenson/jupyterlab_tabular_data_viewer_extension/issues"
18
+ },
19
+ "license": "BSD-3-Clause",
20
+ "author": {
21
+ "name": "Stellars Henson",
22
+ "email": "konrad.jelen@gmail.com"
23
+ },
24
+ "files": [
25
+ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
26
+ "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
27
+ "schema/*.json",
28
+ "src/**/*.{ts,tsx}"
29
+ ],
30
+ "main": "lib/index.js",
31
+ "types": "lib/index.d.ts",
32
+ "style": "style/index.css",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/stellarshenson/jupyterlab_tabular_data_viewer_extension.git"
36
+ },
37
+ "scripts": {
38
+ "build": "jlpm build:lib && jlpm build:labextension:dev",
39
+ "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
40
+ "build:labextension": "jupyter labextension build .",
41
+ "build:labextension:dev": "jupyter labextension build --development True .",
42
+ "build:lib": "tsc --sourceMap",
43
+ "build:lib:prod": "tsc",
44
+ "clean": "jlpm clean:lib",
45
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
46
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
47
+ "clean:labextension": "rimraf jupyterlab_tabular_data_viewer_extension/labextension jupyterlab_tabular_data_viewer_extension/_version.py",
48
+ "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
49
+ "eslint": "jlpm eslint:check --fix",
50
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
51
+ "install:extension": "jlpm build",
52
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
53
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
54
+ "prettier": "jlpm prettier:base --write --list-different",
55
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
56
+ "prettier:check": "jlpm prettier:base --check",
57
+ "stylelint": "jlpm stylelint:check --fix",
58
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
59
+ "test": "jest --coverage",
60
+ "watch": "run-p watch:src watch:labextension",
61
+ "watch:src": "tsc -w --sourceMap",
62
+ "watch:labextension": "jupyter labextension watch ."
63
+ },
64
+ "dependencies": {
65
+ "@jupyterlab/application": "^4.0.0",
66
+ "@jupyterlab/apputils": "^4.0.0",
67
+ "@jupyterlab/coreutils": "^6.0.0",
68
+ "@jupyterlab/docregistry": "^4.0.0",
69
+ "@jupyterlab/services": "^7.0.0",
70
+ "@lumino/widgets": "^2.0.0",
71
+ "rimraf": "^6.1.0"
72
+ },
73
+ "devDependencies": {
74
+ "@jupyterlab/builder": "^4.0.0",
75
+ "@jupyterlab/testutils": "^4.0.0",
76
+ "@types/jest": "^29.2.0",
77
+ "@types/json-schema": "^7.0.11",
78
+ "@types/react": "^18.0.26",
79
+ "@types/react-addons-linked-state-mixin": "^0.14.22",
80
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
81
+ "@typescript-eslint/parser": "^6.1.0",
82
+ "css-loader": "^6.7.1",
83
+ "eslint": "^8.36.0",
84
+ "eslint-config-prettier": "^8.8.0",
85
+ "eslint-plugin-prettier": "^5.0.0",
86
+ "jest": "^29.2.0",
87
+ "mkdirp": "^1.0.3",
88
+ "npm-run-all2": "^7.0.1",
89
+ "prettier": "^3.0.0",
90
+ "source-map-loader": "^1.0.2",
91
+ "style-loader": "^3.3.1",
92
+ "stylelint": "^15.10.1",
93
+ "stylelint-config-recommended": "^13.0.0",
94
+ "stylelint-config-standard": "^34.0.0",
95
+ "stylelint-csstree-validator": "^3.0.0",
96
+ "stylelint-prettier": "^4.0.0",
97
+ "typescript": "~5.8.0",
98
+ "yjs": "^13.5.0"
99
+ },
100
+ "sideEffects": [
101
+ "style/*.css",
102
+ "style/index.js"
103
+ ],
104
+ "styleModule": "style/index.js",
105
+ "publishConfig": {
106
+ "access": "public"
107
+ },
108
+ "jupyterlab": {
109
+ "discovery": {
110
+ "server": {
111
+ "managers": [
112
+ "pip"
113
+ ],
114
+ "base": {
115
+ "name": "jupyterlab_tabular_data_viewer_extension"
116
+ }
117
+ }
118
+ },
119
+ "extension": true,
120
+ "outputDir": "jupyterlab_tabular_data_viewer_extension/labextension",
121
+ "schemaDir": "schema"
122
+ },
123
+ "eslintIgnore": [
124
+ "node_modules",
125
+ "dist",
126
+ "coverage",
127
+ "**/*.d.ts",
128
+ "tests",
129
+ "**/__tests__",
130
+ "ui-tests"
131
+ ],
132
+ "eslintConfig": {
133
+ "extends": [
134
+ "eslint:recommended",
135
+ "plugin:@typescript-eslint/eslint-recommended",
136
+ "plugin:@typescript-eslint/recommended",
137
+ "plugin:prettier/recommended"
138
+ ],
139
+ "parser": "@typescript-eslint/parser",
140
+ "parserOptions": {
141
+ "project": "tsconfig.json",
142
+ "sourceType": "module"
143
+ },
144
+ "plugins": [
145
+ "@typescript-eslint"
146
+ ],
147
+ "rules": {
148
+ "@typescript-eslint/naming-convention": [
149
+ "error",
150
+ {
151
+ "selector": "interface",
152
+ "format": [
153
+ "PascalCase"
154
+ ],
155
+ "custom": {
156
+ "regex": "^I[A-Z]",
157
+ "match": true
158
+ }
159
+ }
160
+ ],
161
+ "@typescript-eslint/no-unused-vars": [
162
+ "warn",
163
+ {
164
+ "args": "none"
165
+ }
166
+ ],
167
+ "@typescript-eslint/no-explicit-any": "off",
168
+ "@typescript-eslint/no-namespace": "off",
169
+ "@typescript-eslint/no-use-before-define": "off",
170
+ "@typescript-eslint/quotes": [
171
+ "error",
172
+ "single",
173
+ {
174
+ "avoidEscape": true,
175
+ "allowTemplateLiterals": false
176
+ }
177
+ ],
178
+ "curly": [
179
+ "error",
180
+ "all"
181
+ ],
182
+ "eqeqeq": "error",
183
+ "prefer-arrow-callback": "error"
184
+ }
185
+ },
186
+ "prettier": {
187
+ "singleQuote": true,
188
+ "trailingComma": "none",
189
+ "arrowParens": "avoid",
190
+ "endOfLine": "auto",
191
+ "overrides": [
192
+ {
193
+ "files": "package.json",
194
+ "options": {
195
+ "tabWidth": 4
196
+ }
197
+ }
198
+ ]
199
+ },
200
+ "stylelint": {
201
+ "extends": [
202
+ "stylelint-config-recommended",
203
+ "stylelint-config-standard",
204
+ "stylelint-prettier/recommended"
205
+ ],
206
+ "plugins": [
207
+ "stylelint-csstree-validator"
208
+ ],
209
+ "rules": {
210
+ "csstree/validator": true,
211
+ "property-no-vendor-prefix": null,
212
+ "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
213
+ "selector-no-vendor-prefix": null,
214
+ "value-no-vendor-prefix": null
215
+ }
216
+ }
217
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "title": "Tabular Data Viewer Extension",
3
+ "description": "Settings for the Tabular Data Viewer Extension. Supports Parquet, Excel, CSV, and TSV files.",
4
+ "type": "object",
5
+ "properties": {
6
+ "enableParquet": {
7
+ "title": "Enable Parquet files",
8
+ "description": "Use this extension to view .parquet files",
9
+ "type": "boolean",
10
+ "default": true
11
+ },
12
+ "enableExcel": {
13
+ "title": "Enable Excel files",
14
+ "description": "Use this extension to view .xlsx files",
15
+ "type": "boolean",
16
+ "default": true
17
+ },
18
+ "enableCSV": {
19
+ "title": "Enable CSV files",
20
+ "description": "Use this extension to view .csv files",
21
+ "type": "boolean",
22
+ "default": true
23
+ },
24
+ "enableTSV": {
25
+ "title": "Enable TSV files",
26
+ "description": "Use this extension to view .tsv files",
27
+ "type": "boolean",
28
+ "default": true
29
+ }
30
+ },
31
+ "additionalProperties": false
32
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Example of [Jest](https://jestjs.io/docs/getting-started) unit tests
3
+ */
4
+
5
+ describe('jupyterlab_tabular_data_viewer_extension', () => {
6
+ it('should be tested', () => {
7
+ expect(1 + 1).toEqual(2);
8
+ });
9
+ });
@@ -0,0 +1,11 @@
1
+ import { DocumentWidget } from '@jupyterlab/docregistry';
2
+ import { TabularDataViewer } from './widget';
3
+
4
+ /**
5
+ * A document widget for tabular data files (Parquet, Excel)
6
+ */
7
+ export class TabularDataDocument extends DocumentWidget<TabularDataViewer> {
8
+ constructor(options: DocumentWidget.IOptions<TabularDataViewer>) {
9
+ super(options);
10
+ }
11
+ }
package/src/index.ts ADDED
@@ -0,0 +1,276 @@
1
+ import {
2
+ JupyterFrontEnd,
3
+ JupyterFrontEndPlugin
4
+ } from '@jupyterlab/application';
5
+
6
+ import {
7
+ IDocumentWidget,
8
+ DocumentRegistry,
9
+ ABCWidgetFactory
10
+ } from '@jupyterlab/docregistry';
11
+
12
+ import { ISettingRegistry } from '@jupyterlab/settingregistry';
13
+
14
+ import { TabularDataViewer } from './widget';
15
+ import { TabularDataDocument } from './document';
16
+
17
+ /**
18
+ * A widget factory for Parquet files
19
+ */
20
+ class TabularDataWidgetFactory extends ABCWidgetFactory<
21
+ IDocumentWidget<TabularDataViewer>
22
+ > {
23
+ private _setLastContextMenuRow: (row: any) => void;
24
+ private _setActiveWidget: (widget: TabularDataViewer) => void;
25
+
26
+ constructor(
27
+ options: DocumentRegistry.IWidgetFactoryOptions,
28
+ setLastContextMenuRow: (row: any) => void,
29
+ setActiveWidget: (widget: TabularDataViewer) => void
30
+ ) {
31
+ super(options);
32
+ this._setLastContextMenuRow = setLastContextMenuRow;
33
+ this._setActiveWidget = setActiveWidget;
34
+ }
35
+
36
+ /**
37
+ * Create a new widget given a context
38
+ */
39
+ protected createNewWidget(
40
+ context: DocumentRegistry.Context
41
+ ): IDocumentWidget<TabularDataViewer> {
42
+ // console.log(`[Tabular Data Viewer] Creating widget for file: ${context.path}`);
43
+ // console.log(`[Tabular Data Viewer] File type: ${context.contentsModel?.type}, Format: ${context.contentsModel?.format}`);
44
+
45
+ const content = new TabularDataViewer(context.path, this._setLastContextMenuRow);
46
+ const widget = new TabularDataDocument({ content, context });
47
+ widget.title.label = context.path.split('/').pop() || 'Tabular Data File';
48
+
49
+ // Track this as the active widget when context menu is used
50
+ this._setActiveWidget(content);
51
+
52
+ return widget;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Settings interface
58
+ */
59
+ interface ISettings {
60
+ enableParquet: boolean;
61
+ enableExcel: boolean;
62
+ enableCSV: boolean;
63
+ enableTSV: boolean;
64
+ }
65
+
66
+ /**
67
+ * Initialization data for the jupyterlab_tabular_data_viewer_extension extension.
68
+ */
69
+ const plugin: JupyterFrontEndPlugin<void> = {
70
+ id: 'jupyterlab_tabular_data_viewer_extension:plugin',
71
+ description:
72
+ 'Jupyterlab extension to allow simple browsing of tabular data files (Parquet, Excel) with filtering and sorting capabilities',
73
+ autoStart: true,
74
+ requires: [ISettingRegistry],
75
+ activate: async (app: JupyterFrontEnd, settingRegistry: ISettingRegistry) => {
76
+ // console.log(
77
+ // 'JupyterLab extension jupyterlab_tabular_data_viewer_extension is activated!'
78
+ // );
79
+
80
+ const { docRegistry, commands, contextMenu } = app;
81
+
82
+ // Track last right-clicked row for context menu
83
+ let lastContextMenuRow: any = null;
84
+ let activeWidget: TabularDataViewer | null = null;
85
+
86
+ // Load settings
87
+ let settings: ISettings = {
88
+ enableParquet: true,
89
+ enableExcel: true,
90
+ enableCSV: true,
91
+ enableTSV: true
92
+ };
93
+
94
+ // console.log('[Tabular Data Viewer] Default settings:', settings);
95
+
96
+ try {
97
+ // console.log('[Tabular Data Viewer] Loading settings from registry with id:', plugin.id);
98
+ const pluginSettings = await settingRegistry.load(plugin.id);
99
+ settings = pluginSettings.composite as unknown as ISettings;
100
+ // console.log('[Tabular Data Viewer] Loaded settings:', settings);
101
+ // console.log('[Tabular Data Viewer] Settings detail - enableParquet:', settings.enableParquet, 'enableExcel:', settings.enableExcel);
102
+
103
+ // Watch for settings changes
104
+ pluginSettings.changed.connect(() => {
105
+ settings = pluginSettings.composite as unknown as ISettings;
106
+ // console.log('[Tabular Data Viewer] Settings changed:', settings);
107
+ });
108
+ } catch (error) {
109
+ console.error('[Tabular Data Viewer] Failed to load settings:', error);
110
+ // console.log('[Tabular Data Viewer] Using default settings:', settings);
111
+ }
112
+
113
+ // Command to copy row as JSON
114
+ const copyRowCommand = 'tabular-data-viewer:copy-row-json';
115
+ commands.addCommand(copyRowCommand, {
116
+ label: 'Copy Row as JSON',
117
+ caption: 'Copy the row data as JSON to clipboard',
118
+ isEnabled: () => {
119
+ return lastContextMenuRow !== null;
120
+ },
121
+ execute: async () => {
122
+ if (lastContextMenuRow) {
123
+ const jsonString = JSON.stringify(lastContextMenuRow, null, 2);
124
+ await navigator.clipboard.writeText(jsonString);
125
+ // console.log('Row copied to clipboard as JSON');
126
+
127
+ // Clean up highlight after copy
128
+ if (activeWidget) {
129
+ activeWidget.getCleanupHighlight()();
130
+ }
131
+ }
132
+ }
133
+ });
134
+
135
+ // Add to context menu for tabular data viewer rows
136
+ contextMenu.addItem({
137
+ command: copyRowCommand,
138
+ selector: '.jp-TabularDataViewer-row',
139
+ rank: 10
140
+ });
141
+
142
+ // Register file types based on settings
143
+ // console.log('[Tabular Data Viewer] Starting file type registration...');
144
+ // console.log('[Tabular Data Viewer] Current settings state:', settings);
145
+ const binaryFileTypes: string[] = [];
146
+ const textFileTypes: string[] = [];
147
+
148
+ // Register Parquet file type if enabled
149
+ if (settings.enableParquet) {
150
+ try {
151
+ docRegistry.addFileType({
152
+ name: 'parquet',
153
+ displayName: 'Parquet',
154
+ extensions: ['.parquet'],
155
+ mimeTypes: ['application/x-parquet'],
156
+ iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon',
157
+ contentType: 'file',
158
+ fileFormat: 'base64'
159
+ });
160
+ binaryFileTypes.push('parquet');
161
+ // console.log('[Tabular Data Viewer] Parquet file type registered');
162
+ } catch (e) {
163
+ console.warn('[Tabular Data Viewer] Parquet file type already registered', e);
164
+ }
165
+ }
166
+
167
+ // Register Excel file type if enabled
168
+ if (settings.enableExcel) {
169
+ try {
170
+ docRegistry.addFileType({
171
+ name: 'xlsx-parquet-viewer',
172
+ displayName: 'Excel (Parquet Viewer)',
173
+ extensions: ['.xlsx'],
174
+ mimeTypes: ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
175
+ iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon',
176
+ contentType: 'file',
177
+ fileFormat: 'base64'
178
+ });
179
+ binaryFileTypes.push('xlsx-parquet-viewer');
180
+ // console.log('[Tabular Data Viewer] Excel file type registered');
181
+ } catch (e) {
182
+ console.warn('[Tabular Data Viewer] Excel file type already registered', e);
183
+ }
184
+ }
185
+
186
+ // Register CSV file type if enabled
187
+ if (settings.enableCSV) {
188
+ try {
189
+ docRegistry.addFileType({
190
+ name: 'csv-tabular-viewer',
191
+ displayName: 'CSV (Tabular Viewer)',
192
+ extensions: ['.csv'],
193
+ mimeTypes: ['text/csv'],
194
+ iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon',
195
+ contentType: 'file',
196
+ fileFormat: 'text'
197
+ });
198
+ textFileTypes.push('csv-tabular-viewer');
199
+ // console.log('[Tabular Data Viewer] CSV file type registered');
200
+ } catch (e) {
201
+ console.warn('[Tabular Data Viewer] CSV file type already registered', e);
202
+ }
203
+ }
204
+
205
+ // Register TSV file type if enabled
206
+ if (settings.enableTSV) {
207
+ try {
208
+ docRegistry.addFileType({
209
+ name: 'tsv-tabular-viewer',
210
+ displayName: 'TSV (Tabular Viewer)',
211
+ extensions: ['.tsv'],
212
+ mimeTypes: ['text/tab-separated-values'],
213
+ iconClass: 'jp-MaterialIcon jp-SpreadsheetIcon',
214
+ contentType: 'file',
215
+ fileFormat: 'text'
216
+ });
217
+ textFileTypes.push('tsv-tabular-viewer');
218
+ // console.log('[Tabular Data Viewer] TSV file type registered');
219
+ } catch (e) {
220
+ console.warn('[Tabular Data Viewer] TSV file type already registered', e);
221
+ }
222
+ }
223
+
224
+ // Create binary factory for Parquet and Excel files
225
+ if (binaryFileTypes.length > 0) {
226
+ const binaryFactory = new TabularDataWidgetFactory(
227
+ {
228
+ name: 'Tabular Data Viewer (Binary)',
229
+ modelName: 'base64',
230
+ fileTypes: binaryFileTypes,
231
+ defaultFor: binaryFileTypes,
232
+ defaultRendered: binaryFileTypes,
233
+ readOnly: true
234
+ },
235
+ (row: any) => {
236
+ lastContextMenuRow = row;
237
+ },
238
+ (widget: TabularDataViewer) => {
239
+ activeWidget = widget;
240
+ }
241
+ );
242
+
243
+ docRegistry.addWidgetFactory(binaryFactory);
244
+ // console.log(`[Tabular Data Viewer] Binary factory registered for: ${binaryFileTypes.join(', ')}`);
245
+ }
246
+
247
+ // Create text factory for CSV and TSV files
248
+ if (textFileTypes.length > 0) {
249
+ const textFactory = new TabularDataWidgetFactory(
250
+ {
251
+ name: 'Tabular Data Viewer (Text)',
252
+ modelName: 'text',
253
+ fileTypes: textFileTypes,
254
+ defaultFor: textFileTypes,
255
+ defaultRendered: textFileTypes,
256
+ readOnly: true
257
+ },
258
+ (row: any) => {
259
+ lastContextMenuRow = row;
260
+ },
261
+ (widget: TabularDataViewer) => {
262
+ activeWidget = widget;
263
+ }
264
+ );
265
+
266
+ docRegistry.addWidgetFactory(textFactory);
267
+ // console.log(`[Tabular Data Viewer] Text factory registered for: ${textFileTypes.join(', ')}`);
268
+ }
269
+
270
+ if (binaryFileTypes.length === 0 && textFileTypes.length === 0) {
271
+ console.warn('[Tabular Data Viewer] No file types enabled in settings');
272
+ }
273
+ }
274
+ };
275
+
276
+ export default plugin;
package/src/request.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { URLExt } from '@jupyterlab/coreutils';
2
+
3
+ import { ServerConnection } from '@jupyterlab/services';
4
+
5
+ /**
6
+ * Call the server extension
7
+ *
8
+ * @param endPoint API REST end point for the extension
9
+ * @param init Initial values for the request
10
+ * @returns The response body interpreted as JSON
11
+ */
12
+ export async function requestAPI<T>(
13
+ endPoint = '',
14
+ init: RequestInit = {}
15
+ ): Promise<T> {
16
+ // Make request to Jupyter API
17
+ const settings = ServerConnection.makeSettings();
18
+ const requestUrl = URLExt.join(
19
+ settings.baseUrl,
20
+ 'jupyterlab-tabular-data-viewer-extension', // our server extension's API namespace
21
+ endPoint
22
+ );
23
+
24
+ let response: Response;
25
+ try {
26
+ response = await ServerConnection.makeRequest(requestUrl, init, settings);
27
+ } catch (error) {
28
+ throw new ServerConnection.NetworkError(error as any);
29
+ }
30
+
31
+ let data: any = await response.text();
32
+
33
+ if (data.length > 0) {
34
+ try {
35
+ data = JSON.parse(data);
36
+ } catch (error) {
37
+ console.log('Not a JSON response body.', response);
38
+ }
39
+ }
40
+
41
+ if (!response.ok) {
42
+ throw new ServerConnection.ResponseError(response, data.message || data);
43
+ }
44
+
45
+ return data;
46
+ }