gennaker-tools 0.0.1 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Angus
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -1,45 +1,119 @@
1
1
  # gennaker-tools
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ [![Github Actions Status](https://github.com/agoose77/gennaker-tools/workflows/Build/badge.svg)](https://github.com/agoose77/gennaker-tools/actions/workflows/build.yml)
4
4
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
5
+ A series of JupyterLab and Jupyter Server extensions to power the gennaker project.
6
6
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
7
+ ## Features
8
8
 
9
- ## Purpose
9
+ ### TOML ←→ JSON Settings Sync
10
10
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `gennaker-tools`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
11
+ <img width="1130" height="900" alt="image" src="https://github.com/user-attachments/assets/c9babdd6-d247-45a1-a590-f17165f5b7fb" />
15
12
 
16
- ## What is OIDC Trusted Publishing?
13
+ _Synchronise between JSON and TOML representations of settings under the JupyterLab settings path._
17
14
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
15
+ ## Code Snippets
19
16
 
20
- ## Setup Instructions
17
+ <img width="1350" height="870" alt="image" src="https://github.com/user-attachments/assets/4e2673a9-3e27-4535-a3b0-b86e27f12f49" />
21
18
 
22
- To properly configure OIDC trusted publishing for this package:
19
+ _Use CodeMirror snippets to autocomplete text and modular units of content._ See https://codemirror.net/docs/ref/#autocomplete.autocompletion for more information.
23
20
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
21
+ ## Reset Command
28
22
 
29
- ## DO NOT USE THIS PACKAGE
23
+ ```
24
+ gennaker-tools:reset-jupyterlab
25
+ ```
30
26
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
27
+ <img width="887" height="423" alt="image" src="https://github.com/user-attachments/assets/2ef028a5-0eca-45ab-81e1-f22627c427c1" />
36
28
 
37
- ## More Information
29
+ _Reset the current session by reloading the page_.
38
30
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
31
+ ## Stateless Execution Command
42
32
 
43
- ---
33
+ ```
34
+ gennaker-tools:restart-run-stateless
35
+ ```
44
36
 
45
- **Maintained for OIDC setup purposes only**
37
+ <img width="627" height="318" alt="image" src="https://github.com/user-attachments/assets/079684f8-e7bc-484f-b466-533f2dc1665e" />
38
+
39
+ _Perform some compute_
40
+
41
+ <img width="618" height="140" alt="image" src="https://github.com/user-attachments/assets/0029730a-e9ea-4791-8f61-a4ed0da783f8" />
42
+
43
+ _Modify the earlier state_
44
+
45
+ <img width="626" height="362" alt="image" src="https://github.com/user-attachments/assets/18ad2055-105e-461e-af36-5be4b4c7c51e" />
46
+
47
+ _Re-run up to a particular cell, and clear outputs_
48
+
49
+ ## Requirements
50
+
51
+ - JupyterLab >= 4.0.0
52
+
53
+ ## Install
54
+
55
+ To install the extension, execute:
56
+
57
+ ```bash
58
+ pip install gennaker-tools
59
+ ```
60
+
61
+ ## Uninstall
62
+
63
+ To remove the extension, execute:
64
+
65
+ ```bash
66
+ pip uninstall gennaker-tools
67
+ ```
68
+
69
+ ## Contributing
70
+
71
+ ### Development install
72
+
73
+ Note: You will need NodeJS to build the extension package.
74
+
75
+ The `jlpm` command is JupyterLab's pinned version of
76
+ [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use
77
+ `yarn` or `npm` in lieu of `jlpm` below.
78
+
79
+ ```bash
80
+ # Clone the repo to your local environment
81
+ # Change directory to the gennaker_tools directory
82
+ # Install package in development mode
83
+ pip install -e "."
84
+ # Link your development version of the extension with JupyterLab
85
+ jupyter labextension develop . --overwrite
86
+ # Rebuild extension Typescript source after making changes
87
+ jlpm build
88
+ ```
89
+
90
+ You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.
91
+
92
+ ```bash
93
+ # Watch the source directory in one terminal, automatically rebuilding when needed
94
+ jlpm watch
95
+ # Run JupyterLab in another terminal
96
+ jupyter lab
97
+ ```
98
+
99
+ With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).
100
+
101
+ By default, the `jlpm build` command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command:
102
+
103
+ ```bash
104
+ jupyter lab build --minimize=False
105
+ ```
106
+
107
+ ### Development uninstall
108
+
109
+ ```bash
110
+ pip uninstall gennaker-tools
111
+ ```
112
+
113
+ In development mode, you will also need to remove the symlink created by `jupyter labextension develop`
114
+ command. To find its location, you can run `jupyter labextension list` to figure out where the `labextensions`
115
+ folder is located. Then you can remove the symlink named `gennaker-tools` within that folder.
116
+
117
+ ### Packaging the extension
118
+
119
+ See [RELEASE](RELEASE.md)
package/lib/index.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ /**
3
+ * Initialization data for the gennaker-tools extension.
4
+ */
5
+ export declare const statelessRunPlugin: JupyterFrontEndPlugin<void>;
6
+ /**
7
+ * Initialization data for the gennaker-tools extension.
8
+ */
9
+ export declare const reloadPlugin: JupyterFrontEndPlugin<void>;
10
+ export declare const snippetsPlugin: JupyterFrontEndPlugin<void>;
11
+ declare const _default: JupyterFrontEndPlugin<void>[];
12
+ export default _default;
package/lib/index.js ADDED
@@ -0,0 +1,147 @@
1
+ import { INotebookTracker } from '@jupyterlab/notebook';
2
+ import { ICommandPalette } from '@jupyterlab/apputils';
3
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
4
+ import { IEditorExtensionRegistry, EditorExtensionRegistry } from '@jupyterlab/codemirror';
5
+ import { snippetCompletion, autocompletion, completeFromList } from '@codemirror/autocomplete';
6
+ const RESTART_RUN_STATELESS = 'gennaker-tools:restart-run-stateless';
7
+ const RESET_JUPYTERLAB = 'gennaker-tools:reset-jupyterlab';
8
+ /**
9
+ * Initialization data for the gennaker-tools extension.
10
+ */
11
+ export const statelessRunPlugin = {
12
+ id: 'gennaker-tools:stateless-run',
13
+ description: 'A JupyterLab extension.',
14
+ autoStart: true,
15
+ requires: [ICommandPalette, INotebookTracker],
16
+ optional: [ITranslator],
17
+ activate: (app, palette, tracker, translator) => {
18
+ const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
19
+ const { commands, shell } = app;
20
+ console.log('JupyterLab extension gennaker-tools is activated!');
21
+ // Add a command
22
+ const isEnabled = () => {
23
+ console.log('enabed', { tracker, shell });
24
+ return (tracker.currentWidget !== null &&
25
+ tracker.currentWidget === shell.currentWidget);
26
+ };
27
+ commands.addCommand(RESTART_RUN_STATELESS, {
28
+ label: 'Restart Kernel, Clear Outputs, and Run All Above Selected Cell',
29
+ caption: 'Clear all outputs, restart kernel, and run all cells above the selected cell',
30
+ isEnabled,
31
+ execute: async (args) => {
32
+ const orig = args['origin'];
33
+ console.log(`${RESTART_RUN_STATELESS} has been called from... ${orig}.`);
34
+ if (orig !== 'init') {
35
+ // Clear all outputs
36
+ await commands.execute('apputils:run-all-enabled', {
37
+ commands: [
38
+ 'notebook:clear-all-cell-outputs',
39
+ 'notebook:restart-and-run-to-selected'
40
+ ]
41
+ });
42
+ }
43
+ }
44
+ });
45
+ // Add the command to the command palette
46
+ const category = trans.__('Notebook Operations');
47
+ palette.addItem({
48
+ command: RESTART_RUN_STATELESS,
49
+ category,
50
+ args: { origin: 'from palette' }
51
+ });
52
+ }
53
+ };
54
+ /**
55
+ * Initialization data for the gennaker-tools extension.
56
+ */
57
+ export const reloadPlugin = {
58
+ id: 'gennaker-tools:reload',
59
+ description: 'A JupyterLab extension.',
60
+ autoStart: true,
61
+ requires: [ICommandPalette],
62
+ optional: [ITranslator],
63
+ activate: (app, palette, translator) => {
64
+ const { commands } = app;
65
+ const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
66
+ commands.addCommand(RESET_JUPYTERLAB, {
67
+ label: 'Reset JupyterLab',
68
+ caption: 'Reset JupyterLab',
69
+ isEnabled: () => true,
70
+ execute: (args) => {
71
+ const orig = args['origin'];
72
+ if (orig !== 'init') {
73
+ window.location.reload();
74
+ }
75
+ }
76
+ });
77
+ // Add the command to the command palette
78
+ const category = trans.__('Reset');
79
+ palette.addItem({
80
+ command: RESET_JUPYTERLAB,
81
+ category,
82
+ args: { origin: 'from palette' }
83
+ });
84
+ }
85
+ };
86
+ const SNIPPET_EXTENSION_SCHEMA = {
87
+ properties: {
88
+ snippets: {
89
+ type: 'array',
90
+ title: 'Codemirror snippets',
91
+ description: 'Snippets of the form accepted by Codemirrors snippetCompletion',
92
+ items: {
93
+ type: 'object',
94
+ properties: {
95
+ type: {
96
+ type: 'string',
97
+ enum: [
98
+ 'class',
99
+ 'constant',
100
+ 'enum',
101
+ 'function',
102
+ 'interface',
103
+ 'keyword',
104
+ 'method',
105
+ 'namespace',
106
+ 'property',
107
+ 'text',
108
+ 'type',
109
+ 'variable'
110
+ ]
111
+ },
112
+ body: { type: 'string' },
113
+ label: { type: 'string' }
114
+ },
115
+ required: ['type', 'body', 'label']
116
+ }
117
+ }
118
+ },
119
+ additionalProperties: false,
120
+ type: 'object'
121
+ };
122
+ const SNIPPETS_PLUGIN_ID = 'gennaker-tools:snippets';
123
+ export const snippetsPlugin = {
124
+ id: SNIPPETS_PLUGIN_ID,
125
+ description: 'A JupyterLab extension.',
126
+ autoStart: true,
127
+ requires: [IEditorExtensionRegistry],
128
+ optional: [],
129
+ activate: (app, registry) => {
130
+ registry.addExtension(Object.freeze({
131
+ name: 'gennaker-tools:snippets',
132
+ factory: () => EditorExtensionRegistry.createConfigurableExtension((config) => {
133
+ return autocompletion({
134
+ override: [
135
+ completeFromList(config.snippets.map(snippet => {
136
+ const { body, ...rest } = snippet;
137
+ return snippetCompletion(body, rest);
138
+ }))
139
+ ]
140
+ });
141
+ }),
142
+ default: { snippets: [] },
143
+ schema: SNIPPET_EXTENSION_SCHEMA
144
+ }));
145
+ }
146
+ };
147
+ export default [statelessRunPlugin, reloadPlugin, snippetsPlugin];
package/package.json CHANGED
@@ -1,10 +1,193 @@
1
1
  {
2
- "name": "gennaker-tools",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for gennaker-tools",
5
- "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
2
+ "name": "gennaker-tools",
3
+ "version": "0.1.2",
4
+ "description": "A JupyterLab extension to provide a restart-and-run-to-selected command variant that clears non-executed cell outputs.",
5
+ "keywords": [
6
+ "jupyter",
7
+ "jupyterlab",
8
+ "jupyterlab-extension"
9
+ ],
10
+ "homepage": "",
11
+ "bugs": {
12
+ "url": "https://github.com/agoose77/gennaker-tools/issues"
13
+ },
14
+ "license": "BSD-3-Clause",
15
+ "author": {
16
+ "name": "Angus",
17
+ "email": "angus@oieltd.com"
18
+ },
19
+ "files": [
20
+ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
21
+ "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
22
+ "schema/*.json",
23
+ "src/**/*.{ts,tsx}"
24
+ ],
25
+ "main": "lib/index.js",
26
+ "types": "lib/index.d.ts",
27
+ "style": "style/index.css",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/agoose77/gennaker-tools.git"
31
+ },
32
+ "scripts": {
33
+ "build": "jlpm build:lib && jlpm build:labextension:dev",
34
+ "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension",
35
+ "build:labextension": "jupyter labextension build .",
36
+ "build:labextension:dev": "jupyter labextension build --development True .",
37
+ "build:lib": "tsc --sourceMap",
38
+ "build:lib:prod": "tsc",
39
+ "clean": "jlpm clean:lib",
40
+ "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
41
+ "clean:lintcache": "rimraf .eslintcache .stylelintcache",
42
+ "clean:labextension": "rimraf gennaker_tools/labextension gennaker_tools/_version.py",
43
+ "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache",
44
+ "eslint": "jlpm eslint:check --fix",
45
+ "eslint:check": "eslint . --cache --ext .ts,.tsx",
46
+ "install:extension": "jlpm build",
47
+ "lint": "jlpm stylelint && jlpm prettier && jlpm eslint",
48
+ "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check",
49
+ "prettier": "jlpm prettier:base --write --list-different",
50
+ "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
51
+ "prettier:check": "jlpm prettier:base --check",
52
+ "stylelint": "jlpm stylelint:check --fix",
53
+ "stylelint:check": "stylelint --cache \"style/**/*.css\"",
54
+ "watch": "run-p watch:src watch:labextension",
55
+ "watch:src": "tsc -w --sourceMap",
56
+ "watch:labextension": "jupyter labextension watch ."
57
+ },
58
+ "dependencies": {
59
+ "@codemirror/autocomplete": "^6.0.1",
60
+ "@jupyterlab/application": "^4.4.0",
61
+ "@jupyterlab/codemirror": "^4.4.0",
62
+ "@jupyterlab/mainmenu": "^4.4.0",
63
+ "@jupyterlab/notebook": "^4.4.0"
64
+ },
65
+ "devDependencies": {
66
+ "@jupyterlab/builder": "^4.2.0",
67
+ "@types/json-schema": "^7.0.11",
68
+ "@types/react": "^18.0.26",
69
+ "@types/react-addons-linked-state-mixin": "^0.14.22",
70
+ "@typescript-eslint/eslint-plugin": "^6.1.0",
71
+ "@typescript-eslint/parser": "^6.1.0",
72
+ "css-loader": "^6.7.1",
73
+ "eslint": "^8.36.0",
74
+ "eslint-config-prettier": "^8.8.0",
75
+ "eslint-plugin-prettier": "^5.0.0",
76
+ "npm-run-all2": "^7.0.1",
77
+ "prettier": "^3.0.0",
78
+ "rimraf": "^5.0.1",
79
+ "source-map-loader": "^1.0.2",
80
+ "style-loader": "^3.3.1",
81
+ "stylelint": "^15.10.1",
82
+ "stylelint-config-recommended": "^13.0.0",
83
+ "stylelint-config-standard": "^34.0.0",
84
+ "stylelint-csstree-validator": "^3.0.0",
85
+ "stylelint-prettier": "^4.0.0",
86
+ "typescript": "~5.5.4",
87
+ "yjs": "^13.5.0"
88
+ },
89
+ "sideEffects": [
90
+ "style/*.css",
91
+ "style/index.js"
92
+ ],
93
+ "styleModule": "style/index.js",
94
+ "publishConfig": {
95
+ "access": "public"
96
+ },
97
+ "jupyterlab": {
98
+ "extension": true,
99
+ "outputDir": "gennaker_tools/labextension",
100
+ "schemaDir": "schema"
101
+ },
102
+ "eslintIgnore": [
103
+ "node_modules",
104
+ "dist",
105
+ "coverage",
106
+ "**/*.d.ts"
107
+ ],
108
+ "eslintConfig": {
109
+ "extends": [
110
+ "eslint:recommended",
111
+ "plugin:@typescript-eslint/eslint-recommended",
112
+ "plugin:@typescript-eslint/recommended",
113
+ "plugin:prettier/recommended"
114
+ ],
115
+ "parser": "@typescript-eslint/parser",
116
+ "parserOptions": {
117
+ "project": "tsconfig.json",
118
+ "sourceType": "module"
119
+ },
120
+ "plugins": [
121
+ "@typescript-eslint"
122
+ ],
123
+ "rules": {
124
+ "@typescript-eslint/naming-convention": [
125
+ "error",
126
+ {
127
+ "selector": "interface",
128
+ "format": [
129
+ "PascalCase"
130
+ ],
131
+ "custom": {
132
+ "regex": "^I[A-Z]",
133
+ "match": true
134
+ }
135
+ }
136
+ ],
137
+ "@typescript-eslint/no-unused-vars": [
138
+ "warn",
139
+ {
140
+ "args": "none"
141
+ }
142
+ ],
143
+ "@typescript-eslint/no-explicit-any": "off",
144
+ "@typescript-eslint/no-namespace": "off",
145
+ "@typescript-eslint/no-use-before-define": "off",
146
+ "@typescript-eslint/quotes": [
147
+ "error",
148
+ "single",
149
+ {
150
+ "avoidEscape": true,
151
+ "allowTemplateLiterals": false
152
+ }
153
+ ],
154
+ "curly": [
155
+ "error",
156
+ "all"
157
+ ],
158
+ "eqeqeq": "error",
159
+ "prefer-arrow-callback": "error"
160
+ }
161
+ },
162
+ "prettier": {
163
+ "singleQuote": true,
164
+ "trailingComma": "none",
165
+ "arrowParens": "avoid",
166
+ "endOfLine": "auto",
167
+ "overrides": [
168
+ {
169
+ "files": "package.json",
170
+ "options": {
171
+ "tabWidth": 4
172
+ }
173
+ }
174
+ ]
175
+ },
176
+ "stylelint": {
177
+ "extends": [
178
+ "stylelint-config-recommended",
179
+ "stylelint-config-standard",
180
+ "stylelint-prettier/recommended"
181
+ ],
182
+ "plugins": [
183
+ "stylelint-csstree-validator"
184
+ ],
185
+ "rules": {
186
+ "csstree/validator": true,
187
+ "property-no-vendor-prefix": null,
188
+ "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$",
189
+ "selector-no-vendor-prefix": null,
190
+ "value-no-vendor-prefix": null
191
+ }
192
+ }
10
193
  }
@@ -0,0 +1,17 @@
1
+ {
2
+ "title": "Gennaker Settings",
3
+ "description": "Gennaker tools settings.",
4
+ "jupyter.lab.menus": {
5
+ "main": [
6
+ {
7
+ "id": "jp-mainmenu-run",
8
+ "items": [
9
+ {
10
+ "command": "stateless:clear-restart-run-to-selected",
11
+ "rank": 80
12
+ }
13
+ ]
14
+ }
15
+ ]
16
+ }
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,199 @@
1
+ import {
2
+ JupyterFrontEnd,
3
+ JupyterFrontEndPlugin
4
+ } from '@jupyterlab/application';
5
+ import { INotebookTracker } from '@jupyterlab/notebook';
6
+ import { ICommandPalette } from '@jupyterlab/apputils';
7
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
8
+ import {
9
+ IEditorExtensionRegistry,
10
+ EditorExtensionRegistry
11
+ } from '@jupyterlab/codemirror';
12
+ import {
13
+ snippetCompletion,
14
+ autocompletion,
15
+ completeFromList
16
+ } from '@codemirror/autocomplete';
17
+ import type { Completion } from '@codemirror/autocomplete';
18
+
19
+ const RESTART_RUN_STATELESS = 'gennaker-tools:restart-run-stateless';
20
+ const RESET_JUPYTERLAB = 'gennaker-tools:reset-jupyterlab';
21
+
22
+ /**
23
+ * Initialization data for the gennaker-tools extension.
24
+ */
25
+ export const statelessRunPlugin: JupyterFrontEndPlugin<void> = {
26
+ id: 'gennaker-tools:stateless-run',
27
+ description: 'A JupyterLab extension.',
28
+ autoStart: true,
29
+ requires: [ICommandPalette, INotebookTracker],
30
+ optional: [ITranslator],
31
+ activate: (
32
+ app: JupyterFrontEnd,
33
+ palette: ICommandPalette,
34
+ tracker: INotebookTracker,
35
+ translator: ITranslator | null
36
+ ) => {
37
+ const trans = (translator ?? nullTranslator).load('jupyterlab');
38
+ const { commands, shell } = app;
39
+
40
+ console.log('JupyterLab extension gennaker-tools is activated!');
41
+ // Add a command
42
+
43
+ const isEnabled = () => {
44
+ console.log('enabed', { tracker, shell });
45
+ return (
46
+ tracker.currentWidget !== null &&
47
+ tracker.currentWidget === shell.currentWidget
48
+ );
49
+ };
50
+
51
+ commands.addCommand(RESTART_RUN_STATELESS, {
52
+ label: 'Restart Kernel, Clear Outputs, and Run All Above Selected Cell',
53
+ caption:
54
+ 'Clear all outputs, restart kernel, and run all cells above the selected cell',
55
+ isEnabled,
56
+ execute: async (args: any) => {
57
+ const orig = args['origin'];
58
+ console.log(
59
+ `${RESTART_RUN_STATELESS} has been called from... ${orig}.`
60
+ );
61
+ if (orig !== 'init') {
62
+ // Clear all outputs
63
+ await commands.execute('apputils:run-all-enabled', {
64
+ commands: [
65
+ 'notebook:clear-all-cell-outputs',
66
+ 'notebook:restart-and-run-to-selected'
67
+ ]
68
+ });
69
+ }
70
+ }
71
+ });
72
+
73
+ // Add the command to the command palette
74
+ const category = trans.__('Notebook Operations');
75
+ palette.addItem({
76
+ command: RESTART_RUN_STATELESS,
77
+ category,
78
+ args: { origin: 'from palette' }
79
+ });
80
+ }
81
+ };
82
+
83
+ /**
84
+ * Initialization data for the gennaker-tools extension.
85
+ */
86
+ export const reloadPlugin: JupyterFrontEndPlugin<void> = {
87
+ id: 'gennaker-tools:reload',
88
+ description: 'A JupyterLab extension.',
89
+ autoStart: true,
90
+ requires: [ICommandPalette],
91
+ optional: [ITranslator],
92
+ activate: (
93
+ app: JupyterFrontEnd,
94
+ palette: ICommandPalette,
95
+ translator: ITranslator | null
96
+ ) => {
97
+ const { commands } = app;
98
+ const trans = (translator ?? nullTranslator).load('jupyterlab');
99
+
100
+ commands.addCommand(RESET_JUPYTERLAB, {
101
+ label: 'Reset JupyterLab',
102
+ caption: 'Reset JupyterLab',
103
+ isEnabled: () => true,
104
+ execute: (args: any) => {
105
+ const orig = args['origin'];
106
+ if (orig !== 'init') {
107
+ window.location.reload();
108
+ }
109
+ }
110
+ });
111
+
112
+ // Add the command to the command palette
113
+ const category = trans.__('Reset');
114
+ palette.addItem({
115
+ command: RESET_JUPYTERLAB,
116
+ category,
117
+ args: { origin: 'from palette' }
118
+ });
119
+ }
120
+ };
121
+
122
+ type SnippetConfigurationItem = Completion & { body: string };
123
+ type SnippetConfiguration = {
124
+ snippets: SnippetConfigurationItem[];
125
+ };
126
+
127
+ const SNIPPET_EXTENSION_SCHEMA = {
128
+ properties: {
129
+ snippets: {
130
+ type: 'array',
131
+ title: 'Codemirror snippets',
132
+ description:
133
+ 'Snippets of the form accepted by Codemirrors snippetCompletion',
134
+ items: {
135
+ type: 'object',
136
+ properties: {
137
+ type: {
138
+ type: 'string',
139
+ enum: [
140
+ 'class',
141
+ 'constant',
142
+ 'enum',
143
+ 'function',
144
+ 'interface',
145
+ 'keyword',
146
+ 'method',
147
+ 'namespace',
148
+ 'property',
149
+ 'text',
150
+ 'type',
151
+ 'variable'
152
+ ]
153
+ },
154
+
155
+ body: { type: 'string' },
156
+ label: { type: 'string' }
157
+ },
158
+ required: ['type', 'body', 'label']
159
+ }
160
+ }
161
+ },
162
+ additionalProperties: false,
163
+ type: 'object'
164
+ };
165
+
166
+ const SNIPPETS_PLUGIN_ID = 'gennaker-tools:snippets';
167
+ export const snippetsPlugin: JupyterFrontEndPlugin<void> = {
168
+ id: SNIPPETS_PLUGIN_ID,
169
+ description: 'A JupyterLab extension.',
170
+ autoStart: true,
171
+ requires: [IEditorExtensionRegistry],
172
+ optional: [],
173
+ activate: (app: JupyterFrontEnd, registry: IEditorExtensionRegistry) => {
174
+ registry.addExtension(
175
+ Object.freeze({
176
+ name: 'gennaker-tools:snippets',
177
+ factory: () =>
178
+ EditorExtensionRegistry.createConfigurableExtension(
179
+ (config: SnippetConfiguration) => {
180
+ return autocompletion({
181
+ override: [
182
+ completeFromList(
183
+ config.snippets.map(snippet => {
184
+ const { body, ...rest } = snippet;
185
+ return snippetCompletion(body, rest);
186
+ })
187
+ )
188
+ ]
189
+ });
190
+ }
191
+ ),
192
+ default: { snippets: [] },
193
+ schema: SNIPPET_EXTENSION_SCHEMA
194
+ })
195
+ );
196
+ }
197
+ };
198
+
199
+ export default [statelessRunPlugin, reloadPlugin, snippetsPlugin];
package/style/base.css ADDED
@@ -0,0 +1,5 @@
1
+ /*
2
+ See the JupyterLab Developer Guide for useful CSS Patterns:
3
+
4
+ https://jupyterlab.readthedocs.io/en/stable/developer/css.html
5
+ */
@@ -0,0 +1 @@
1
+ @import url('base.css');
package/style/index.js ADDED
@@ -0,0 +1 @@
1
+ import './base.css';