Repository: mwouts/jupytext (language: python)

Issue:
jupyterlab how to create a new text notebook
in nbclassic we used to have a menu for creating a new text notebook
![image](https://github.com/mwouts/jupytext/assets/4519227/ef899200-18d7-418a-b386-0ab3dcf2309a)

I can't seem to find the equivalent under jlab
was it dropped on purpose or is it just lack of time, i.e. value/devel time ratio deemed too poor ?

it would be very helpful for students, as we're trying to have them use git for everything, and ipynb under git really does not cut it :)



Discussion / hints:
Hey @parmentelat , yea it's just lack of time / typescript skills. I agree that the "New text notebook" entry was useful. Also I find that accessing the pairing commands through a menu was a bit more user friendly, but I never found the time to map the commands in the menu, although I believe that this should be possible.
would you agree that as a first step, a palette-accessible command to create a new text notebook would make sense ?
or more likely one command per format, I guess ?

Hey @parmentelat , yes indeed!

I think it would be useful to have a commands to create at least notebooks in these formats:
- `py:percent` (or maybe a percent notebook for the language used in the current notebook)
- `md:MyST`

FYI in the notebook extension the menu commands were added here:
https://github.com/mwouts/jupytext/blob/main/jupytext/nbextension/index.js#L376-L383
hi @mwouts 

I still think this is a helpful feature, but I had to put this on the backburner

However maybe we can start discussing how the feature could look like
in my opinion the most natural point where to add this would be here, which conveniently is valid for both lab and nb7 (even in the tree view btw)

![image](https://github.com/mwouts/jupytext/assets/4519227/4cdd458a-b6bd-4c3f-b201-4690bbf6c8e9)

(and at this location we could find a submenu with one entry per text format, like New Py:percent, etc..)

one difference with nb classic though, is that here and esp. in the context of lab, there is no 'current notebook' so the path where the new notebook is created might raise new questions; the simple approach could be to always create it at the server root, and let people move that about using the file explorer; otherwise the feature may need some sort of directory browser.. but I'm not there yet - and far from it ;)

anyways, I have taken a look at the nbextension code that you pointed at; it looks like the crux of it is actually the `new_text_notebook` function https://github.com/mwouts/jupytext/blob/main/jupytext/nbextension/index.js#L237-L267 
but so far I fail to see how that actually works, plus I suspect many changes are going to be needed for jlab.

fyi I'm entering a rather low period for the next few weeks, so as far as I am concerned it's probably not going to happen anytime soon ;-)

Hey @parmentelat , thank you for discussing this! Yes sure not problem at all, this would be useful indeed but there is no hurry, it will happen when we can make it happen.

Yes it would be great to have one or two entries there (maybe just for MyST Markdown and py:percent?).

Personally I mostly use the 'launcher' to create a new notebook, so maybe there we could have two entries 'New percent notebook' and 'New MyST Markdown notebook', with the list of available kernels below?
![launcher](https://github.com/mwouts/jupytext/assets/29915202/e3b0a9ba-1eb6-4326-ba68-6cdfdee48306)

In the case of the percent notebook, the extension would be chosen depending on the kernel indicated by the selected icon in the launcher.

I am moving [this discussion](https://github.com/mwouts/jupytext/pull/1147#issuecomment-1777628401) here for better trackability. 

@parmentelat Did you have any time to work on it? I think this would be a valuable feature for `jupytext`. I can see multiple points where we can add `jupytext` commands:

- Main menu when user wants to create new text notebooks as @parmentelat pointed out
- Launcher icons (only for JupyterLab) @mwouts pointed out
- We can add pairing commands to notebook tool bar as well.

![lab-toolbar](https://github.com/mwouts/jupytext/assets/44365948/b7e225ae-39b2-42ac-a5d8-2c9de6beeb2b)

As you can see there is a lot of space there and we can add all the pairing commands there for better UX.

I can work on these features, maybe implementing them separately for better discussion and review. What do you think?

Hi @mahendrapaipuri 

thank you so much for raising this again; 
actually I have not had time to address this at all, so please feel free to go ahead !

as far as I am concerned, I have no preference between all the methods proposed
going from the current situation (no simple way to create a text notebook) to a situation where we would have one - any, really - way to do it simply, would have immense value in the classroom where newbie students are currently at a loss to do this :)



Ok, I will try it out and keep you posted. 

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b01fc6d3d..0dbbcbc98 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,12 +9,12 @@ on:
         description: Upload build artifacts
   push:
     paths-ignore:
-      - 'CHANGELOG.md'
-    branches: [ main ]
+      - "CHANGELOG.md"
+    branches: [main]
   pull_request:
-    branches: [ main ]
+    branches: [main]
   schedule:
-    - cron: '0 11 * * 4'
+    - cron: "0 11 * * 4"
 
 permissions:
   # All nested workflows will inherit these permissions and so no need to declare
@@ -34,25 +34,29 @@ jobs:
     uses: ./.github/workflows/step_pre-commit.yml
 
   codeql:
-    needs: [ pre-commit ]
+    needs: [pre-commit]
     uses: ./.github/workflows/step_static-analysis.yml
 
   test-pip:
-    needs: [ codeql ]
+    needs: [codeql]
     uses: ./.github/workflows/step_tests-pip.yml
 
   test-conda:
-    needs: [ codeql ]
+    needs: [codeql]
     uses: ./.github/workflows/step_tests-conda.yml
 
+  test-ui:
+    needs: [codeql]
+    uses: ./.github/workflows/step_tests-ui.yml
+
   build:
-    needs: [ test-pip, test-conda ]
+    needs: [test-pip, test-conda, test-ui]
     uses: ./.github/workflows/step_build.yml
     with:
       upload: ${{ inputs.upload-build-artifacts || false }}
 
   pass:
-    needs: [ pre-commit, codeql, test-pip, test-conda, build ]
+    needs: [pre-commit, codeql, test-pip, test-conda, test-ui, build]
     runs-on: ubuntu-latest
     steps:
       - name: Check jobs
diff --git a/docs/images/jupyterlab_launcher.png b/docs/images/jupyterlab_launcher.png
new file mode 100644
index 000000000..39bbbabea
Binary files /dev/null and b/docs/images/jupyterlab_launcher.png differ
diff --git a/docs/images/jupyterlab_main_menu.png b/docs/images/jupyterlab_main_menu.png
new file mode 100644
index 000000000..243db3a01
Binary files /dev/null and b/docs/images/jupyterlab_main_menu.png differ
diff --git a/docs/images/jupyterlab_settings.png b/docs/images/jupyterlab_settings.png
new file mode 100644
index 000000000..dc7a4316e
Binary files /dev/null and b/docs/images/jupyterlab_settings.png differ
diff --git a/docs/index.md b/docs/index.md
index 80a4bbb51..06538f3a6 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -10,6 +10,7 @@
 install.md
 text-notebooks.md
 paired-notebooks.md
+jupyterlab-extension.md
 config.md
 advanced-options.md
 formats-scripts.md
diff --git a/docs/jupyterlab-extension.md b/docs/jupyterlab-extension.md
new file mode 100644
index 000000000..eb6982261
--- /dev/null
+++ b/docs/jupyterlab-extension.md
@@ -0,0 +1,31 @@
+# Frontend extension
+
+Recent versions of Jupytext (`>=1.16.0`) ships frontend extension that enables users
+to create text notebooks and pair notebooks from main menu of JupyterLab 4 and
+Notebook 7. In addition, the frontend extension adds selected Jupytext text
+notebook formats to launcher in the `Jupytext` section so that users can launch text
+notebooks as they launch a regular notebook from JupyterLab launcher.
+
+## Launcher icons
+
+After installing Jupytext extension, users will have a new category in the launcher
+called Jupytext as shown below:
+
+![](images/jupyterlab_launcher.png)
+
+Users can remove and/or add new formats to the Jupytext section _via_ `Settings>Jupytext`.
+
+![](images/jupyterlab_settings.png)
+
+By clicking `Add` and adding a new format, say `qmd`, will
+add the Quatro Text Notebook to the launcher. **Note** that users need to refresh the current browser tab when they modify the settings for them to take effect.
+
+Besides, users can also change the category of Text Notebook launcher icons using
+`Category` field in the Settings. For example, by using `Notebook` as category, all
+Text Notebook launchers will be moved into `Notebook` category.
+
+## Main menu
+
+It is also possible to launch text notebooks and/or pair existing notebooks from the dedicated Jupytext main menu as shown below.
+
+![](images/jupyterlab_main_menu.png)
diff --git a/jupyterlab/.gitignore b/jupyterlab/.gitignore
index 56078241f..4598cc2e9 100644
--- a/jupyterlab/.gitignore
+++ b/jupyterlab/.gitignore
@@ -25,3 +25,7 @@ lib/
 *.egg-info/
 .ipynb_checkpoints
 *.tsbuildinfo
+
+# Ignore playwright stuff
+**/playwright-report
+**/test-results
diff --git a/jupyterlab/package.json b/jupyterlab/package.json
index d2e7da9b9..2d3017670 100644
--- a/jupyterlab/package.json
+++ b/jupyterlab/package.json
@@ -61,7 +61,7 @@
         ],
         "parser": "@typescript-eslint/parser",
         "parserOptions": {
-            "project": "tsconfig.eslint.json",
+            "project": "packages/**/tsconfig.json",
             "sourceType": "module",
             "tsconfigRootDir": "."
         },
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/README.md b/jupyterlab/packages/jupyterlab-jupytext-extension/README.md
index d36e2d420..e6dbe22e3 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/README.md
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/README.md
@@ -23,15 +23,33 @@ jupyter labextension install jupyterlab-jupytext@1.1.1  # for JupyterLab 1.x
 
 # How to develop this extension
 
-Please follow the instructions at [developing.md](../../../docs/developing.md), i.e.
-create a Python environment with a recent version of `nodejs`, and install
-a development version of Jupytext by running
+For fine-grained access to the `jlpm` command and various build steps:
 
 ```bash
 pip install -e '.[dev]'
+cd jupyterlab/packages/jupyterlab-jupytext
+jlpm
+jlpm install:extension     # Symlink into `{sys.prefix}/share/jupyter/labextensions`
 ```
 
-at the root of the Jupytext repository.
+(see also the instructions at [developing.md](../../../docs/developing.md) on how to create a Python environment with a recent version of `nodejs`)
+
+Watch the source directory and automatically rebuild the `lib` folder:
+
+```bash
+cd jupyterlab/packages/jupyterlab-jupytext
+# Watch the source directory in one terminal, automatically rebuilding when needed
+jlpm watch
+# Run JupyterLab in another terminal
+jupyter lab
+```
+
+While running `jlpm watch`, every saved change to a `.ts` file will immediately be
+built locally and available in your running Jupyter client. "Hard" refresh JupyterLab or Notebook
+with <kbd>CTRL-F5</kbd> or <kbd>⌘-F5</kbd> to load the change in your browser
+(you may need to wait several seconds for the extension to be fully rebuilt).
+
+Read more on this on the [JupyterLab documentation](https://jupyterlab.readthedocs.io/en/latest/extension/extension_dev.html#developing-a-prebuilt-extension).
 
 # How to publish a new version of the extension on npm
 
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/package.json b/jupyterlab/packages/jupyterlab-jupytext-extension/package.json
index 8b1c9c6d0..92bea16b3 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/package.json
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/package.json
@@ -24,6 +24,9 @@
     "type": "git",
     "url": "https://github.com/mwouts/jupytext.git"
   },
+  "workspaces": [
+    "ui-tests"
+  ],
   "scripts": {
     "build": "jlpm run build:lib && jlpm run build:labextension:dev && jlpm run copy:extensioncfgfile",
     "build:labextension": "jupyter labextension build .",
@@ -35,7 +38,7 @@
     "clean:all": "jlpm run clean:lib && jlpm run clean:labextension",
     "clean:labextension": "rimraf ../../jupyterlab_jupytext/labextension",
     "clean:lib": "rimraf lib tsconfig.tsbuildinfo",
-    "install:extension": "jupyter labextension develop --overwrite .",
+    "install:extension": "python ../../scripts/install_extension.py",
     "watch": "run-p watch:src watch:labextension",
     "watch:labextension": "jupyter labextension watch .",
     "watch:src": "tsc -w"
@@ -65,18 +68,23 @@
     "@jupyterlab/application": "^4.0.0",
     "@jupyterlab/apputils": "^4.0.0",
     "@jupyterlab/codeeditor": "^4.0.0",
+    "@jupyterlab/docregistry": "^4.0.0",
+    "@jupyterlab/filebrowser": "^4.0.0",
+    "@jupyterlab/launcher": "^4.0.0",
     "@jupyterlab/nbformat": "^4.0.0",
     "@jupyterlab/notebook": "^4.0.0",
     "@jupyterlab/rendermime": "^4.0.0",
     "@jupyterlab/settingregistry": "^4.0.0",
     "@jupyterlab/translation": "^4.0.0",
     "@jupyterlab/ui-components": "^4.0.0",
+    "@lumino/coreutils": "^2.0.0",
+    "@lumino/disposable": "^2.0.0",
     "jupyterlab-rise": "^0.41.0"
   },
   "devDependencies": {
     "@jupyterlab/builder": "^4.0.0",
     "npm-run-all": "^4.1.5",
-    "rimraf": "^3.0.2",
-    "typescript": "~5.0.1"
+    "rimraf": "^4.4.1",
+    "typescript": "~5.0.2"
   }
 }
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/schema/plugin.json b/jupyterlab/packages/jupyterlab-jupytext-extension/schema/plugin.json
new file mode 100644
index 000000000..2dcfc2b60
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/schema/plugin.json
@@ -0,0 +1,182 @@
+{
+  "title": "Jupytext",
+  "description": "Jupytext Menu Settings.",
+  "properties": {
+    "category": {
+      "title": "Category",
+      "description": "Category under which Jupytext Text Notebooks will be placed. Refresh the current browser tab for the changes to take effect.",
+      "type": "string",
+      "default": "Jupytext"
+    },
+    "format": {
+      "title": "Jupytext Text Notebook Launcher Items Formats",
+      "description": "List of Jupytext Text Notebook formats that will be added to launcher. Refresh the current browser tab for the changes to take effect.",
+      "items": {
+        "type": "string"
+      },
+      "type": "array",
+      "default": ["auto:light", "auto:percent", "md", "md:myst", "Rmd"]
+    }
+  },
+  "jupyter.lab.menus": {
+    "main": [
+      {
+        "id": "jp-mainmenu-jupytext-menu",
+        "label": "Jupytext",
+        "rank": 80,
+        "items": [
+          {
+            "type": "submenu",
+            "submenu": {
+              "id": "jp-mainmenu-jupytext-new",
+              "label": "New Text Notebook",
+              "items": [
+                {
+                  "command": "jupytext:create-new-text-noteboook-auto:percent",
+                  "rank": 1
+                },
+                {
+                  "type": "separator",
+                  "rank": 2
+                },
+                {
+                  "command": "jupytext:create-new-text-noteboook-auto:light",
+                  "rank": 5
+                },
+                {
+                  "command": "jupytext:create-new-text-noteboook-auto:hydrogen",
+                  "rank": 10
+                },
+                {
+                  "command": "jupytext:create-new-text-noteboook-auto:nomarker",
+                  "rank": 15
+                },
+                {
+                  "type": "separator",
+                  "rank": 16
+                },
+                {
+                  "command": "jupytext:create-new-text-noteboook-md",
+                  "rank": 20
+                },
+                {
+                  "command": "jupytext:create-new-text-noteboook-md:myst",
+                  "rank": 25
+                },
+                {
+                  "type": "separator",
+                  "rank": 26
+                },
+                {
+                  "command": "jupytext:create-new-text-noteboook-Rmd",
+                  "rank": 30
+                },
+                {
+                  "command": "jupytext:create-new-text-noteboook-qmd",
+                  "rank": 35
+                }
+              ]
+            },
+            "rank": 1
+          },
+          {
+            "type": "submenu",
+            "submenu": {
+              "id": "jp-mainmenu-jupytext-new",
+              "label": "Pair Notebook",
+              "items": [
+                {
+                  "command": "jupytext:ipynb",
+                  "rank": 1
+                },
+                {
+                  "type": "separator",
+                  "rank": 2
+                },
+                {
+                  "command": "jupytext:auto:light",
+                  "rank": 5
+                },
+                {
+                  "command": "jupytext:auto:percent",
+                  "rank": 10
+                },
+                {
+                  "command": "jupytext:auto:hydrogen",
+                  "rank": 15
+                },
+                {
+                  "command": "jupytext:auto:nomarker",
+                  "rank": 20
+                },
+                {
+                  "type": "separator",
+                  "rank": 25
+                },
+                {
+                  "command": "jupytext:md",
+                  "rank": 30
+                },
+                {
+                  "command": "jupytext:md:myst",
+                  "rank": 35
+                },
+                {
+                  "type": "separator",
+                  "rank": 40
+                },
+                {
+                  "command": "jupytext:Rmd",
+                  "rank": 45
+                },
+                {
+                  "command": "jupytext:qmd",
+                  "rank": 50
+                },
+                {
+                  "type": "separator",
+                  "rank": 55
+                },
+                {
+                  "command": "jupytext:custom",
+                  "rank": 60
+                },
+                {
+                  "type": "separator",
+                  "rank": 65
+                },
+                {
+                  "command": "jupytext:none",
+                  "rank": 70
+                }
+              ]
+            },
+            "rank": 10
+          },
+          {
+            "type": "separator",
+            "rank": 11
+          },
+          {
+            "command": "jupytext:metadata",
+            "rank": 30
+          },
+          {
+            "type": "separator",
+            "rank": 31
+          },
+          {
+            "command": "jupytext:faq",
+            "rank": 40
+          },
+          {
+            "command": "jupytext:reference",
+            "rank": 41
+          }
+        ]
+      }
+    ]
+  },
+  "additionalProperties": false,
+  "type": "object"
+}
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts
index 06a2c1064..a700e7ed4 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts
@@ -5,552 +5,193 @@ import {
 
 import {
   ICommandPalette,
-  showErrorMessage,
   IToolbarWidgetRegistry,
   createToolbarFactory,
+  showErrorMessage,
 } from '@jupyterlab/apputils';
 
+import { IDocumentManager } from '@jupyterlab/docmanager';
+
+import { Contents } from '@jupyterlab/services';
+
+import { ILauncher } from '@jupyterlab/launcher';
+
 import { ISettingRegistry } from '@jupyterlab/settingregistry';
 
 import { IEditorServices } from '@jupyterlab/codeeditor';
 
-import * as nbformat from '@jupyterlab/nbformat';
-
 import {
   INotebookTracker,
   INotebookWidgetFactory,
   NotebookPanel,
   NotebookWidgetFactory,
 } from '@jupyterlab/notebook';
+
 import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
 
-import {
-  ITranslator,
-  nullTranslator,
-  TranslationBundle,
-} from '@jupyterlab/translation';
+import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
 
-import { markdownIcon } from '@jupyterlab/ui-components';
+import { IDocumentWidget } from '@jupyterlab/docregistry';
 
-import { IRisePreviewFactory } from 'jupyterlab-rise';
+import { ITranslator, nullTranslator } from '@jupyterlab/translation';
 
-interface IJupytextFormat {
-  /**
-   * Conversion format
-   */
-  format: string;
-  /**
-   * Command label
-   */
-  label: string;
-}
-
-interface IJupytextRepresentation {
-  format_name: string;
-  extension: string;
-}
-
-interface IJupytextSection {
-  formats?: string;
-  notebook_metadata_filter?: string;
-  cell_metadata_filter?: string;
-  text_representation?: IJupytextRepresentation;
-}
-
-function getJupytextFormats(trans: TranslationBundle): IJupytextFormat[] {
-  return [
-    {
-      format: 'ipynb',
-      label: trans.__('Pair Notebook with ipynb document'),
-    },
-    {
-      format: 'auto:light',
-      label: trans.__('Pair Notebook with light Script'),
-    },
-    {
-      format: 'auto:percent',
-      label: trans.__('Pair Notebook with percent Script'),
-    },
-    {
-      format: 'auto:hydrogen',
-      label: trans.__('Pair Notebook with Hydrogen Script'),
-    },
-    {
-      format: 'auto:nomarker',
-      label: trans.__('Pair Notebook with nomarker Script'),
-    },
-    {
-      format: 'md',
-      label: trans.__('Pair Notebook with Markdown'),
-    },
-    {
-      format: 'md:myst',
-      label: trans.__('Pair Notebook with MyST Markdown'),
-    },
-    {
-      format: 'Rmd',
-      label: trans.__('Pair Notebook with R Markdown'),
-    },
-    {
-      format: 'qmd',
-      label: trans.__('Pair Notebook with Quarto (qmd)'),
-    },
-    {
-      format: 'custom',
-      label: trans.__('Custom pairing'),
-    },
-    {
-      format: 'none',
-      label: trans.__('Unpair Notebook'),
-    },
-  ];
-}
+import { markdownIcon, notebookIcon } from '@jupyterlab/ui-components';
 
-/**
- * Supported file formats.
- */
-const LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS = ['ipynb', 'md', 'Rmd', 'qmd'];
-
-// will get updated upon activation
-// default is true, and we will turn this off only iff
-// app.name is "JupyterLab" and the version is 3.x or below
-let JLAB4 = true;
-
-function get_jupytext_formats(
-  notebook_tracker: INotebookTracker
-): Array<string> {
-  if (!notebook_tracker.currentWidget) {
-    return [];
-  }
-
-  const model = notebook_tracker.currentWidget.context.model;
-
-  const jupytext: IJupytextSection = (
-    JLAB4
-      ? (model as any).getMetadata('jupytext')
-      : (model.metadata as any)?.get('jupytext')
-  ) as IJupytextSection;
-  if (!jupytext) {
-    return [];
-  }
-  const formats: Array<string> =
-    jupytext && jupytext.formats ? jupytext.formats.split(',') : [];
-  return formats.filter((fmt) => {
-    return fmt !== '';
-  });
-}
-
-function get_selected_formats(
-  notebook_tracker: INotebookTracker
-): Array<string> {
-  if (!notebook_tracker.currentWidget) {
-    return [];
-  }
-
-  let formats = get_jupytext_formats(notebook_tracker);
-
-  const model = notebook_tracker.currentWidget.context.model;
-
-  const lang = (
-    JLAB4
-      ? (model as any).getMetadata('language_info')
-      : (model.metadata as any)?.get('language_info')
-  ) as nbformat.ILanguageInfoMetadata;
-  if (lang && lang.file_extension) {
-    const script_ext = lang.file_extension.substring(1);
-    formats = formats.map((fmt) => {
-      if (fmt === script_ext) {
-        return 'auto:light';
-      }
-      return fmt.replace(script_ext + ':', 'auto:');
-    });
-  }
-
-  let notebook_extension: string | undefined =
-    notebook_tracker.currentWidget.context.path.split('.').pop();
-  if (!notebook_extension) {
-    return formats;
-  }
-
-  notebook_extension =
-    LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS.indexOf(notebook_extension) === -1
-      ? 'auto'
-      : notebook_extension;
-  for (const i in formats) {
-    const ext = formats[i].split(':')[0];
-    if (ext === notebook_extension) {
-      return formats;
-    }
-  }
-
-  // the notebook extension was not found among the formats
-  if (
-    LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS.indexOf(notebook_extension) !== -1
-  ) {
-    formats.push(notebook_extension);
-  } else {
-    let format_name = 'light';
-
-    const model = notebook_tracker.currentWidget.context.model;
-    const jupytext: IJupytextSection = (
-      JLAB4
-        ? (model as any).getMetadata('jupytext')
-        : (model.metadata as any)?.get('jupytext')
-    ) as IJupytextSection;
-    if (
-      jupytext &&
-      jupytext.text_representation &&
-      jupytext.text_representation.format_name
-    ) {
-      format_name = jupytext.text_representation.format_name;
-    }
+import { DisposableSet } from '@lumino/disposable';
 
-    formats.push('auto:' + format_name);
-  }
-  return formats;
-}
+import { JSONExt, ReadonlyJSONValue } from '@lumino/coreutils';
+
+import { IRisePreviewFactory } from 'jupyterlab-rise';
+
+import {
+  JUPYTEXT_EXTENSION_ID,
+  FILE_TYPES,
+  TEXT_NOTEBOOKS_LAUNCHER_ICONS,
+  FACTORY,
+  CommandIDs,
+  IJupytextFormat,
+} from './tokens';
+
+import {
+  getAvailJupytextFormats,
+  isPairCommandToggled,
+  isPairCommandEnabled,
+  executePairCommand,
+  isMetadataCommandToggled,
+  isMetadataCommandEnabled,
+  executeMetadataCommand,
+} from './jupytext';
 
 /**
  * Initialization data for the jupyterlab-jupytext extension.
  */
 const extension: JupyterFrontEndPlugin<void> = {
-  id: 'jupyterlab-jupytext',
+  id: JUPYTEXT_EXTENSION_ID,
   autoStart: true,
-  optional: [ITranslator, ICommandPalette, IRisePreviewFactory],
+  optional: [
+    ILauncher,
+    IDefaultFileBrowser,
+    ITranslator,
+    ICommandPalette,
+    IRisePreviewFactory,
+  ],
   requires: [
     NotebookPanel.IContentFactory,
     IEditorServices,
+    IDocumentManager,
     IRenderMimeRegistry,
     INotebookWidgetFactory,
     INotebookTracker,
     ISettingRegistry,
     IToolbarWidgetRegistry,
   ],
-  activate: (
+  activate: async (
     app: JupyterFrontEnd,
     contentFactory: NotebookPanel.IContentFactory,
     editorServices: IEditorServices,
+    docManager: IDocumentManager,
     rendermime: IRenderMimeRegistry,
     notebookFactory: NotebookWidgetFactory.IFactory,
     notebookTracker: INotebookTracker,
     settingRegistry: ISettingRegistry,
     toolbarRegistry: IToolbarWidgetRegistry,
+    launcher: ILauncher | null,
+    defaultBrowser: IDefaultFileBrowser | null,
     translator: ITranslator | null,
     palette: ICommandPalette | null,
     riseFactory: IRisePreviewFactory | null
   ) => {
-    // https://semver.org/#semantic-versioning-specification-semver
-    // npm semver requires pre-release versions to come with a hyphen
-    // so 7.0.0rc2 won't work with semver
-    // in addition, when running in the notebook7 context, app refers
-    // to the notebook7 application, not the jupyterlab application
-    if (app.name === 'JupyterLab') {
-      const app_numbers = app.version.match(/[0-9]+/);
-      if (app_numbers) {
-        JLAB4 = parseInt(app_numbers[0], 10) >= 4;
-      }
-    }
     console.log('JupyterLab extension jupytext is activating...');
-    console.debug(`bundled jupytext labextension: JLAB4=${JLAB4}`);
     const trans = (translator ?? nullTranslator).load('jupytext');
 
-    // Jupytext formats
-    const JUPYTEXT_FORMATS = getJupytextFormats(trans);
-    JUPYTEXT_FORMATS.forEach((args, rank) => {
+    // Load settings
+    let launcherItems = TEXT_NOTEBOOKS_LAUNCHER_ICONS;
+    let launcherItemsCategory = 'Jupytext';
+    if (settingRegistry) {
+      const settings = await settingRegistry.load(extension.id);
+      launcherItems = settings.get('format').composite as string[];
+      launcherItemsCategory = settings.get('category').composite as string;
+    }
+
+    // Unpack necessary components
+    const { commands, serviceManager, docRegistry } = app;
+
+    // Get all Jupytext formats
+    const JUPYTEXT_FORMATS = getAvailJupytextFormats(trans);
+
+    // Register all pairing commands
+    JUPYTEXT_FORMATS.forEach((args: IJupytextFormat, rank: number) => {
       const format: string = args['format'];
-      const command: string = 'jupytext:' + format;
-      app.commands.addCommand(command, {
+      const command = `jupytext:${format}`;
+      commands.addCommand(command, {
         label: args['label'],
         isToggled: () => {
-          if (!notebookTracker.currentWidget) {
-            return false;
-          }
-          const jupytext_formats = get_selected_formats(notebookTracker);
-
-          if (format === 'custom') {
-            for (const i in jupytext_formats) {
-              const fmt = jupytext_formats[i];
-              if (
-                [
-                  'ipynb',
-                  'auto:light',
-                  'auto:percent',
-                  'auto:hydrogen',
-                  'auto:nomarker',
-                  'md',
-                  'Rmd',
-                  'qmd',
-                  'md:myst',
-                ].indexOf(fmt) === -1
-              ) {
-                return true;
-              }
-            }
-            return false;
-          }
-          return jupytext_formats.indexOf(format) !== -1;
+          return isPairCommandToggled(format, notebookTracker);
         },
         isEnabled: () => {
-          if (!notebookTracker.currentWidget) {
-            return false;
-          }
-
-          const notebook_extension: string | undefined =
-            notebookTracker.currentWidget.context.path.split('.').pop();
-          if (format === notebook_extension) {
-            return false;
-          }
-
-          if (format === 'none') {
-            const formats = get_selected_formats(notebookTracker);
-            return formats.length > 1;
-          }
-
-          return true;
+          return isPairCommandEnabled(format, notebookTracker);
         },
         execute: () => {
-          if (notebookTracker.currentWidget === null) {
-            return;
-          }
-          const model = notebookTracker.currentWidget.context.model;
-          let jupytext: IJupytextSection = (
-            JLAB4
-              ? (model as any).getMetadata('jupytext')
-              : (model.metadata as any)?.get('jupytext')
-          ) as IJupytextSection | undefined;
-          let formats: Array<string> = get_selected_formats(notebookTracker);
-
-          // Toggle the selected format
-          console.log('Jupytext: executing command=' + command);
-          if (format === 'custom') {
-            showErrorMessage(
-              trans.__('Error'),
-              trans.__(
-                'Please edit the notebook metadata directly if you wish a custom configuration.'
-              )
-            );
-            return;
-          }
-          // Toggle the selected format
-          let notebook_extension: string =
-            notebookTracker.currentWidget.context.path
-              .split('.')
-              .pop() as string;
-          notebook_extension =
-            LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS.indexOf(
-              notebook_extension
-            ) === -1
-              ? 'auto'
-              : notebook_extension;
-
-          // Toggle the selected format
-          const index = formats.indexOf(format);
-          if (format === 'none') {
-            // Only keep one format - one that matches the current extension
-            for (const i in formats) {
-              const fmt = formats[i];
-              if (fmt.split(':')[0] === notebook_extension) {
-                formats = [fmt];
-                break;
-              }
-            }
-          } else if (index !== -1) {
-            formats.splice(index, 1);
-
-            // The current file extension can't be unpaired
-            let ext_found = false;
-            for (const i in formats) {
-              const fmt = formats[i];
-              if (fmt.split(':')[0] === notebook_extension) {
-                ext_found = true;
-                break;
-              }
-            }
-
-            if (!ext_found) {
-              return;
-            }
-          } else {
-            // We can't have the same extension multiple times
-            const new_formats = [];
-            for (const i in formats) {
-              const fmt = formats[i];
-              if (fmt.split(':')[0] !== format.split(':')[0]) {
-                new_formats.push(fmt);
-              }
-            }
-
-            formats = new_formats;
-            formats.push(format);
-          }
-
-          if (formats.length === 1) {
-            if (notebook_extension !== 'auto') {
-              formats = [];
-            } else if (jupytext?.text_representation) {
-              const format_name = formats[0].split(':')[1];
-              jupytext.text_representation.format_name = format_name;
-              formats = [];
-            }
-          }
-
-          if (formats.length === 0) {
-            // an older version was re-fetching the jupytext metadata here
-            // but this is not necessary, as the metadata is already available
-            if (!jupytext) {
-              return;
-            }
-
-            if (jupytext.formats) {
-              delete jupytext.formats;
-            }
-            if (Object.keys(jupytext).length === 0) {
-              JLAB4
-                ? (model as any).deleteMetadata('jupytext')
-                : (model.metadata as any).delete('jupytext');
-            } else if (JLAB4) {
-              (model as any).setMetadata('jupytext', jupytext);
-            }
-            return;
-          }
-
-          // set the desired format
-          if (jupytext) {
-            jupytext.formats = formats.join();
-          } else {
-            jupytext = { formats: formats.join() };
-            if (!JLAB4) {
-              (model.metadata as any)?.set('jupytext', jupytext);
-            }
-          }
-          if (JLAB4) {
-            (model as any).setMetadata('jupytext', jupytext);
-          }
+          return executePairCommand(command, format, notebookTracker, trans);
         },
       });
 
       console.log(
-        'Jupytext: adding command=' + command + ' with rank=' + (rank + 1)
+        'Registering pairing command=' + command + ' with rank=' + (rank + 1)
       );
       palette?.addItem({ command, rank: rank + 2, category: 'Jupytext' });
     });
 
-    // Jupytext's documentation
-    palette?.addItem({
-      args: {
-        text: trans.__('Jupytext Reference'),
-        url: 'https://jupytext.readthedocs.io/en/latest/',
-      },
-      command: 'help:open',
-      category: 'Jupytext',
-      rank: 0,
-    });
-
-    palette?.addItem({
-      args: {
-        text: trans.__('Jupytext FAQ'),
-        url: 'https://jupytext.readthedocs.io/en/latest/faq.html',
-      },
-      command: 'help:open',
-      category: 'Jupytext',
-      rank: 1,
-    });
-
     // Metadata in text representation
-    app.commands.addCommand('jupytext_metadata', {
+    commands.addCommand(CommandIDs.metadata, {
       label: trans.__('Include Metadata'),
       isToggled: () => {
-        if (!notebookTracker.currentWidget) {
-          return false;
-        }
-
-        const model = notebookTracker.currentWidget.context.model;
-        const jupytext_metadata = JLAB4
-          ? (model as any).getMetadata('jupytext')
-          : (model.metadata as any)?.get('jupytext');
-        if (!jupytext_metadata) {
-          return false;
-        }
-
-        const jupytext: IJupytextSection =
-          jupytext_metadata as unknown as IJupytextSection;
-
-        if (jupytext.notebook_metadata_filter === '-all') {
-          return false;
-        }
-
-        return true;
+        return isMetadataCommandToggled(notebookTracker);
       },
       isEnabled: () => {
-        if (!notebookTracker.currentWidget) {
-          return false;
-        }
-
-        const model = notebookTracker.currentWidget.context.model;
-        const jupytext_metadata = JLAB4
-          ? (model as any).getMetadata('jupytext')
-          : (model.metadata as any)?.get('jupytext');
-        if (!jupytext_metadata) {
-          return false;
-        }
-
-        const jupytext: IJupytextSection =
-          jupytext_metadata as unknown as IJupytextSection;
-
-        if (jupytext.notebook_metadata_filter === undefined) {
-          return true;
-        }
-
-        if (jupytext.notebook_metadata_filter === '-all') {
-          return true;
-        }
-
-        return false;
+        return isMetadataCommandEnabled(notebookTracker);
       },
       execute: () => {
-        console.log('Jupytext: toggling YAML header');
-        if (!notebookTracker.currentWidget) {
-          return;
-        }
-
-        const model = notebookTracker.currentWidget.context.model;
-        const jupytext_metadata = JLAB4
-          ? (model as any).getMetadata('jupytext')
-          : (model.metadata as any)?.get('jupytext');
-        if (!jupytext_metadata) {
-          return;
-        }
+        return executeMetadataCommand(notebookTracker);
+      },
+    });
+    palette?.addItem({
+      command: CommandIDs.metadata,
+      rank: JUPYTEXT_FORMATS.length + 3,
+      category: 'Jupytext',
+    });
 
-        const jupytext = ((jupytext_metadata as unknown) ??
-          {}) as IJupytextSection;
-
-        if (jupytext.notebook_metadata_filter) {
-          delete jupytext.notebook_metadata_filter;
-          if (jupytext.cell_metadata_filter === '-all') {
-            delete jupytext.cell_metadata_filter;
-          }
-        } else {
-          jupytext.notebook_metadata_filter = '-all';
-          if (jupytext.cell_metadata_filter === undefined) {
-            jupytext.cell_metadata_filter = '-all';
-          }
-        }
-        if (JLAB4) {
-          (model as any).setMetadata('jupytext', jupytext);
-        }
+    // Register Jupytext FAQ command
+    commands.addCommand(CommandIDs.faq, {
+      label: trans.__('Jupytext FAQ'),
+      execute: () => {
+        window.open('https://jupytext.readthedocs.io/en/latest/faq.html');
       },
     });
+    palette?.addItem({
+      command: CommandIDs.faq,
+      rank: 99,
+      category: 'Jupytext',
+    });
 
+    // Register Jupytext Reference
+    commands.addCommand(CommandIDs.reference, {
+      label: trans.__('Jupytext Reference'),
+      execute: () => {
+        window.open('https://jupytext.readthedocs.io/en/latest/');
+      },
+    });
     palette?.addItem({
-      command: 'jupytext_metadata',
-      rank: JUPYTEXT_FORMATS.length + 3,
+      command: CommandIDs.faq,
+      rank: 100,
       category: 'Jupytext',
     });
 
     // Define file types
-    app.docRegistry.addFileType(
+    docRegistry.addFileType(
       {
         name: 'myst',
+        contentType: 'notebook',
         displayName: trans.__('MyST Markdown Notebook'),
         extensions: ['.myst', '.mystnb', '.mnb'],
         icon: markdownIcon,
@@ -558,20 +199,22 @@ const extension: JupyterFrontEndPlugin<void> = {
       ['Notebook']
     );
 
-    app.docRegistry.addFileType(
+    docRegistry.addFileType(
       {
         name: 'r-markdown',
+        contentType: 'notebook',
         displayName: trans.__('R Markdown Notebook'),
         // Extension file are transformed to lower case...
-        extensions: ['.rmd'],
+        extensions: ['.Rmd'],
         icon: markdownIcon,
       },
       ['Notebook']
     );
 
-    app.docRegistry.addFileType(
+    docRegistry.addFileType(
       {
         name: 'quarto',
+        contentType: 'notebook',
         displayName: trans.__('Quarto Notebook'),
         extensions: ['.qmd'],
         icon: markdownIcon,
@@ -579,40 +222,23 @@ const extension: JupyterFrontEndPlugin<void> = {
       ['Notebook']
     );
 
-    // the way to create the toolbar factory is different in JupyterLab 3 and 4
-    let toolbarFactory;
-    if (!JLAB4) {
-      toolbarFactory = notebookFactory.toolbarFactory;
-    } else {
-      // primarily this block is copied/pasted from jlab4 code and specifically
-      // jupyterlab/packages/notebook-extension/src/index.ts
-      // inside the function `activateWidgetFactory` at line 1150 as of this writing
-      //
-      const FACTORY = 'Notebook';
-      const PANEL_SETTINGS = '@jupyterlab/notebook-extension:panel';
-
-      toolbarFactory = createToolbarFactory(
-        toolbarRegistry,
-        settingRegistry,
-        FACTORY,
-        PANEL_SETTINGS,
-        translator
-      );
-    }
+    // primarily this block is copied/pasted from jlab4 code and specifically
+    // jupyterlab/packages/notebook-extension/src/index.ts
+    // inside the function `activateWidgetFactory` at line 1150 as of this writing
+    //
+    const toolbarFactory = createToolbarFactory(
+      toolbarRegistry,
+      settingRegistry,
+      'Notebook',
+      '@jupyterlab/notebook-extension:panel',
+      translator
+    );
     // Duplicate notebook factory to apply it on Jupytext notebooks
-    //   Mirror: https://github.com/jupyterlab/jupyterlab/blob/8a8c3752564f37493d4eb6b4c59008027fa83880/packages/notebook-extension/src/index.ts#L860
+    // Mirror: https://github.com/jupyterlab/jupyterlab/blob/8a8c3752564f37493d4eb6b4c59008027fa83880/packages/notebook-extension/src/index.ts#L860
     const factory = new NotebookWidgetFactory({
-      name: 'Jupytext Notebook',
-      label: trans.__('Jupytext Notebook'),
-      fileTypes: [
-        'markdown',
-        'myst',
-        'r-markdown',
-        'quarto',
-        'julia',
-        'python',
-        'r',
-      ],
+      name: FACTORY,
+      label: trans.__(FACTORY),
+      fileTypes: FILE_TYPES,
       modelName: notebookFactory.modelName ?? 'notebook',
       preferKernel: notebookFactory.preferKernel ?? true,
       canStartKernel: notebookFactory.canStartKernel ?? true,
@@ -624,12 +250,12 @@ const extension: JupyterFrontEndPlugin<void> = {
       toolbarFactory: toolbarFactory,
       translator,
     });
-    app.docRegistry.addWidgetFactory(factory);
+    docRegistry.addWidgetFactory(factory);
 
     // Register widget created with the new factory in the notebook tracker
-    //   This is required to activate notebook commands (and therefore shortcuts)
+    // This is required to activate notebook commands (and therefore shortcuts)
     let id = 0; // The ID counter for notebook panels.
-    const ft = app.docRegistry.getFileType('notebook');
+    const ft = docRegistry.getFileType('notebook');
 
     factory.widgetCreated.connect((sender, widget) => {
       // If the notebook panel does not have an ID, assign it one.
@@ -652,14 +278,151 @@ const extension: JupyterFrontEndPlugin<void> = {
 
     // Add support for RISE slides
     if (riseFactory) {
-      riseFactory.addFileType('markdown');
-      riseFactory.addFileType('myst');
-      riseFactory.addFileType('r-markdown');
-      riseFactory.addFileType('quarto');
-      riseFactory.addFileType('julia');
-      riseFactory.addFileType('python');
-      riseFactory.addFileType('r');
+      for (const fileType of FILE_TYPES) {
+        riseFactory.addFileType(fileType);
+      }
     }
+
+    // All supported format extensions bar ipynb, custom and none
+    // must be added to create new text notebook commands
+    const jupytextTextNotebookFormats = JUPYTEXT_FORMATS.filter(
+      (jupytextFormat: IJupytextFormat) => {
+        return !['ipynb', 'custom', 'none'].includes(jupytextFormat.format);
+      }
+    );
+
+    // Register a new command to create untitled file. This snippet is taken
+    // from docManager package https://github.com/jupyterlab/jupyterlab/blob/c106f0a19110efad7c5e1b136144985819e21100/packages/docmanager-extension/src/index.tsx#L679-L680
+    // We are "duplicating" it as base command adds extension to the options
+    // only if it is of type file. But we are interested in creating notebook type
+    // files with different Jupytext supported extensions. So we create a dedicated
+    // create new text notebook command here and make sure we pass extension in options
+    commands.addCommand(CommandIDs.newUntitled, {
+      execute: async (args) => {
+        const errorTitle = (args['error'] as string) || trans.__('Error');
+        const path =
+          typeof args['path'] === 'undefined' ? '' : (args['path'] as string);
+        const options: Partial<Contents.ICreateOptions> = {
+          type: args['type'] as Contents.ContentType,
+          path,
+        };
+
+        // Ensure we pass extension to command always
+        options.ext = (args['ext'] as string) || '.txt';
+
+        return docManager.services.contents
+          .newUntitled(options)
+          .catch((error) => showErrorMessage(errorTitle, error));
+      },
+      label: (args) =>
+        (args['label'] as string) || `New ${args['type'] as string}`,
+    });
+    palette?.addItem({
+      command: CommandIDs.newUntitled,
+      rank: 50,
+      category: 'Jupytext',
+    });
+
+    // Register all the commands that create text notebooks with different formats
+    // Nicked from notebook-extension package in JupyterLab
+    // https://github.com/jupyterlab/jupyterlab/blob/c106f0a19110efad7c5e1b136144985819e21100/packages/notebook-extension/src/index.ts#L1902-L1965
+    jupytextTextNotebookFormats.forEach(
+      (jupytextFormat: IJupytextFormat, rank: number) => {
+        const command = `jupytext:create-new-text-noteboook-${jupytextFormat.format}`;
+        const label = trans.__(jupytextFormat.label.split('with')[1].trim());
+        console.log('Registering text notebook command=', command);
+        commands.addCommand(command, {
+          label: (args) => {
+            if (args['isLauncher']) {
+              return trans.__(label);
+            }
+            if (args['isPalette'] || args['isContextMenu']) {
+              return trans.__(`New Text Notebook with ${label}`);
+            }
+            return trans.__(label);
+          },
+          caption: trans.__(`Create New Text Notebook with ${label}`),
+          icon: (args) => (args['isPalette'] ? undefined : notebookIcon),
+          execute: (args) => {
+            const cwd =
+              (args['cwd'] as string) || (defaultBrowser?.model.path ?? '');
+            const kernelId = (args['kernelId'] as string) || '';
+            const kernelName = (args['kernelName'] as string) || '';
+            return createNew(cwd, kernelId, kernelName, jupytextFormat.format);
+          },
+        });
+        palette?.addItem({
+          command,
+          args: { isPalette: true },
+          rank: rank + 51,
+          category: 'Jupytext',
+        });
+
+        // Add a launcher item if the launcher is available.
+        if (launcher && launcherItems.includes(jupytextFormat.format)) {
+          void serviceManager.ready.then(() => {
+            let disposables: DisposableSet | null = null;
+            const onSpecsChanged = () => {
+              if (disposables) {
+                disposables.dispose();
+                disposables = null;
+              }
+              const specs = serviceManager.kernelspecs.specs;
+              if (!specs) {
+                return;
+              }
+              disposables = new DisposableSet();
+
+              for (const name in specs.kernelspecs) {
+                const rank = name === specs.default ? 0 : Infinity;
+                const spec = specs.kernelspecs[name]!;
+                const kernelIconUrl =
+                  spec.resources['logo-svg'] || spec.resources['logo-64x64'];
+                disposables.add(
+                  launcher.add({
+                    command: command,
+                    args: { isLauncher: true, kernelName: name },
+                    category: trans.__(launcherItemsCategory),
+                    rank,
+                    kernelIconUrl,
+                    metadata: {
+                      kernel: JSONExt.deepCopy(
+                        spec.metadata || {}
+                      ) as ReadonlyJSONValue,
+                    },
+                  })
+                );
+              }
+            };
+            onSpecsChanged();
+            serviceManager.kernelspecs.specsChanged.connect(onSpecsChanged);
+          });
+        }
+      }
+    );
+
+    // Utility function to create a new notebook.
+    const createNew = async (
+      cwd: string,
+      kernelId: string,
+      kernelName: string,
+      format: string
+    ) => {
+      const model = await commands.execute(CommandIDs.newUntitled, {
+        path: cwd,
+        type: 'notebook',
+        ext: format.replace('auto', 'py'), // Replace auto with py
+      });
+      if (model !== undefined) {
+        const widget = (await commands.execute('docmanager:open', {
+          path: model.path,
+          factory: FACTORY,
+          kernel: { id: kernelId, name: kernelName },
+        })) as unknown as IDocumentWidget;
+        widget.isUntitled = true;
+        return widget;
+      }
+    };
   },
 };
 
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/jupytext.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/jupytext.ts
new file mode 100644
index 000000000..faab6b3d2
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/jupytext.ts
@@ -0,0 +1,373 @@
+import { showErrorMessage } from '@jupyterlab/apputils';
+
+import { INotebookTracker } from '@jupyterlab/notebook';
+
+import * as nbformat from '@jupyterlab/nbformat';
+
+import { TranslationBundle } from '@jupyterlab/translation';
+
+import {
+  LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS,
+  ALL_JUPYTEXT_FORMATS,
+  ALL_JUPYTEXT_FORMAT_EXTENSIONS,
+  IJupytextFormat,
+  IJupytextSection,
+} from './tokens';
+
+/**
+ * Get a list of all supported Jupytext formats
+ */
+export function getAvailJupytextFormats(
+  trans: TranslationBundle
+): IJupytextFormat[] {
+  return ALL_JUPYTEXT_FORMATS.map((formatObj) => {
+    return { format: formatObj.format, label: trans.__(formatObj.label) };
+  });
+}
+
+/**
+ * Get Jupytext format of current widget if it is a text notebook
+ */
+function getWidgetJupytextFormats(
+  notebookTracker: INotebookTracker
+): Array<string> {
+  const model = notebookTracker.currentWidget.context.model;
+
+  const jupytext: IJupytextSection = (model as any).getMetadata('jupytext');
+  if (!jupytext) {
+    return [];
+  }
+  const formats: Array<string> = jupytext.formats
+    ? jupytext.formats.split(',')
+    : [];
+  return formats.filter((format) => {
+    return format !== '';
+  });
+}
+
+/**
+ * Get file extension of current notebook widget
+ */
+function getNotebookFileExtension(notebookTracker: INotebookTracker): string {
+  let notebookFileExtension: string | undefined =
+    notebookTracker.currentWidget.context.path.split('.').pop();
+  if (!notebookFileExtension) {
+    return '';
+  }
+
+  notebookFileExtension = LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS.includes(
+    notebookFileExtension
+  )
+    ? notebookFileExtension
+    : 'auto';
+  return notebookFileExtension;
+}
+
+/**
+ * Get a list of all selected formats
+ */
+function getSelectedFormats(notebookTracker: INotebookTracker): Array<string> {
+  if (!notebookTracker.currentWidget) {
+    return [];
+  }
+
+  let formats = getWidgetJupytextFormats(notebookTracker);
+
+  const model = notebookTracker.currentWidget.context.model;
+
+  const languageInfo = (model as any).getMetadata(
+    'language_info'
+  ) as nbformat.ILanguageInfoMetadata;
+  if (languageInfo && languageInfo.file_extension) {
+    const scriptExt = languageInfo.file_extension.substring(1);
+    formats = formats.map((format) => {
+      // By default use light format
+      if (format === scriptExt) {
+        return 'auto:light';
+      }
+      // Replace language specific extension with auto
+      return format.replace(`${scriptExt}:`, 'auto:');
+    });
+  }
+
+  const notebookFileExtension = getNotebookFileExtension(notebookTracker);
+  if (!notebookFileExtension) {
+    return formats;
+  }
+  // Remove variant after : in format
+  const formatExtensions = formats.map((format) => {
+    return format.split(':')[0];
+  });
+  // If current notebook file extension in formats, return
+  if (formatExtensions.includes(notebookFileExtension)) {
+    return formats;
+  }
+
+  // When notebook loads for the first time, ipynb extension would not be
+  // in the formats. Here we add it and return formats
+  if (
+    LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS.includes(notebookFileExtension)
+  ) {
+    formats.push(notebookFileExtension);
+  } else {
+    const model = notebookTracker.currentWidget.context.model;
+    const jupytext: IJupytextSection = (model as any).getMetadata(
+      'jupytext'
+    ) as IJupytextSection;
+    const formatName = jupytext
+      ? jupytext?.text_representation?.formatName || 'light'
+      : 'light';
+    formats.push(`auto:${formatName}`);
+  }
+  return formats;
+}
+
+/**
+ * Toggle pair command
+ */
+export function isPairCommandToggled(
+  format: string,
+  notebookTracker: INotebookTracker
+): boolean {
+  if (!notebookTracker.currentWidget) {
+    return false;
+  }
+
+  // Get selected formats on current widget
+  const selectedFormats = getSelectedFormats(notebookTracker);
+
+  if (format === 'custom') {
+    for (const selecFormat of selectedFormats) {
+      if (!ALL_JUPYTEXT_FORMAT_EXTENSIONS.includes(selecFormat)) {
+        return true;
+      }
+    }
+    return false;
+  }
+  return selectedFormats.includes(format);
+}
+
+/**
+ * Enable pair command
+ */
+export function isPairCommandEnabled(
+  format: string,
+  notebookTracker: INotebookTracker
+): boolean {
+  if (!notebookTracker.currentWidget) {
+    return false;
+  }
+
+  const notebookFileExtension: string | undefined =
+    notebookTracker.currentWidget.context.path.split('.').pop();
+  if (format === notebookFileExtension) {
+    return false;
+  }
+
+  // Get selected formats on current widget
+  const selectedFormats = getSelectedFormats(notebookTracker);
+
+  if (format === 'none') {
+    return selectedFormats.length > 1;
+  }
+
+  return true;
+}
+
+/**
+ * Execute pair command
+ */
+export function executePairCommand(
+  command: string,
+  format: string,
+  notebookTracker: INotebookTracker,
+  trans: TranslationBundle
+): void {
+  if (!notebookTracker.currentWidget) {
+    return;
+  }
+  const model = notebookTracker.currentWidget.context.model;
+  let jupytext: IJupytextSection = (model as any).getMetadata('jupytext') as
+    | IJupytextSection
+    | undefined;
+
+  // Get selected formats on current widget
+  let selectedFormats = getSelectedFormats(notebookTracker);
+
+  // Toggle the selected format
+  console.log('Jupytext: executing command=' + command, selectedFormats);
+  if (format === 'custom') {
+    showErrorMessage(
+      trans.__('Error'),
+      trans.__(
+        'Please edit the notebook metadata directly if you wish a custom configuration.'
+      )
+    );
+    return;
+  }
+
+  // Get current notebook widget extension
+  const notebookFileExtension = getNotebookFileExtension(notebookTracker);
+
+  // Toggle the selected format
+  const index = selectedFormats.indexOf(format);
+  if (format === 'none') {
+    // Only keep one format - one that matches the current extension
+    for (const selectedFormat of selectedFormats) {
+      if (selectedFormat.split(':')[0] === notebookFileExtension) {
+        selectedFormats = [selectedFormat];
+        break;
+      }
+    }
+  } else if (index !== -1) {
+    selectedFormats.splice(index, 1);
+
+    // The current file extension can't be unpaired
+    let extFound = false;
+    for (const selectedFormat of selectedFormats) {
+      if (selectedFormat.split(':')[0] === notebookFileExtension) {
+        extFound = true;
+        break;
+      }
+    }
+
+    if (!extFound) {
+      return;
+    }
+  } else {
+    // We can't have the same extension multiple times
+    const newFormats = [];
+    for (const selectedFormat of selectedFormats) {
+      if (selectedFormat.split(':')[0] !== format.split(':')[0]) {
+        newFormats.push(selectedFormat);
+      }
+    }
+
+    selectedFormats = newFormats;
+    selectedFormats.push(format);
+  }
+
+  if (selectedFormats.length === 1) {
+    if (notebookFileExtension !== 'auto') {
+      selectedFormats = [];
+    } else if (jupytext?.text_representation) {
+      jupytext.text_representation.formatName =
+        selectedFormats[0].split(':')[1];
+      selectedFormats = [];
+    }
+  }
+
+  if (selectedFormats.length === 0) {
+    // an older version was re-fetching the jupytext metadata here
+    // but this is not necessary, as the metadata is already available
+    if (!jupytext) {
+      return;
+    }
+
+    if (jupytext.formats) {
+      delete jupytext.formats;
+    }
+    if (Object.keys(jupytext).length === 0) {
+      (model as any).deleteMetadata('jupytext');
+      (model as any).setMetadata('jupytext', jupytext);
+    }
+    return;
+  }
+
+  // set the desired format
+  if (jupytext) {
+    jupytext.formats = selectedFormats.join();
+  } else {
+    jupytext = { formats: selectedFormats.join() };
+  }
+  (model as any).setMetadata('jupytext', jupytext);
+}
+
+/**
+ * Toggle metadata command
+ */
+export function isMetadataCommandToggled(
+  notebookTracker: INotebookTracker
+): boolean {
+  if (!notebookTracker.currentWidget) {
+    return false;
+  }
+
+  const model = notebookTracker.currentWidget.context.model;
+  const jupytextMetadata = (model as any).getMetadata('jupytext');
+  if (!jupytextMetadata) {
+    return false;
+  }
+
+  const jupytext: IJupytextSection =
+    jupytextMetadata as unknown as IJupytextSection;
+
+  if (jupytext.notebook_metadata_filter === '-all') {
+    return false;
+  }
+
+  return true;
+}
+
+/**
+ * Enable metadata command
+ */
+export function isMetadataCommandEnabled(
+  notebookTracker: INotebookTracker
+): boolean {
+  if (!notebookTracker.currentWidget) {
+    return false;
+  }
+
+  const model = notebookTracker.currentWidget.context.model;
+  const jupytextMetadata = (model as any).getMetadata('jupytext');
+  if (!jupytextMetadata) {
+    return false;
+  }
+
+  const jupytext: IJupytextSection =
+    jupytextMetadata as unknown as IJupytextSection;
+
+  if (jupytext.notebook_metadata_filter === undefined) {
+    return true;
+  }
+
+  if (jupytext.notebook_metadata_filter === '-all') {
+    return true;
+  }
+
+  return false;
+}
+
+/**
+ * Execute metadata command
+ */
+export function executeMetadataCommand(
+  notebookTracker: INotebookTracker
+): void {
+  console.log('Jupytext: toggling YAML header');
+  if (!notebookTracker.currentWidget) {
+    return;
+  }
+
+  const model = notebookTracker.currentWidget.context.model;
+  const jupytextMetadata = (model as any).getMetadata('jupytext');
+  if (!jupytextMetadata) {
+    return;
+  }
+
+  const jupytext = ((jupytextMetadata as unknown) ?? {}) as IJupytextSection;
+
+  if (jupytext.notebook_metadata_filter) {
+    delete jupytext.notebook_metadata_filter;
+    if (jupytext.notebook_metadata_filter === '-all') {
+      delete jupytext.notebook_metadata_filter;
+    }
+  } else {
+    jupytext.notebook_metadata_filter = '-all';
+    if (jupytext.notebook_metadata_filter === undefined) {
+      jupytext.notebook_metadata_filter = '-all';
+    }
+  }
+  (model as any).setMetadata('jupytext', jupytext);
+}
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/tokens.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/tokens.ts
new file mode 100644
index 000000000..2a3c22cbe
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/tokens.ts
@@ -0,0 +1,149 @@
+/**
+ * Jupytext extension ID
+ */
+export const JUPYTEXT_EXTENSION_ID = 'jupyterlab-jupytext:plugin';
+
+/**
+ * Jupytext widget factory
+ */
+export const FACTORY = 'Jupytext Notebook';
+
+/**
+ * Supported file formats.
+ */
+export const LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS = [
+  'ipynb',
+  'md',
+  'Rmd',
+  'qmd',
+];
+
+/**
+ * Supported file types.
+ */
+export const FILE_TYPES = [
+  'markdown',
+  'myst',
+  'r-markdown',
+  'quarto',
+  'julia',
+  'python',
+  'r',
+];
+
+/**
+ * The short namespace for commands, etc.
+ */
+export const NS = 'jupytext';
+
+/**
+ * Command IDs of Jupytext
+ */
+export namespace CommandIDs {
+  export const metadata = `${NS}:metadata`;
+  export const reference = `${NS}:reference`;
+  export const faq = `${NS}:faq`;
+  export const newUntitled = `${NS}:new-untitled-text-notebook`;
+}
+
+/**
+ * Supported Jupytext formats with metadata.
+ */
+export const ALL_JUPYTEXT_FORMATS = [
+  {
+    format: 'ipynb',
+    label: 'Pair Notebook with ipynb document',
+  },
+  {
+    format: 'auto:light',
+    label: 'Pair Notebook with Light Script',
+  },
+  {
+    format: 'auto:percent',
+    label: 'Pair Notebook with Percent Script',
+  },
+  {
+    format: 'auto:hydrogen',
+    label: 'Pair Notebook with Hydrogen Script',
+  },
+  {
+    format: 'auto:nomarker',
+    label: 'Pair Notebook with Nomarker Script',
+  },
+  {
+    format: 'md',
+    label: 'Pair Notebook with Markdown',
+  },
+  {
+    format: 'md:myst',
+    label: 'Pair Notebook with MyST Markdown',
+  },
+  {
+    format: 'Rmd',
+    label: 'Pair Notebook with R Markdown',
+  },
+  {
+    format: 'qmd',
+    label: 'Pair Notebook with Quarto (qmd)',
+  },
+  {
+    format: 'custom',
+    label: 'Custom pairing',
+  },
+  {
+    format: 'none',
+    label: 'Unpair Notebook',
+  },
+];
+
+/**
+ * Supported Jupytext format extensions bar custom and none
+ */
+export const ALL_JUPYTEXT_FORMAT_EXTENSIONS = ALL_JUPYTEXT_FORMATS.map(
+  (formatObj) => {
+    return formatObj.format;
+  }
+).filter((format) => {
+  return !['custom', 'none'].includes(format);
+});
+
+/**
+ * List of formats that would be added to launcher icons
+ */
+export const TEXT_NOTEBOOKS_LAUNCHER_ICONS =
+  ALL_JUPYTEXT_FORMAT_EXTENSIONS.filter((format) => {
+    return ![
+      'ipynb',
+      'auto:hydrogen',
+      'auto:nomarker',
+      'qmd',
+      'custom',
+      'none',
+    ].includes(format);
+  });
+
+/**
+ * An interface for Jupytext format
+ */
+export interface IJupytextFormat {
+  format: string;
+  label: string;
+}
+
+/**
+ * An interface for Jupytext representation
+ */
+export interface IJupytextRepresentation {
+  formatName: string;
+  extension: string;
+}
+
+/**
+ * An interface for Jupytext metadata
+ */
+export interface IJupytextSection {
+  formats?: string;
+  notebook_metadata_filter?: string;
+  cell_metadata_filer?: string;
+  text_representation?: IJupytextRepresentation;
+}
diff --git a/jupyterlab/scripts/install_extension.py b/jupyterlab/scripts/install_extension.py
new file mode 100644
index 000000000..3c3133044
--- /dev/null
+++ b/jupyterlab/scripts/install_extension.py
@@ -0,0 +1,48 @@
+# This script does the "same" job as `jupyter labextension develop --overwrite .`
+#
+# Seems like we cannot use the upstream script on our repo organization.
+# If we want to use the upstream script we need to have a setup.py or pyproject.toml
+# in jupyterlab/jupyterlab-jupytext folder with the name of the package.
+#
+# We are only interested in making a symlink to the
+# sys.prefix/share/jupyter/labextensions folder for developement. We should be able
+# to do it in simple script
+#
+
+import os
+
+import jupyterlab_jupytext
+from jupyterlab.federated_labextensions import build_labextension, develop_labextension
+
+
+def main():
+    """Create symlink in sys.prefix based on name of extension"""
+
+    labexts = jupyterlab_jupytext._jupyter_labextension_paths()
+    base_path = os.path.join(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+        "jupyterlab_jupytext",
+    )
+
+    for labext in labexts:
+        src = os.path.join(base_path, labext["src"])
+        dest = labext["dest"]
+        print(f"Installing {src} -> {dest}")
+
+        if not os.path.exists(src):
+            build_labextension(base_path)
+
+        full_dest = develop_labextension(
+            src,
+            overwrite=True,
+            symlink=True,
+            user=False,
+            sys_prefix=True,
+            labextensions_dir="",
+            destination=dest,
+        )
+        print(f"Creating symlink at {full_dest}")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/jupyterlab/tsconfig.eslint.json b/jupyterlab/tsconfig.eslint.json
index 02079091c..0075cb598 100644
--- a/jupyterlab/tsconfig.eslint.json
+++ b/jupyterlab/tsconfig.eslint.json
@@ -14,5 +14,6 @@
     "types": [],
     "esModuleInterop": true
   },
+  "extends": "../../tsconfig.eslint.json",
   "include": ["**/src/*"]
 }
diff --git a/jupyterlab/yarn.lock b/jupyterlab/yarn.lock
index 1148728fe..4bd48a121 100644
--- a/jupyterlab/yarn.lock
+++ b/jupyterlab/yarn.lock
@@ -231,8 +231,8 @@ __metadata:
   linkType: hard
 
 "@codemirror/language@npm:^6.0.0, @codemirror/language@npm:^6.3.0, @codemirror/language@npm:^6.4.0, @codemirror/language@npm:^6.6.0, @codemirror/language@npm:^6.8.0":
-  version: 6.9.1
-  resolution: "@codemirror/language@npm:6.9.1"
+  version: 6.9.2
+  resolution: "@codemirror/language@npm:6.9.2"
   dependencies:
     "@codemirror/state": ^6.0.0
     "@codemirror/view": ^6.0.0
@@ -240,7 +240,7 @@ __metadata:
     "@lezer/highlight": ^1.0.0
     "@lezer/lr": ^1.0.0
     style-mod: ^4.0.0
-  checksum: 62265f1042d2edfd3a091c408d9d0071f23889099b2f6ce8275fa910118bd2c45b8c4b29228c7be6e6d5f0e0812a522de902bc75ba8d8b2e62e42ade1692a49a
+  checksum: eee7b861b5591114cac7502cd532d5b923639740081a4cd7e28696c252af8d759b14686aaf6d5eee7e0969ff647b7aaf03a5eea7235fb6d9858ee19433f1c74d
   languageName: node
   linkType: hard
 
@@ -283,13 +283,13 @@ __metadata:
   linkType: hard
 
 "@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.9.6":
-  version: 6.21.3
-  resolution: "@codemirror/view@npm:6.21.3"
+  version: 6.22.0
+  resolution: "@codemirror/view@npm:6.22.0"
   dependencies:
     "@codemirror/state": ^6.1.4
     style-mod: ^4.1.0
     w3c-keyname: ^2.2.4
-  checksum: 7fda5a60e04fe1ac3d22ee478d4a90fc307953b8c900752ef5ca33af06c4e7851356e460f14b05034230b3a1677f36379ea01d85a3ea3b3a3e85e871ed62346a
+  checksum: 2a24674687fbde06898d0a131abe5f86a812d79e111cf8dc94110dac86eed8c20a2094b547c1b3c379fe8edf0c66318d03a7594158e4f6628ee060a03a5d1bab
   languageName: node
   linkType: hard
 
@@ -321,15 +321,15 @@ __metadata:
   linkType: hard
 
 "@eslint-community/regexpp@npm:^4.4.0, @eslint-community/regexpp@npm:^4.6.1":
-  version: 4.9.1
-  resolution: "@eslint-community/regexpp@npm:4.9.1"
-  checksum: 06fb839e9c756f6375cc545c2f2e05a0a64576bd6370e8e3c07983fd29a3d6e164ef4aa48a361f7d27e6713ab79c83053ff6a2ccb78748bc955e344279c4a3b6
+  version: 4.10.0
+  resolution: "@eslint-community/regexpp@npm:4.10.0"
+  checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b
   languageName: node
   linkType: hard
 
-"@eslint/eslintrc@npm:^2.1.2":
-  version: 2.1.2
-  resolution: "@eslint/eslintrc@npm:2.1.2"
+"@eslint/eslintrc@npm:^2.1.3":
+  version: 2.1.3
+  resolution: "@eslint/eslintrc@npm:2.1.3"
   dependencies:
     ajv: ^6.12.4
     debug: ^4.3.2
@@ -340,14 +340,14 @@ __metadata:
     js-yaml: ^4.1.0
     minimatch: ^3.1.2
     strip-json-comments: ^3.1.1
-  checksum: bc742a1e3b361f06fedb4afb6bf32cbd27171292ef7924f61c62f2aed73048367bcc7ac68f98c06d4245cd3fabc43270f844e3c1699936d4734b3ac5398814a7
+  checksum: 5c6c3878192fe0ddffa9aff08b4e2f3bcc8f1c10d6449b7295a5f58b662019896deabfc19890455ffd7e60a5bd28d25d0eaefb2f78b2d230aae3879af92b89e5
   languageName: node
   linkType: hard
 
-"@eslint/js@npm:8.51.0":
-  version: 8.51.0
-  resolution: "@eslint/js@npm:8.51.0"
-  checksum: 0228bf1e1e0414843e56d9ff362a2a72d579c078f93174666f29315690e9e30a8633ad72c923297f7fd7182381b5a476805ff04dac8debe638953eb1ded3ac73
+"@eslint/js@npm:8.53.0":
+  version: 8.53.0
+  resolution: "@eslint/js@npm:8.53.0"
+  checksum: e0d5cfb0000aaee237c8e6d6d6e366faa60b1ef7f928ce17778373aa44d3b886368f6d5e1f97f913f0f16801aad016db8b8df78418c9d18825c15590328028af
   languageName: node
   linkType: hard
 
@@ -365,14 +365,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@humanwhocodes/config-array@npm:^0.11.11":
-  version: 0.11.11
-  resolution: "@humanwhocodes/config-array@npm:0.11.11"
+"@humanwhocodes/config-array@npm:^0.11.13":
+  version: 0.11.13
+  resolution: "@humanwhocodes/config-array@npm:0.11.13"
   dependencies:
-    "@humanwhocodes/object-schema": ^1.2.1
+    "@humanwhocodes/object-schema": ^2.0.1
     debug: ^4.1.1
     minimatch: ^3.0.5
-  checksum: db84507375ab77b8ffdd24f498a5b49ad6b64391d30dd2ac56885501d03964d29637e05b1ed5aefa09d57ac667e28028bc22d2da872bfcd619652fbdb5f4ca19
+  checksum: f8ea57b0d7ed7f2d64cd3944654976829d9da91c04d9c860e18804729a33f7681f78166ef4c761850b8c324d362f7d53f14c5c44907a6b38b32c703ff85e4805
   languageName: node
   linkType: hard
 
@@ -383,10 +383,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@humanwhocodes/object-schema@npm:^1.2.1":
-  version: 1.2.1
-  resolution: "@humanwhocodes/object-schema@npm:1.2.1"
-  checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1
+"@humanwhocodes/object-schema@npm:^2.0.1":
+  version: 2.0.1
+  resolution: "@humanwhocodes/object-schema@npm:2.0.1"
+  checksum: 24929487b1ed48795d2f08346a0116cc5ee4634848bce64161fb947109352c562310fd159fc64dda0e8b853307f5794605191a9547f7341158559ca3c8262a45
   languageName: node
   linkType: hard
 
@@ -493,20 +493,20 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@jupyterlab/application@npm:^4.0.0, @jupyterlab/application@npm:^4.0.2":
-  version: 4.0.7
-  resolution: "@jupyterlab/application@npm:4.0.7"
+"@jupyterlab/application@npm:^4.0.0, @jupyterlab/application@npm:^4.0.2, @jupyterlab/application@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/application@npm:4.0.8"
   dependencies:
     "@fortawesome/fontawesome-free": ^5.12.0
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/docregistry": ^4.0.7
-    "@jupyterlab/rendermime": ^4.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/statedb": ^4.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/statedb": ^4.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/application": ^2.2.1
     "@lumino/commands": ^2.1.3
@@ -517,23 +517,23 @@ __metadata:
     "@lumino/properties": ^2.0.1
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
-  checksum: 4684edfcf7dfe9724e86938cf50a45a3518650dba3535bea9d13e024dcc9cd80a5862d2c1564b6498f6f086253766c0952eded677c93ce56b8b7265d739892c4
+  checksum: e6c50720992d50f8d6151752a31bf8dda2a21e912e896bbe9037f72086bddb515836fb9554603df8773313b27f45a39e01bda7e7b75cb2aca70ef15bcae1bc5e
   languageName: node
   linkType: hard
 
-"@jupyterlab/apputils@npm:^4.0.0, @jupyterlab/apputils@npm:^4.0.2, @jupyterlab/apputils@npm:^4.1.7":
-  version: 4.1.7
-  resolution: "@jupyterlab/apputils@npm:4.1.7"
-  dependencies:
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/settingregistry": ^4.0.7
-    "@jupyterlab/statedb": ^4.0.7
-    "@jupyterlab/statusbar": ^4.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+"@jupyterlab/apputils@npm:^4.0.0, @jupyterlab/apputils@npm:^4.0.2, @jupyterlab/apputils@npm:^4.1.8":
+  version: 4.1.8
+  resolution: "@jupyterlab/apputils@npm:4.1.8"
+  dependencies:
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/settingregistry": ^4.0.8
+    "@jupyterlab/statedb": ^4.0.8
+    "@jupyterlab/statusbar": ^4.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/commands": ^2.1.3
     "@lumino/coreutils": ^2.1.2
@@ -546,27 +546,27 @@ __metadata:
     "@types/react": ^18.0.26
     react: ^18.2.0
     sanitize-html: ~2.7.3
-  checksum: d8a3739ea4b74244b0e14e6a9bced973cc2fc8eb645fe25d36da960e3413492c5451332f44975ba601daecbe6b1e80073f36860f65482da16e94ed24f11a5947
+  checksum: 1b028893ac0358d9f90585edd5fbb89a4fe251c31789cf6d809fb316b91c958c6ba33884d463dbe78dfdd864b579535e1e1849bcb9b16853002271a71418d31e
   languageName: node
   linkType: hard
 
-"@jupyterlab/attachments@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/attachments@npm:4.0.7"
+"@jupyterlab/attachments@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/attachments@npm:4.0.8"
   dependencies:
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime": ^4.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
     "@lumino/disposable": ^2.1.2
     "@lumino/signaling": ^2.1.2
-  checksum: ff118f55b8fbf08d112aef9f1f9867a6310578afacff9953af3c30205d338ed88bc44204112597bd325bc6b2eeb88e5f901187628e869853c9e9b5c2b77e4eb8
+  checksum: 17e14257fb28006144bd7824e9f78e5c4c792c9dfa60e67efdd0bb6e6e33109b52e1e10b18d8a76f4d8e5e8d4ab9a22254e92f13d8c8f9ed0803961f5de8ece0
   languageName: node
   linkType: hard
 
 "@jupyterlab/builder@npm:^4.0.0":
-  version: 4.0.7
-  resolution: "@jupyterlab/builder@npm:4.0.7"
+  version: 4.0.8
+  resolution: "@jupyterlab/builder@npm:4.0.8"
   dependencies:
     "@lumino/algorithm": ^2.0.1
     "@lumino/application": ^2.2.1
@@ -601,32 +601,32 @@ __metadata:
     worker-loader: ^3.0.2
   bin:
     build-labextension: lib/build-labextension.js
-  checksum: 67b034c7843a41f63b314304a224480583d02b4d958fd874b3ea4b7fd9a2ec8df110edaaf0379937a7a1850cb19cf1178fbbadfe535f3dbd9acdc0c3a96b8f8a
+  checksum: 9a1feeba36ba85ac0f1538f8df1b5a2140e5d1786530b7351880b8fb45b2902e961a48c2625d619c0b5c09b68299d9fea045adf139e439fd0f7f3cce41794662
   languageName: node
   linkType: hard
 
-"@jupyterlab/cells@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/cells@npm:4.0.7"
+"@jupyterlab/cells@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/cells@npm:4.0.8"
   dependencies:
     "@codemirror/state": ^6.2.0
     "@codemirror/view": ^6.9.6
     "@jupyter/ydoc": ^1.0.2
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/attachments": ^4.0.7
-    "@jupyterlab/codeeditor": ^4.0.7
-    "@jupyterlab/codemirror": ^4.0.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/documentsearch": ^4.0.7
-    "@jupyterlab/filebrowser": ^4.0.7
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/outputarea": ^4.0.7
-    "@jupyterlab/rendermime": ^4.0.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/toc": ^6.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/attachments": ^4.0.8
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/codemirror": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/documentsearch": ^4.0.8
+    "@jupyterlab/filebrowser": ^4.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/outputarea": ^4.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/toc": ^6.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/domutils": ^2.0.1
@@ -637,22 +637,22 @@ __metadata:
     "@lumino/virtualdom": ^2.0.1
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: 3b986c3fb734031ce998e7a67208d06b0c0892a972db1d8123767bdcc9e14109f7e79be3f116f788bcfc2194e7a5a14d5918671c9021b9de51e82ca7f0421436
+  checksum: 42e4464574a603f48df299ccf2d84bf5f90f0aa39b0a9c680c9db5d254b416ee784ec3b5a1475ed72a10e6dcf1075715d66bf3afeee55e365c42656daea3f002
   languageName: node
   linkType: hard
 
-"@jupyterlab/codeeditor@npm:^4.0.0, @jupyterlab/codeeditor@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/codeeditor@npm:4.0.7"
+"@jupyterlab/codeeditor@npm:^4.0.0, @jupyterlab/codeeditor@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/codeeditor@npm:4.0.8"
   dependencies:
     "@codemirror/state": ^6.2.0
     "@jupyter/ydoc": ^1.0.2
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/statusbar": ^4.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/statusbar": ^4.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/dragdrop": ^2.1.3
@@ -660,13 +660,13 @@ __metadata:
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: d6c1c072b77f0afdc4c61ed9392297b43afa5ef0a3279e05631ead870122f9195eb9d5b6182b1ee984aa4fa7aee56051e710d601c550e43af27d43fc3397c333
+  checksum: 151be40c60bcedf463d01b9c53466afc4b4747dd341fb4d5c2b9fc8b3c181af2ba391f867e10e78bb948848cdd300139b4b112634dec9f8aac9c36c5a13e1654
   languageName: node
   linkType: hard
 
-"@jupyterlab/codemirror@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/codemirror@npm:4.0.7"
+"@jupyterlab/codemirror@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/codemirror@npm:4.0.8"
   dependencies:
     "@codemirror/autocomplete": ^6.5.1
     "@codemirror/commands": ^6.2.3
@@ -689,11 +689,11 @@ __metadata:
     "@codemirror/state": ^6.2.0
     "@codemirror/view": ^6.9.6
     "@jupyter/ydoc": ^1.0.2
-    "@jupyterlab/codeeditor": ^4.0.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/documentsearch": ^4.0.7
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/translation": ^4.0.7
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/documentsearch": ^4.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/translation": ^4.0.8
     "@lezer/common": ^1.0.2
     "@lezer/generator": ^1.2.2
     "@lezer/highlight": ^1.1.4
@@ -702,13 +702,40 @@ __metadata:
     "@lumino/disposable": ^2.1.2
     "@lumino/signaling": ^2.1.2
     yjs: ^13.5.40
-  checksum: 8b813dc5144a5adbfd535fe4c817ba96a2c123e60999674ea60ac207fa2b7d06d34314b46bf07564b9c6ca3c21077c5ee34279a857c9191b3133a488f0bf1c22
+  checksum: 2edd2ac9d695f8107d444379289b4cecf3603c118d748608738abe2a1af9d311d0407c5709ccbe016dd0a16c3a52d7f0132446173678246841c8ddf8ade371c7
+  languageName: node
+  linkType: hard
+
+"@jupyterlab/console@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/console@npm:4.0.8"
+  dependencies:
+    "@codemirror/state": ^6.2.0
+    "@codemirror/view": ^6.9.6
+    "@jupyter/ydoc": ^1.0.2
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/cells": ^4.0.8
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
+    "@lumino/coreutils": ^2.1.2
+    "@lumino/disposable": ^2.1.2
+    "@lumino/dragdrop": ^2.1.3
+    "@lumino/messaging": ^2.0.1
+    "@lumino/signaling": ^2.1.2
+    "@lumino/widgets": ^2.3.0
+  checksum: 0d8461167377dfecd9d42427c1941fa932877ceae839d6c7043a690e71a675f7e1979c49bb1b1c7124bfe74f3351d5ff35845c6e9585f8e79fa9221f7ad74a54
   languageName: node
   linkType: hard
 
-"@jupyterlab/coreutils@npm:^6.0.2, @jupyterlab/coreutils@npm:^6.0.7":
-  version: 6.0.7
-  resolution: "@jupyterlab/coreutils@npm:6.0.7"
+"@jupyterlab/coreutils@npm:^6.0.2, @jupyterlab/coreutils@npm:^6.0.8":
+  version: 6.0.8
+  resolution: "@jupyterlab/coreutils@npm:6.0.8"
   dependencies:
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
@@ -716,21 +743,58 @@ __metadata:
     minimist: ~1.2.0
     path-browserify: ^1.0.0
     url-parse: ~1.5.4
-  checksum: 18a14e0bc957bf087c3de3e86c5dc7ee568027906edf5dc820d9c794af6c9dece84d0b396e837786849f9144bb156746e3d4f2e838fd023a42eee94ebeb9014f
+  checksum: b56e3b95c0ce52745b79549ef5b18a27e620086b87cf997b3a743b59d18dc529e403c812751b7e294a4abc60ac957381301e14327e1a4b9c1afb232f181f3a4d
   languageName: node
   linkType: hard
 
-"@jupyterlab/docmanager@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/docmanager@npm:4.0.7"
-  dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/docregistry": ^4.0.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/statusbar": ^4.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+"@jupyterlab/debugger@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/debugger@npm:4.0.8"
+  dependencies:
+    "@codemirror/state": ^6.2.0
+    "@codemirror/view": ^6.9.6
+    "@jupyter/ydoc": ^1.0.2
+    "@jupyterlab/application": ^4.0.8
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/cells": ^4.0.8
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/codemirror": ^4.0.8
+    "@jupyterlab/console": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/fileeditor": ^4.0.8
+    "@jupyterlab/notebook": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
+    "@lumino/algorithm": ^2.0.1
+    "@lumino/commands": ^2.1.3
+    "@lumino/coreutils": ^2.1.2
+    "@lumino/datagrid": ^2.2.0
+    "@lumino/disposable": ^2.1.2
+    "@lumino/messaging": ^2.0.1
+    "@lumino/polling": ^2.1.2
+    "@lumino/signaling": ^2.1.2
+    "@lumino/widgets": ^2.3.0
+    "@vscode/debugprotocol": ^1.51.0
+    react: ^18.2.0
+  checksum: 54d9f065f22f355e13300ba4578c328f715148faee855a62794c8f2f1662f6ef6e74e8133a524d16b6feede8a765efaad1d3c9dcb2178f238da913601c4e7535
+  languageName: node
+  linkType: hard
+
+"@jupyterlab/docmanager@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/docmanager@npm:4.0.8"
+  dependencies:
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/statusbar": ^4.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
@@ -739,24 +803,24 @@ __metadata:
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: 4ccbcfa431563cb0cdfa12d0f1ffed107816b8bcd420de5b6dc85e6c124ff1f691e72ce421102663880dc340717bfb71bdceb25eb8fc4074e08adb58ae6ba371
+  checksum: 70eea965bb9edd6a4042c92d2cb98f8b1b145b6727a354e12e3f5ab84ff2ab7207c5d9433c43c03d01f4377f9dc901359d789d0f47d2c725e56f41c487295550
   languageName: node
   linkType: hard
 
-"@jupyterlab/docregistry@npm:^4.0.2, @jupyterlab/docregistry@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/docregistry@npm:4.0.7"
+"@jupyterlab/docregistry@npm:^4.0.0, @jupyterlab/docregistry@npm:^4.0.2, @jupyterlab/docregistry@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/docregistry@npm:4.0.8"
   dependencies:
     "@jupyter/ydoc": ^1.0.2
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/codeeditor": ^4.0.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime": ^4.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
@@ -764,17 +828,17 @@ __metadata:
     "@lumino/properties": ^2.0.1
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
-  checksum: 1d420696305dc17b2e96b22bf31af2caf2b16e31529c57b824bf859c71ac5caecb5a0a00d32ebc34ca1af65f720cec2c442d786c0460da60d7f65deb402dd891
+  checksum: 280697f97ca146cc711c5dafa1b27eaa89d96bc53fc92ade7d4b78c44c997feb9d2495b392c31e75ed3c836797865e2f3fa6ea8f3207f46a4ab2d26061dc9498
   languageName: node
   linkType: hard
 
-"@jupyterlab/documentsearch@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/documentsearch@npm:4.0.7"
+"@jupyterlab/documentsearch@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/documentsearch@npm:4.0.8"
   dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/messaging": ^2.0.1
@@ -782,23 +846,23 @@ __metadata:
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: 96f51844b22a2c8e234c85e32915a9af41a54d5bd21a49de63d37181083089c84d18265c14d7d8d5adeb460771ba044e87caafdb82fd0e805837a23d56aa2fe3
+  checksum: 5ee4c4b910af158b4ca488c617b12b2a84d391cfb6be94a38136a1eb80f639ec4b446fd862748a76732bc3eccd290750c0e9f6b6211d3c15d0776073173a5343
   languageName: node
   linkType: hard
 
-"@jupyterlab/filebrowser@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/filebrowser@npm:4.0.7"
-  dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/docmanager": ^4.0.7
-    "@jupyterlab/docregistry": ^4.0.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/statedb": ^4.0.7
-    "@jupyterlab/statusbar": ^4.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+"@jupyterlab/filebrowser@npm:^4.0.0, @jupyterlab/filebrowser@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/filebrowser@npm:4.0.8"
+  dependencies:
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docmanager": ^4.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/statedb": ^4.0.8
+    "@jupyterlab/statusbar": ^4.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
@@ -810,20 +874,90 @@ __metadata:
     "@lumino/virtualdom": ^2.0.1
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: 586b8a07fbe0a9bb0b0cd13a9d6fb083e797831a41fc5273d70124bb2daeeeb641e6b4584fc752a4799a5961bb14acc1379fd09847ef7f38b2908516b9f254e3
+  checksum: 907dade3b9ab6bde667cdf2acc76c0fb2d631c06d794115a456cb6b1995a3b89b2a9f5c53a75824b337833cd05967c55fd919ca1311f24279aa5f067f948378a
   languageName: node
   linkType: hard
 
-"@jupyterlab/lsp@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/lsp@npm:4.0.7"
-  dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/codeeditor": ^4.0.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/docregistry": ^4.0.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/translation": ^4.0.7
+"@jupyterlab/fileeditor@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/fileeditor@npm:4.0.8"
+  dependencies:
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/codemirror": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/documentsearch": ^4.0.8
+    "@jupyterlab/lsp": ^4.0.8
+    "@jupyterlab/statusbar": ^4.0.8
+    "@jupyterlab/toc": ^6.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
+    "@lumino/commands": ^2.1.3
+    "@lumino/coreutils": ^2.1.2
+    "@lumino/messaging": ^2.0.1
+    "@lumino/widgets": ^2.3.0
+    react: ^18.2.0
+    regexp-match-indices: ^1.0.2
+  checksum: 0e31402e889f5519a380ab62f46efaff6787be03736daec46d4608f127a9335304963e2a20f37a3269a4aec7d358eee70a01fe8446968f0df96711b9e9cb9123
+  languageName: node
+  linkType: hard
+
+"@jupyterlab/galata@npm:^5.0.0":
+  version: 5.0.8
+  resolution: "@jupyterlab/galata@npm:5.0.8"
+  dependencies:
+    "@jupyterlab/application": ^4.0.8
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/debugger": ^4.0.8
+    "@jupyterlab/docmanager": ^4.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/notebook": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/settingregistry": ^4.0.8
+    "@lumino/coreutils": ^2.1.2
+    "@playwright/test": ^1.32.2
+    "@stdlib/stats": ~0.0.13
+    fs-extra: ^10.1.0
+    json5: ^2.2.3
+    path: ~0.12.7
+    systeminformation: ^5.8.6
+    vega: ^5.20.0
+    vega-lite: ^5.6.1
+    vega-statistics: ^1.7.9
+  checksum: 7874d8e54abb3d449e0a3962726101738e40a760ee047f20485567113bb2fc76d43f9d9e86c84a7b7a72dd8e0ae32c3f62a8ad025336b95f42bc3c89a547a7b5
+  languageName: node
+  linkType: hard
+
+"@jupyterlab/launcher@npm:^4.0.0":
+  version: 4.0.8
+  resolution: "@jupyterlab/launcher@npm:4.0.8"
+  dependencies:
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
+    "@lumino/algorithm": ^2.0.1
+    "@lumino/commands": ^2.1.3
+    "@lumino/coreutils": ^2.1.2
+    "@lumino/disposable": ^2.1.2
+    "@lumino/properties": ^2.0.1
+    "@lumino/widgets": ^2.3.0
+    react: ^18.2.0
+  checksum: 90d64e9a7ff8ceb936a1dc04cb5639bd1f792bb8d1c587a19cb36bfb0d3ca37ea6ce4e30d65eb0a0800466634be33daae0c2af81311afd19ad5c5cffec8a4b2b
+  languageName: node
+  linkType: hard
+
+"@jupyterlab/lsp@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/lsp@npm:4.0.8"
+  dependencies:
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/translation": ^4.0.8
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/signaling": ^2.1.2
@@ -831,56 +965,56 @@ __metadata:
     vscode-jsonrpc: ^6.0.0
     vscode-languageserver-protocol: ^3.17.0
     vscode-ws-jsonrpc: ~1.0.2
-  checksum: a038fb51648b082652850e8a7190e0b9726be3be3b478258954a7a119df78df1b97182c53a4c8e6adb3ca22dbeaf2f5a40935b916a7dccb99952ebe44e112d9c
+  checksum: baf2d42800a617a9d06d8cbb9c755e0cc40994c25c7c5d31bca1b7ac4fc4ad67d77fadb53de020e52d499a46e41dea1d84abb388b9bc20d468a5e888c687f301
   languageName: node
   linkType: hard
 
 "@jupyterlab/mainmenu@npm:^4.0.2":
-  version: 4.0.7
-  resolution: "@jupyterlab/mainmenu@npm:4.0.7"
+  version: 4.0.8
+  resolution: "@jupyterlab/mainmenu@npm:4.0.8"
   dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/commands": ^2.1.3
     "@lumino/coreutils": ^2.1.2
     "@lumino/widgets": ^2.3.0
-  checksum: 7ed86d4c2a02d38b7ef5a1544528729e35b92a5393db509564122922736aeb7f0332688f2643e519324dd9a3314762ba6afc453a50ca64bc03ff6b9fe585a668
+  checksum: 761e625450d9e56ca3f23ebbe5e2d2af7c12d2e12cb2dd9823946b8b55596fa30393cb6ce3e00d2fcb991d6757535195387112cc97e382a2b94faa028a581d80
   languageName: node
   linkType: hard
 
-"@jupyterlab/nbformat@npm:^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0, @jupyterlab/nbformat@npm:^4.0.0, @jupyterlab/nbformat@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/nbformat@npm:4.0.7"
+"@jupyterlab/nbformat@npm:^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0, @jupyterlab/nbformat@npm:^4.0.0, @jupyterlab/nbformat@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/nbformat@npm:4.0.8"
   dependencies:
     "@lumino/coreutils": ^2.1.2
-  checksum: 32a14a6a3e6d068fa34aec385090531100170480869681156dfb510ea9154141277e678031a0df770af8ae5a0f06dc7c00570089c9187485552e1aeba5130ca8
+  checksum: 2d8255ac7c7c20dbfa8497ce4d8d2f5840568adefb2feaec8eb8ddbb4892f50706ce60e8c4719113485c5523f720802f7e4e7b63ed43fac90f870ff1134bed7a
   languageName: node
   linkType: hard
 
-"@jupyterlab/notebook@npm:^4.0.0, @jupyterlab/notebook@npm:^4.0.2":
-  version: 4.0.7
-  resolution: "@jupyterlab/notebook@npm:4.0.7"
+"@jupyterlab/notebook@npm:^4.0.0, @jupyterlab/notebook@npm:^4.0.2, @jupyterlab/notebook@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/notebook@npm:4.0.8"
   dependencies:
     "@jupyter/ydoc": ^1.0.2
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/cells": ^4.0.7
-    "@jupyterlab/codeeditor": ^4.0.7
-    "@jupyterlab/codemirror": ^4.0.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/docregistry": ^4.0.7
-    "@jupyterlab/documentsearch": ^4.0.7
-    "@jupyterlab/lsp": ^4.0.7
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime": ^4.0.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/settingregistry": ^4.0.7
-    "@jupyterlab/statusbar": ^4.0.7
-    "@jupyterlab/toc": ^6.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/cells": ^4.0.8
+    "@jupyterlab/codeeditor": ^4.0.8
+    "@jupyterlab/codemirror": ^4.0.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/documentsearch": ^4.0.8
+    "@jupyterlab/lsp": ^4.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/settingregistry": ^4.0.8
+    "@jupyterlab/statusbar": ^4.0.8
+    "@jupyterlab/toc": ^6.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/domutils": ^2.0.1
@@ -891,34 +1025,34 @@ __metadata:
     "@lumino/virtualdom": ^2.0.1
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: 75fe89a1c59d47cb861a66b1c36d5e22593e93b8e0f8b3195e43e79e67e87542ccc00f245f3cdbd55617f889f1f7baa0a868d6be7d8fcfdc6ccab53e93f69bf4
+  checksum: 64ea11e1923fcae46d263c732cfa6a69a51099bdd637f40599da5359fb884600179a3c5b6f3c176c6ffa725f425b33f435be6391f8d087b443e5fc6d6d11ced2
   languageName: node
   linkType: hard
 
-"@jupyterlab/observables@npm:^5.0.7":
-  version: 5.0.7
-  resolution: "@jupyterlab/observables@npm:5.0.7"
+"@jupyterlab/observables@npm:^5.0.8":
+  version: 5.0.8
+  resolution: "@jupyterlab/observables@npm:5.0.8"
   dependencies:
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/messaging": ^2.0.1
     "@lumino/signaling": ^2.1.2
-  checksum: 459ec3ec77a12534cd16864892d8d3af3ead32a56956daeb89ab68e16c53651c8f20021e088e5a601b214eed46398bbbaea8bc3dc23f23b2700660558fa7c317
+  checksum: 833c6af7f66a338d53e4ebfae2c10c57a55b8a1710730eed89e7a0103a4dd27b7b5634d0e7cf9c7db47d891fd4c8e72235de9816833482ef68356846200613be
   languageName: node
   linkType: hard
 
-"@jupyterlab/outputarea@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/outputarea@npm:4.0.7"
-  dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime": ^4.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/translation": ^4.0.7
+"@jupyterlab/outputarea@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/outputarea@npm:4.0.8"
+  dependencies:
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/translation": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
@@ -926,65 +1060,65 @@ __metadata:
     "@lumino/properties": ^2.0.1
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
-  checksum: ea5ff9052408a117f5a74ce5c3938cda53f88d3dd227bea330cf042b69094c17d33d0b64d556f31b763bfe352bde29dc977cdaab69337159f0c9d9301e50a632
+  checksum: 4e97a367d6d06b05838056ef6da48891484fe84e5c06a08f0928f7359fa8362f48903434d033e8ddabdc193ef4b61859855fecbebda3949703583275fd099339
   languageName: node
   linkType: hard
 
-"@jupyterlab/rendermime-interfaces@npm:^3.8.7":
-  version: 3.8.7
-  resolution: "@jupyterlab/rendermime-interfaces@npm:3.8.7"
+"@jupyterlab/rendermime-interfaces@npm:^3.8.8":
+  version: 3.8.8
+  resolution: "@jupyterlab/rendermime-interfaces@npm:3.8.8"
   dependencies:
     "@lumino/coreutils": ^1.11.0 || ^2.1.2
     "@lumino/widgets": ^1.37.2 || ^2.3.0
-  checksum: 8095fc99f89e49ef6793e37d7864511cc182fd2260219d3fe94dc974ac34411d4daf898f237279bd5e097aea19cca04196356bf31bd774e94c77b54894baf71b
+  checksum: b356cc18acedd7eebbf9e6f03329ad58f0aadb676ef7ef6b64dec610857a53593662df54752bb58780d34f39938ec35c6940918513e3a3cef7c5893bd0909684
   languageName: node
   linkType: hard
 
-"@jupyterlab/rendermime@npm:^4.0.0, @jupyterlab/rendermime@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/rendermime@npm:4.0.7"
-  dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/translation": ^4.0.7
+"@jupyterlab/rendermime@npm:^4.0.0, @jupyterlab/rendermime@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/rendermime@npm:4.0.8"
+  dependencies:
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/translation": ^4.0.8
     "@lumino/coreutils": ^2.1.2
     "@lumino/messaging": ^2.0.1
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
     lodash.escape: ^4.0.1
-  checksum: 8e7bc7dc8d569fa8748783d0d23b716deb64af530d2f6861f6a08fe66ace5ff0d75e3cc67eb4ebb50b2089574917fe0b65da0dcf5368c3539fdb78f595560885
+  checksum: c1f9ebffc746fdc13c1b14a148fd2ae10132b5ca4e1eab27d18ac5bf3d3ae70cf2850b06f6c05a799f2c769792d81dab1447885d0cda7206c7cf63af10bbe4f2
   languageName: node
   linkType: hard
 
-"@jupyterlab/services@npm:^7.0.7":
-  version: 7.0.7
-  resolution: "@jupyterlab/services@npm:7.0.7"
+"@jupyterlab/services@npm:^7.0.8":
+  version: 7.0.8
+  resolution: "@jupyterlab/services@npm:7.0.8"
   dependencies:
     "@jupyter/ydoc": ^1.0.2
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/settingregistry": ^4.0.7
-    "@jupyterlab/statedb": ^4.0.7
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/settingregistry": ^4.0.8
+    "@jupyterlab/statedb": ^4.0.8
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/polling": ^2.1.2
     "@lumino/properties": ^2.0.1
     "@lumino/signaling": ^2.1.2
     ws: ^8.11.0
-  checksum: 203f9e9eeab55eac9251c43d14ebaad881e8152a1337156ed7f2abbada54177237128c108f91a49f45df00226df8ba6a374d02afbd3bbd80ebb795cb5bc62e23
+  checksum: b0112854d3014eff9d9855a6840d1efd0d866d4c011e7a9c4c1c5fba404dd13107b62de6ce845902d12cc6404aafdfee95127a2af43560ade53a00fc7b73378a
   languageName: node
   linkType: hard
 
-"@jupyterlab/settingregistry@npm:^4.0.0, @jupyterlab/settingregistry@npm:^4.0.2, @jupyterlab/settingregistry@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/settingregistry@npm:4.0.7"
+"@jupyterlab/settingregistry@npm:^4.0.0, @jupyterlab/settingregistry@npm:^4.0.2, @jupyterlab/settingregistry@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/settingregistry@npm:4.0.8"
   dependencies:
-    "@jupyterlab/nbformat": ^4.0.7
-    "@jupyterlab/statedb": ^4.0.7
+    "@jupyterlab/nbformat": ^4.0.8
+    "@jupyterlab/statedb": ^4.0.8
     "@lumino/commands": ^2.1.3
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
@@ -994,28 +1128,28 @@ __metadata:
     json5: ^2.2.3
   peerDependencies:
     react: ">=16"
-  checksum: f13dd888c42ccbcb1764037e94ea6b9ee6aa82a232cbb0d8b506212b9e9d5d58965215768110f83a310585482d71dfb649a7c2bbb187553d39dd1292b5919dbe
+  checksum: e9661539357edae60e4b300dff68b369e95e96acb343aeb25e23bdbcd6964c59dd40118ce3a856afaf969833958f3872c480e75cc488a5e882546cb88587c461
   languageName: node
   linkType: hard
 
-"@jupyterlab/statedb@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/statedb@npm:4.0.7"
+"@jupyterlab/statedb@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/statedb@npm:4.0.8"
   dependencies:
     "@lumino/commands": ^2.1.3
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/properties": ^2.0.1
     "@lumino/signaling": ^2.1.2
-  checksum: 4f4217fa1fceb40be8837cb450b1e66d4f6758531603c82ac277412ec43e3b94a5207bdeb74339307509a1b059ae6436d653beaff2fadfbc8136434ff0967190
+  checksum: bfd016e91158daf47e07e760126c0c2c3f6d01ecc8e9cad3e17241e5873decbc5fdfce82bf039fa83633b8760245af8003008f38272dafba56b73ac24768a99f
   languageName: node
   linkType: hard
 
-"@jupyterlab/statusbar@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/statusbar@npm:4.0.7"
+"@jupyterlab/statusbar@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/statusbar@npm:4.0.8"
   dependencies:
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
@@ -1023,52 +1157,52 @@ __metadata:
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: 7a2f75215789722a7b9a63548e91a1b179e8c315513d1b8741b508a58937569d723f2207bf542400674767246ad871432a09d1e87779151e43fa3749aa1ade06
+  checksum: a07345a173e1c4500e5ce9aca6c8d619e5fecd928de0f6e88fd29241b39c09b85b26722279cc8119031d3015f2b32a0d3b9d85fd3cf9370c7605ebcd37d0d31a
   languageName: node
   linkType: hard
 
-"@jupyterlab/toc@npm:^6.0.7":
-  version: 6.0.7
-  resolution: "@jupyterlab/toc@npm:6.0.7"
+"@jupyterlab/toc@npm:^6.0.8":
+  version: 6.0.8
+  resolution: "@jupyterlab/toc@npm:6.0.8"
   dependencies:
-    "@jupyterlab/apputils": ^4.1.7
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/docregistry": ^4.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime": ^4.0.7
-    "@jupyterlab/translation": ^4.0.7
-    "@jupyterlab/ui-components": ^4.0.7
+    "@jupyterlab/apputils": ^4.1.8
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/docregistry": ^4.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime": ^4.0.8
+    "@jupyterlab/translation": ^4.0.8
+    "@jupyterlab/ui-components": ^4.0.8
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/messaging": ^2.0.1
     "@lumino/signaling": ^2.1.2
     "@lumino/widgets": ^2.3.0
     react: ^18.2.0
-  checksum: 6d0c17f79f8d077074a20d78f81fdda010f43edd5ffa423837c90dc9edd6810f7b7445c008ff7f0b04f917e6d37d76c7817bd1b2cedda48961c3e8c0553bbc16
+  checksum: 5d4373c5e0b3ea302275cdf117e17a201a6ab8337288cbdb7e6048b87de2ed8e293e3dd203fb23f7d25db080e06e8271559b1d5aa5e33202130cc95d0972b95f
   languageName: node
   linkType: hard
 
-"@jupyterlab/translation@npm:^4.0.0, @jupyterlab/translation@npm:^4.0.2, @jupyterlab/translation@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/translation@npm:4.0.7"
+"@jupyterlab/translation@npm:^4.0.0, @jupyterlab/translation@npm:^4.0.2, @jupyterlab/translation@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/translation@npm:4.0.8"
   dependencies:
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
-    "@jupyterlab/services": ^7.0.7
-    "@jupyterlab/statedb": ^4.0.7
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
+    "@jupyterlab/services": ^7.0.8
+    "@jupyterlab/statedb": ^4.0.8
     "@lumino/coreutils": ^2.1.2
-  checksum: 15ad212d9447049f5d77d24681018efd52e61b861e73cdba4b09f4530801bdfa317c0eadde0b71016a9f45b68fbf91f723f9a63de9cbbe568c88923a9676ffab
+  checksum: 998d42d85ccd779237ac69abfaf2e341d865374ed5a1a4d234470337f498636511eec0562c741ad44a6a75fae930a510a0a76e176f72665499be2b7edb0dc5f8
   languageName: node
   linkType: hard
 
-"@jupyterlab/ui-components@npm:^4.0.0, @jupyterlab/ui-components@npm:^4.0.2, @jupyterlab/ui-components@npm:^4.0.7":
-  version: 4.0.7
-  resolution: "@jupyterlab/ui-components@npm:4.0.7"
+"@jupyterlab/ui-components@npm:^4.0.0, @jupyterlab/ui-components@npm:^4.0.2, @jupyterlab/ui-components@npm:^4.0.8":
+  version: 4.0.8
+  resolution: "@jupyterlab/ui-components@npm:4.0.8"
   dependencies:
-    "@jupyterlab/coreutils": ^6.0.7
-    "@jupyterlab/observables": ^5.0.7
-    "@jupyterlab/rendermime-interfaces": ^3.8.7
-    "@jupyterlab/translation": ^4.0.7
+    "@jupyterlab/coreutils": ^6.0.8
+    "@jupyterlab/observables": ^5.0.8
+    "@jupyterlab/rendermime-interfaces": ^3.8.8
+    "@jupyterlab/translation": ^4.0.8
     "@lumino/algorithm": ^2.0.1
     "@lumino/commands": ^2.1.3
     "@lumino/coreutils": ^2.1.2
@@ -1086,7 +1220,7 @@ __metadata:
     typestyle: ^2.0.4
   peerDependencies:
     react: ^18.2.0
-  checksum: 92e722b8b4fe96a1df6644de8f955fdf48f2bf568a5aaf5f450f721659afc0ecdd9c89f833d73cbad8684849caec4316d4c6b6b0575e7da5a6c3058f5e99d03e
+  checksum: 7bf11f5ee3c1f88656175c0d3b290be0670d7787076a1eba944875e4780bc2b34c0b9a3af038806ff925620b3056cee36daff08f3ff91acc6c46fd1438bf004d
   languageName: node
   linkType: hard
 
@@ -1262,12 +1396,12 @@ __metadata:
   linkType: hard
 
 "@lezer/javascript@npm:^1.0.0":
-  version: 1.4.8
-  resolution: "@lezer/javascript@npm:1.4.8"
+  version: 1.4.9
+  resolution: "@lezer/javascript@npm:1.4.9"
   dependencies:
     "@lezer/highlight": ^1.1.3
     "@lezer/lr": ^1.3.0
-  checksum: d0c1de5dd756c0a64b440984273cf5a9ef0d227d6b059d2db96c62fde869e34427b46389d56401d067c82222f11373e2d20f9280e4c403bf681ec6a35ae16126
+  checksum: adac0048e4ab33dc48db42014f02d53a2eab81d12c990b23f237a3e83b125bda271607442aaa50dc0ac870a803e678135111366235f7c29a5052a288c1003960
   languageName: node
   linkType: hard
 
@@ -1282,11 +1416,11 @@ __metadata:
   linkType: hard
 
 "@lezer/lr@npm:^1.0.0, @lezer/lr@npm:^1.1.0, @lezer/lr@npm:^1.3.0":
-  version: 1.3.13
-  resolution: "@lezer/lr@npm:1.3.13"
+  version: 1.3.14
+  resolution: "@lezer/lr@npm:1.3.14"
   dependencies:
     "@lezer/common": ^1.0.0
-  checksum: aad0cb8908796a6b49116842fd490093aa0de54b48150a60a4f418815c014f7a1b4355615832e305caea5c0ba8c5ab577f82aebcd0ea04586b8199284ef0fec8
+  checksum: 07be41edcb6c332a3567436d2c626131544181c4d680811baf23f6157db3dce4ebfef325cbd0b88dc8b128b83fbe6363c5dcf3e0a4ff369ddfae05d9f207daee
   languageName: node
   linkType: hard
 
@@ -1311,12 +1445,12 @@ __metadata:
   linkType: hard
 
 "@lezer/python@npm:^1.1.4":
-  version: 1.1.8
-  resolution: "@lezer/python@npm:1.1.8"
+  version: 1.1.9
+  resolution: "@lezer/python@npm:1.1.9"
   dependencies:
     "@lezer/highlight": ^1.0.0
     "@lezer/lr": ^1.0.0
-  checksum: e4a4e0b0fd871acff25111d4f767944b5015479776504b85c4431859c8a2859fdfa6362f204f3027cf9858c7ea907fd57244852a18b67da9eba3b2fe38d31b03
+  checksum: cc7e712665f0b7990fd00ba798c2e377f8393d0034a85da33b370e256322d92f668f51b70aa91585ed165718bad60fba6e86203f877d537819874be2549ec31f
   languageName: node
   linkType: hard
 
@@ -1331,12 +1465,12 @@ __metadata:
   linkType: hard
 
 "@lezer/xml@npm:^1.0.0":
-  version: 1.0.2
-  resolution: "@lezer/xml@npm:1.0.2"
+  version: 1.0.3
+  resolution: "@lezer/xml@npm:1.0.3"
   dependencies:
     "@lezer/highlight": ^1.0.0
     "@lezer/lr": ^1.0.0
-  checksum: e834bcc5c0dee3eecb5362b3f10187e80908b6a293ebacf5750547a64b57ec710a068497334f109ecf4e5ea05e09e7e9c00e48ebbd30050673ea67b0929e5398
+  checksum: a4758859abcab3bc3f8680f79f7fb7dbbb6842c7d552888f95cc85a845450342a18731fbf49cbaec5f39970b9e5b96a66c8381eda036d3ebf7c952da5ddd7666
   languageName: node
   linkType: hard
 
@@ -1348,13 +1482,13 @@ __metadata:
   linkType: hard
 
 "@lumino/application@npm:^2.2.1":
-  version: 2.2.1
-  resolution: "@lumino/application@npm:2.2.1"
+  version: 2.3.0
+  resolution: "@lumino/application@npm:2.3.0"
   dependencies:
-    "@lumino/commands": ^2.1.3
+    "@lumino/commands": ^2.2.0
     "@lumino/coreutils": ^2.1.2
-    "@lumino/widgets": ^2.3.0
-  checksum: a33e661703728440bc7d2ddb4674261f4de0d20eb8c9846646cbd6debac03b5c65e78d739a500903550fd83b8f47b47fa82ec178c97bc9967ca3ac4014075cde
+    "@lumino/widgets": ^2.3.1
+  checksum: 9d1eb5bc972ed158bf219604a53bbac1262059bc5b0123d3e041974486b9cbb8288abeeec916f3b62f62d7c32e716cccf8b73e4832ae927e4f9dd4e4b0cd37ed
   languageName: node
   linkType: hard
 
@@ -1367,9 +1501,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@lumino/commands@npm:^2.0.1, @lumino/commands@npm:^2.1.3":
-  version: 2.1.3
-  resolution: "@lumino/commands@npm:2.1.3"
+"@lumino/commands@npm:^2.0.1, @lumino/commands@npm:^2.1.3, @lumino/commands@npm:^2.2.0":
+  version: 2.2.0
+  resolution: "@lumino/commands@npm:2.2.0"
   dependencies:
     "@lumino/algorithm": ^2.0.1
     "@lumino/coreutils": ^2.1.2
@@ -1378,7 +1512,7 @@ __metadata:
     "@lumino/keyboard": ^2.0.1
     "@lumino/signaling": ^2.1.2
     "@lumino/virtualdom": ^2.0.1
-  checksum: e4e3ee279f2a5e8d68e4ce142c880333f5542f90c684972402356936ecb5cf5e07163800b59e7cb8c911cbdb4e5089edcc5dd2990bc8db10c87517268de1fc5d
+  checksum: 093e9715491e5cef24bc80665d64841417b400f2fa595f9b60832a3b6340c405c94a6aa276911944a2c46d79a6229f3cc087b73f50852bba25ece805abd0fae9
   languageName: node
   linkType: hard
 
@@ -1389,6 +1523,23 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@lumino/datagrid@npm:^2.2.0":
+  version: 2.3.0
+  resolution: "@lumino/datagrid@npm:2.3.0"
+  dependencies:
+    "@lumino/algorithm": ^2.0.1
+    "@lumino/coreutils": ^2.1.2
+    "@lumino/disposable": ^2.1.2
+    "@lumino/domutils": ^2.0.1
+    "@lumino/dragdrop": ^2.1.4
+    "@lumino/keyboard": ^2.0.1
+    "@lumino/messaging": ^2.0.1
+    "@lumino/signaling": ^2.1.2
+    "@lumino/widgets": ^2.3.1
+  checksum: 906ecd8d02a4ccbd6d09384e181f809ef4c165ca7bbc2388b43276f28d0a7d2949093f8b1782f8e11c988640ffaaeca9fe889722a51ee424cad3314674658695
+  languageName: node
+  linkType: hard
+
 "@lumino/disposable@npm:^1.10.0 || ^2.0.0, @lumino/disposable@npm:^2.0.0, @lumino/disposable@npm:^2.1.2":
   version: 2.1.2
   resolution: "@lumino/disposable@npm:2.1.2"
@@ -1405,13 +1556,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@lumino/dragdrop@npm:^2.1.3":
-  version: 2.1.3
-  resolution: "@lumino/dragdrop@npm:2.1.3"
+"@lumino/dragdrop@npm:^2.1.3, @lumino/dragdrop@npm:^2.1.4":
+  version: 2.1.4
+  resolution: "@lumino/dragdrop@npm:2.1.4"
   dependencies:
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
-  checksum: d5f7eb4cc9f9a084cb9af10f02d6741b25d683350878ecbc324e24ba9d4b5246451a410e2ca5fff227aab1c191d1e73a2faf431f93e13111d67a4e426e126258
+  checksum: 43d82484b13b38b612e7dfb424a840ed6a38d0db778af10655c4ba235c67b5b12db1683929b35a36ab2845f77466066dfd1ee25c1c273e8e175677eba9dc560d
   languageName: node
   linkType: hard
 
@@ -1469,22 +1620,22 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@lumino/widgets@npm:^1.37.2 || ^2.3.0, @lumino/widgets@npm:^2.0.1, @lumino/widgets@npm:^2.3.0":
-  version: 2.3.0
-  resolution: "@lumino/widgets@npm:2.3.0"
+"@lumino/widgets@npm:^1.37.2 || ^2.3.0, @lumino/widgets@npm:^2.0.1, @lumino/widgets@npm:^2.3.0, @lumino/widgets@npm:^2.3.1":
+  version: 2.3.1
+  resolution: "@lumino/widgets@npm:2.3.1"
   dependencies:
     "@lumino/algorithm": ^2.0.1
-    "@lumino/commands": ^2.1.3
+    "@lumino/commands": ^2.2.0
     "@lumino/coreutils": ^2.1.2
     "@lumino/disposable": ^2.1.2
     "@lumino/domutils": ^2.0.1
-    "@lumino/dragdrop": ^2.1.3
+    "@lumino/dragdrop": ^2.1.4
     "@lumino/keyboard": ^2.0.1
     "@lumino/messaging": ^2.0.1
     "@lumino/properties": ^2.0.1
     "@lumino/signaling": ^2.1.2
     "@lumino/virtualdom": ^2.0.1
-  checksum: a8559bd3574b7fc16e7679e05994c515b0d3e78dada35786d161f67c639941d134e92ce31d95c2e4ac06709cdf83b0e7fb4b6414a3f7779579222a2fb525d025
+  checksum: ba7b8f8839c1cd2a41dbda13281094eb6981a270cccf4f25a0cf83686dcc526a2d8044a20204317630bb7dd4a04d65361408c7623a921549c781afca84b91c67
   languageName: node
   linkType: hard
 
@@ -1515,6 +1666,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@npmcli/agent@npm:^2.0.0":
+  version: 2.2.0
+  resolution: "@npmcli/agent@npm:2.2.0"
+  dependencies:
+    agent-base: ^7.1.0
+    http-proxy-agent: ^7.0.0
+    https-proxy-agent: ^7.0.1
+    lru-cache: ^10.0.1
+    socks-proxy-agent: ^8.0.1
+  checksum: 3b25312edbdfaa4089af28e2d423b6f19838b945e47765b0c8174c1395c79d43c3ad6d23cb364b43f59fd3acb02c93e3b493f72ddbe3dfea04c86843a7311fc4
+  languageName: node
+  linkType: hard
+
 "@npmcli/arborist@npm:6.2.3":
   version: 6.2.3
   resolution: "@npmcli/arborist@npm:6.2.3"
@@ -2011,9 +2175,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@playwright/test@npm:^1.32.0, @playwright/test@npm:^1.32.2":
+  version: 1.39.0
+  resolution: "@playwright/test@npm:1.39.0"
+  dependencies:
+    playwright: 1.39.0
+  bin:
+    playwright: cli.js
+  checksum: e93e58fc1af4239f239b890374f066c9a758e2492d25e2c1a532f3f00782ab8e7706956a07540fd14882c74e75f5de36273621adce9b79afb8e36e6c15f1d539
+  languageName: node
+  linkType: hard
+
 "@rjsf/core@npm:^5.1.0":
-  version: 5.13.2
-  resolution: "@rjsf/core@npm:5.13.2"
+  version: 5.13.4
+  resolution: "@rjsf/core@npm:5.13.4"
   dependencies:
     lodash: ^4.17.21
     lodash-es: ^4.17.21
@@ -2023,13 +2198,13 @@ __metadata:
   peerDependencies:
     "@rjsf/utils": ^5.12.x
     react: ^16.14.0 || >=17
-  checksum: e977c33bc74075fe2035a22d242bd1a8433468834e3e45fe9b8edaf9e14e14793c43936917805f105960b3d71385fc6616ce502b5273fd6ee1c4539aa3c4e69c
+  checksum: a263eca0064a62815a1e6a0492072c70834415a35dd67dd24a17f5ca77866c5878b57a1867b216a9a11e56f115b18822b4e20423dec984e22d0d7a8eed52eb22
   languageName: node
   linkType: hard
 
 "@rjsf/utils@npm:^5.1.0":
-  version: 5.13.2
-  resolution: "@rjsf/utils@npm:5.13.2"
+  version: 5.13.4
+  resolution: "@rjsf/utils@npm:5.13.4"
   dependencies:
     json-schema-merge-allof: ^0.8.1
     jsonpointer: ^5.0.1
@@ -2038,7 +2213,7 @@ __metadata:
     react-is: ^18.2.0
   peerDependencies:
     react: ^16.14.0 || >=17
-  checksum: 06834669205fa0429355f04fc551986ca6899c7b656feb2f2f0477c02e6da625bf198bd292b06e703e2c029436d899a2c802fe28d1bfe5017b2a2d016a361180
+  checksum: 9c55dd102a850cded70edc9de1a68b1d0b181d04abe59cbfd9b1cd42500dae6a271ff4e991925843447f881a579790ba28bc578a1373133fd164cad40b8f826e
   languageName: node
   linkType: hard
 
@@ -2086,6 +2261,410 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@stdlib/array@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/array@npm:0.0.12"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/blas": ^0.0.x
+    "@stdlib/complex": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/symbol": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 0d95690461f0c4560eabef0796d1170274415cd03de80333c6d39814d0484a6873ef4be04a64941ebf3a600747e84c3a4f23b21c7020e53842c07985331b39f1
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/assert@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/assert@npm:0.0.12"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/complex": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/ndarray": ^0.0.x
+    "@stdlib/number": ^0.0.x
+    "@stdlib/os": ^0.0.x
+    "@stdlib/process": ^0.0.x
+    "@stdlib/regexp": ^0.0.x
+    "@stdlib/streams": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/symbol": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: d4dcbeabbfb86ba56cdd972ff785f43e7d25018b2b1800cab8b0deb9e5c54c795d6ead3d142f4dd13c351f636deba4dc1857c85147d6b059fdc78eb2c9510b99
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/bigint@npm:^0.0.x":
+  version: 0.0.11
+  resolution: "@stdlib/bigint@npm:0.0.11"
+  dependencies:
+    "@stdlib/utils": ^0.0.x
+  checksum: 7bf825d116e4b010e214209af239706ac1ef923eecb5c8b0af9229c9975450081355e441ecc7b4765d81a9e653141868e0492b8061d1e65724fa42fb8283aabd
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/blas@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/blas@npm:0.0.12"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/number": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 67ea00a968f7a9c710b37f718b7f756e2830e479a1a1ee44cbf6ec3cc27dd8863078928867707d9d1624007e81de89d040f2326d10f435e2cce913cab121975e
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/buffer@npm:^0.0.x":
+  version: 0.0.11
+  resolution: "@stdlib/buffer@npm:0.0.11"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/process": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 93df02e3bf548e940ff9cef65121566e7bf93b554f0614d62336c9dbccfc07c9f1b1c4e9a7aebbe4819ef16a6d2a33a7010c2fdf908fface8298a3109c3c4ef0
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/cli@npm:^0.0.x":
+  version: 0.0.10
+  resolution: "@stdlib/cli@npm:0.0.10"
+  dependencies:
+    "@stdlib/utils": ^0.0.x
+    minimist: ^1.2.0
+  checksum: bbece8d3dbff2835518582a7726c6c4c22743dc408d2303d9e35a3b72151d5d0a8e78d61bc896663d4c3fb702e966abea7a1bd621ed943723a359f57053f121f
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/complex@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/complex@npm:0.0.12"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 8eda35027495417f1b0dd9bbbc2d4983f50ad3cf9e2276ffe0945ccdbe78f0fc66b9fc36ab71926d2a125c8fb7467c8970a222b230b42ff4bb8042c53314ca09
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/constants@npm:^0.0.x":
+  version: 0.0.11
+  resolution: "@stdlib/constants@npm:0.0.11"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/number": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: fc19d055a4e71ae84b6c92e4a3a88371d50693da8f0a813df4063dc549374d19b9cf23f4fdae2fb7b2013e13929f713c3e1b9e4054767e741b75561ed43d15c3
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/fs@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/fs@npm:0.0.12"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/process": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+    debug: ^2.6.9
+  checksum: 33ac5ee4844d4599fe3a8a8402f1a3e2cafee31a5c9cf5b85df530a61a2b54ef17dc30a67be98dacdc2958219413edd0e4cdc3c28266f4bc30277ee024f6a49e
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/math@npm:^0.0.x":
+  version: 0.0.11
+  resolution: "@stdlib/math@npm:0.0.11"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/ndarray": ^0.0.x
+    "@stdlib/number": ^0.0.x
+    "@stdlib/strided": ^0.0.x
+    "@stdlib/symbol": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+    debug: ^2.6.9
+  checksum: 6c4c9dda36fbce50553e1437354c5286aa782c42399534dbed8e696ddeb1b91ef6cff5fe5962f1c9e1eb2ef63c63d9bd58f7ca4b87d59018aaac20099c3fb79a
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/ndarray@npm:^0.0.x":
+  version: 0.0.13
+  resolution: "@stdlib/ndarray@npm:0.0.13"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/bigint": ^0.0.x
+    "@stdlib/buffer": ^0.0.x
+    "@stdlib/complex": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/number": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 842a94afce5fc74bf8a964b75a302ddb8713eadbc79616e6799f1310c8bce860ed9e9877adc4a39338d9136b8798947ee21cf03368d46408308a313c8075d49a
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/nlp@npm:^0.0.x":
+  version: 0.0.11
+  resolution: "@stdlib/nlp@npm:0.0.11"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/random": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 398fe2853fb95404bb6598e3e199ca3e0435b94447d50e14e2e30582cadfb91f43464f23d80a0e1da4d64567a4a108a7299d7440509f1ab26b02aea7bb16e9a8
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/number@npm:^0.0.x":
+  version: 0.0.10
+  resolution: "@stdlib/number@npm:0.0.10"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/os": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 326190956c787cbf9321c332beedab5ba4b3fa97d52a82aa708a0349b4678c0df7a351424f00a606f4eaca4fb4ba4cc191580c99d7c64ee0f08d37baa3de14f2
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/os@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/os@npm:0.0.12"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/process": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 37156b0c723da70d7740d92d08fc592eae803461c1d546cff6ac044765d6e40722fdad342219277e747c39344b513096ac1d0aa1e733cf3079bd8a9a8578612a
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/process@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/process@npm:0.0.12"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/buffer": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/streams": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 6d5c3d943f9914d1ae39bd36ad7436f783cf64baa2bff67a808035c99258676ae3f704c328a78d62754951cf85fe99d8e9af5f4fa7d5f8cba347bca72767e357
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/random@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/random@npm:0.0.12"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/blas": ^0.0.x
+    "@stdlib/buffer": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/process": ^0.0.x
+    "@stdlib/stats": ^0.0.x
+    "@stdlib/streams": ^0.0.x
+    "@stdlib/symbol": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+    debug: ^2.6.9
+    readable-stream: ^2.1.4
+  checksum: 67fcb5553274f8596ceae91153e96ae297bacfd55279821cb09f19f2844845aaf892802e4a5962965323dbfded0c7df8a89a6ce77d60d5c8a5899d483055a964
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/regexp@npm:^0.0.x":
+  version: 0.0.13
+  resolution: "@stdlib/regexp@npm:0.0.13"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: dd52adb096ff9a02d1c4818be2889ae01bc04a0cdbc0d52473685e0a7a4eaa13e1be603b964f140f7488d11450b644dc5f8c97029d77db1ed4a563554245ff1c
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/stats@npm:^0.0.x, @stdlib/stats@npm:~0.0.13":
+  version: 0.0.13
+  resolution: "@stdlib/stats@npm:0.0.13"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/blas": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/ndarray": ^0.0.x
+    "@stdlib/random": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/symbol": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 5ca12b2e123543f56a59aca828e14afaf525ad4aa40467bee7037a9178e21e55d4ce8ba3de9387cc9a0efe3e0d035d6c58705b12f634f77a2b3f87d334dfb076
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/streams@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/streams@npm:0.0.12"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/buffer": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+    debug: ^2.6.9
+    readable-stream: ^2.1.4
+  checksum: 231b4607d082ea81d9dadbeab08002ec398a29c7eb5d611d8a4183f9db6964428e2f8a9e0f8edd085ca12b5d58258576987a575e9d8f6fcabcb5a62c6b8efe88
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/strided@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/strided@npm:0.0.12"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/ndarray": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 55ccc8543596894a2e3ad734b394700c69697b499a54b3bfbcf80cddd8d91509792c23931f5cebf7c89269676ac3f44352582e4f42e2c2c2898363cc3a76403d
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/string@npm:^0.0.x":
+  version: 0.0.14
+  resolution: "@stdlib/string@npm:0.0.14"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/nlp": ^0.0.x
+    "@stdlib/process": ^0.0.x
+    "@stdlib/regexp": ^0.0.x
+    "@stdlib/streams": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: aaaaaddf381cccc67f15dbab76f43ce81cb71a4f5595bfa06ef915b6747458deca3c25c60ff3c002c0c36482687d92a150f364069559dfea915f63a040d5f603
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/symbol@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/symbol@npm:0.0.12"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 2263341ce0296de2063d26038902bd63bf1d7b820307402fdf38c3b248bd026f17d96bccdc3189fd9fcc9c83a778eaab797dc11805bd66203b8ac9c6934f6588
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/time@npm:^0.0.x":
+  version: 0.0.14
+  resolution: "@stdlib/time@npm:0.0.14"
+  dependencies:
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/utils": ^0.0.x
+  checksum: 6e8a1b985a09936ab09c98d44bf1b2c79e08995c3c73401494bc1f6f708747ef136d769af4809a8af92a9ceb3d390db6c4c4e01608cd8d794a86c4b57e343eb1
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/types@npm:^0.0.x":
+  version: 0.0.14
+  resolution: "@stdlib/types@npm:0.0.14"
+  checksum: 5680a655ddb3ad730f5c7eb2363a43e089f3e6a1b85b12546cab49f7749bb3baf293bd50fbfe55486f233f4227f1020b65eb461b754b94fb4a4bc2799647ec22
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
+"@stdlib/utils@npm:^0.0.x":
+  version: 0.0.12
+  resolution: "@stdlib/utils@npm:0.0.12"
+  dependencies:
+    "@stdlib/array": ^0.0.x
+    "@stdlib/assert": ^0.0.x
+    "@stdlib/blas": ^0.0.x
+    "@stdlib/buffer": ^0.0.x
+    "@stdlib/cli": ^0.0.x
+    "@stdlib/constants": ^0.0.x
+    "@stdlib/fs": ^0.0.x
+    "@stdlib/math": ^0.0.x
+    "@stdlib/os": ^0.0.x
+    "@stdlib/process": ^0.0.x
+    "@stdlib/random": ^0.0.x
+    "@stdlib/regexp": ^0.0.x
+    "@stdlib/streams": ^0.0.x
+    "@stdlib/string": ^0.0.x
+    "@stdlib/symbol": ^0.0.x
+    "@stdlib/time": ^0.0.x
+    "@stdlib/types": ^0.0.x
+    debug: ^2.6.9
+  checksum: e0c3671c5f62c11bb3abd721f2958c41641b00a75d449bd25fbb62bcb8689cfe9c1f600c0688e7b6819ae870d6e5974d0fc7b2ec86081c45d9194b316b2a2ec2
+  conditions: (os=aix | os=darwin | os=freebsd | os=linux | os=macos | os=openbsd | os=sunos | os=win32 | os=windows)
+  languageName: node
+  linkType: hard
+
 "@tootallnate/once@npm:2":
   version: 2.0.0
   resolution: "@tootallnate/once@npm:2.0.0"
@@ -2121,19 +2700,26 @@ __metadata:
   linkType: hard
 
 "@types/eslint@npm:*":
-  version: 8.44.5
-  resolution: "@types/eslint@npm:8.44.5"
+  version: 8.44.6
+  resolution: "@types/eslint@npm:8.44.6"
   dependencies:
     "@types/estree": "*"
     "@types/json-schema": "*"
-  checksum: 0fba768ecffbd9eccf07207979ffa82e748bfb67203be107d8fb6c9b1215054d57ca12062577878e73353ef8feb43591951c2a750452fde639858f501e03304a
+  checksum: ed8de582ab3dbd7ec0bf97d41f4f3de28dd8a37fc48bc423e1c406bbb70d1fd8c4175ba17ad6495ef9ef99a43df71421277b7a2a0355097489c4c4cf6bb266ff
   languageName: node
   linkType: hard
 
 "@types/estree@npm:*, @types/estree@npm:^1.0.0":
-  version: 1.0.3
-  resolution: "@types/estree@npm:1.0.3"
-  checksum: f21a5448995f8aa61ab2248d10590d275666b11d26c27fe75b3c23420b07b469d5ce820deefcf7399671faa09d56eb7ce012322948e484d94686fda154be5221
+  version: 1.0.4
+  resolution: "@types/estree@npm:1.0.4"
+  checksum: dcd08e6e967def3afff745774b6b9b912d6394ddacbb3e8be05bb291c1803f5f03f1ab0eeb852bf8a85ca14842663f461f3dac82179dcdccbf45fbc067673bbc
+  languageName: node
+  linkType: hard
+
+"@types/geojson@npm:7946.0.4":
+  version: 7946.0.4
+  resolution: "@types/geojson@npm:7946.0.4"
+  checksum: 541aea46540c918b9fe21ab73f497fe17b1eaf4d0d3baeb5f5614029b7f488c37f63843b644c024a8178dc2fb66d3d6623c25d9cf61d7b553aa19c8dc7f99047
   languageName: node
   linkType: hard
 
@@ -2159,75 +2745,75 @@ __metadata:
   linkType: hard
 
 "@types/node@npm:*":
-  version: 20.8.6
-  resolution: "@types/node@npm:20.8.6"
+  version: 20.8.10
+  resolution: "@types/node@npm:20.8.10"
   dependencies:
-    undici-types: ~5.25.1
-  checksum: ccfb7ac482c5a96edeb239893c5c099f5257fcc2ed9ae62fefdfbc782b79e16dbc2af9a85b379665237bf759904b44ca2be68e75d239e0297882aad42f61905c
+    undici-types: ~5.26.4
+  checksum: 7c61190e43e8074a1b571e52ff14c880bc67a0447f2fe5ed0e1a023eb8a23d5f815658edb98890f7578afe0f090433c4a635c7c87311762544e20dd78723e515
   languageName: node
   linkType: hard
 
 "@types/normalize-package-data@npm:^2.4.0":
-  version: 2.4.2
-  resolution: "@types/normalize-package-data@npm:2.4.2"
-  checksum: 2132e4054711e6118de967ae3a34f8c564e58d71fbcab678ec2c34c14659f638a86c35a0fd45237ea35a4a03079cf0a485e3f97736ffba5ed647bfb5da086b03
+  version: 2.4.3
+  resolution: "@types/normalize-package-data@npm:2.4.3"
+  checksum: 6f60e157c0fc39b80d80eb9043cdd78e4090f25c5264ef0317f5701648a5712fd453d364569675a19aef44a18c6f14f6e4809bdc0b97a46a0ed9ce4a320bbe42
   languageName: node
   linkType: hard
 
 "@types/parse-json@npm:^4.0.0":
-  version: 4.0.0
-  resolution: "@types/parse-json@npm:4.0.0"
-  checksum: fd6bce2b674b6efc3db4c7c3d336bd70c90838e8439de639b909ce22f3720d21344f52427f1d9e57b265fcb7f6c018699b99e5e0c208a1a4823014269a6bf35b
+  version: 4.0.1
+  resolution: "@types/parse-json@npm:4.0.1"
+  checksum: 467c5fb95f4b03ea10fac007b4de7c9db103e8fce87b039ba5b37f17b374911833724624c311f3591435e4c42e376cab219400af1aef1dc314d5bd495d22fde7
   languageName: node
   linkType: hard
 
 "@types/prop-types@npm:*":
-  version: 15.7.8
-  resolution: "@types/prop-types@npm:15.7.8"
-  checksum: 61dfad79da8b1081c450bab83b77935df487ae1cdd4660ec7df6be8e74725c15fa45cf486ce057addc956ca4ae78300b97091e2a25061133d1b9a1440bc896ae
+  version: 15.7.9
+  resolution: "@types/prop-types@npm:15.7.9"
+  checksum: c7591d3ff7593e243908a07e1d3e2bb6e8879008af5800d8378115a90d0fdf669a1cae72a6d7f69e59c4fa7bb4c8ed61f6ebc1c520fe110c6f2b03ac02414072
   languageName: node
   linkType: hard
 
 "@types/react@npm:^18.0.26":
-  version: 18.2.28
-  resolution: "@types/react@npm:18.2.28"
+  version: 18.2.35
+  resolution: "@types/react@npm:18.2.35"
   dependencies:
     "@types/prop-types": "*"
     "@types/scheduler": "*"
     csstype: ^3.0.2
-  checksum: 81381bedeba83278f4c9febb0b83e0bd3f42a25897a50b9cb36ef53651d34b3d50f87ebf11211ea57ea575131f85d31e93e496ce46478a00b0f9bf7b26b5917a
+  checksum: d3f1c39116c82e3a504f3332b9198f94f34a33d8535604434b1e387ecabd41ec995961775e2e1c08feefdde878a5b86220879350900137980d681ae3983db462
   languageName: node
   linkType: hard
 
 "@types/scheduler@npm:*":
-  version: 0.16.4
-  resolution: "@types/scheduler@npm:0.16.4"
-  checksum: a57b0f10da1b021e6bd5eeef8a1917dd3b08a8715bd8029e2ded2096d8f091bb1bb1fef2d66e139588a983c4bfbad29b59e48011141725fa83c76e986e1257d7
+  version: 0.16.5
+  resolution: "@types/scheduler@npm:0.16.5"
+  checksum: 5aae67331bb7877edc65f77f205fb03c3808d9e51c186afe26945ce69f4072886629580a751e9ce8573e4a7538d0dfa1e4ce388c7c451fa689a4c592fdf1ea45
   languageName: node
   linkType: hard
 
 "@types/semver@npm:^7.3.12":
-  version: 7.5.3
-  resolution: "@types/semver@npm:7.5.3"
-  checksum: 349fdd1ab6c213bac5c991bac766bd07b8b12e63762462bb058740dcd2eb09c8193d068bb226f134661275f2022976214c0e727a4e5eb83ec1b131127c980d3e
+  version: 7.5.4
+  resolution: "@types/semver@npm:7.5.4"
+  checksum: 120c0189f6fec5f2d12d0d71ac8a4cfa952dc17fa3d842e8afddb82bba8828a4052f8799c1653e2b47ae1977435f38e8985658fde971905ce5afb8e23ee97ecf
   languageName: node
   linkType: hard
 
 "@types/source-list-map@npm:*":
-  version: 0.1.3
-  resolution: "@types/source-list-map@npm:0.1.3"
-  checksum: a001098786d75b82eef00a6e4f1f7bb73f9f2fdf3e97333f8b741183c3fbf92db91af8bcfc410c7e6c23c4497523a3210f3eee2077b1be93595206f9baf3d909
+  version: 0.1.4
+  resolution: "@types/source-list-map@npm:0.1.4"
+  checksum: c18896ead356c77aa7a5bb6bd0ad72a5e8dea4c7ec1e5162c3f4d7e5afa6f547ace66ce506c47d1726adb34aee9758f9367b35ddd03126f3c9d4bde4700cddf4
   languageName: node
   linkType: hard
 
 "@types/webpack-sources@npm:^0.1.5":
-  version: 0.1.10
-  resolution: "@types/webpack-sources@npm:0.1.10"
+  version: 0.1.11
+  resolution: "@types/webpack-sources@npm:0.1.11"
   dependencies:
     "@types/node": "*"
     "@types/source-list-map": "*"
     source-map: ^0.6.1
-  checksum: 95817c13bc663b1b8b2c9b26d0f8ceadb3b583bb2549b554b660e900245a430dd2ed0b534f14b85153a0121450e01ebdbcefbade9305ed1e80b2190a50659aee
+  checksum: da64fc4b7d774dca57a0b40c20641fd387bc6c02ed3245dfd62af75a9ab0c3bb752773e6c2a023e35ce151563302af4d427ee4e81698ec3f3a7ed9f81f3390f4
   languageName: node
   linkType: hard
 
@@ -2352,6 +2938,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@ungap/structured-clone@npm:^1.2.0":
+  version: 1.2.0
+  resolution: "@ungap/structured-clone@npm:1.2.0"
+  checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524
+  languageName: node
+  linkType: hard
+
+"@vscode/debugprotocol@npm:^1.51.0":
+  version: 1.64.0
+  resolution: "@vscode/debugprotocol@npm:1.64.0"
+  checksum: 1c8a3d6dba035674a9b4ec1b2f5e98d75170fe86cd98cf3d05cdced40d0ee0c3371f3d291516a1903f70cc5a9beadb70048d1ffc71a778bca6767bbeecb716ab
+  languageName: node
+  linkType: hard
+
 "@webassemblyjs/ast@npm:1.11.6, @webassemblyjs/ast@npm:^1.11.5":
   version: 1.11.6
   resolution: "@webassemblyjs/ast@npm:1.11.6"
@@ -2639,11 +3239,11 @@ __metadata:
   linkType: hard
 
 "acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0":
-  version: 8.10.0
-  resolution: "acorn@npm:8.10.0"
+  version: 8.11.2
+  resolution: "acorn@npm:8.11.2"
   bin:
     acorn: bin/acorn
-  checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d
+  checksum: 818450408684da89423e3daae24e4dc9b68692db8ab49ea4569c7c5abb7a3f23669438bf129cc81dfdada95e1c9b944ee1bfca2c57a05a4dc73834a612fbf6a7
   languageName: node
   linkType: hard
 
@@ -2663,6 +3263,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0":
+  version: 7.1.0
+  resolution: "agent-base@npm:7.1.0"
+  dependencies:
+    debug: ^4.3.4
+  checksum: f7828f991470a0cc22cb579c86a18cbae83d8a3cbed39992ab34fc7217c4d126017f1c74d0ab66be87f71455318a8ea3e757d6a37881b8d0f2a2c6aa55e5418f
+  languageName: node
+  linkType: hard
+
 "agentkeepalive@npm:^4.2.1":
   version: 4.5.0
   resolution: "agentkeepalive@npm:4.5.0"
@@ -2913,9 +3522,9 @@ __metadata:
   linkType: hard
 
 "async@npm:^3.2.3":
-  version: 3.2.4
-  resolution: "async@npm:3.2.4"
-  checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89
+  version: 3.2.5
+  resolution: "async@npm:3.2.5"
+  checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4
   languageName: node
   linkType: hard
 
@@ -2941,13 +3550,13 @@ __metadata:
   linkType: hard
 
 "axios@npm:^1.0.0":
-  version: 1.5.1
-  resolution: "axios@npm:1.5.1"
+  version: 1.6.0
+  resolution: "axios@npm:1.6.0"
   dependencies:
     follow-redirects: ^1.15.0
     form-data: ^4.0.0
     proxy-from-env: ^1.1.0
-  checksum: 4444f06601f4ede154183767863d2b8e472b4a6bfc5253597ed6d21899887e1fd0ee2b3de792ac4f8459fe2e359d2aa07c216e45fd8b9e4e0688a6ebf48a5a8d
+  checksum: c7c9f2ae9e0b9bad7d6f9a4dff030930b12ee667dedf54c3c776714f91681feb743c509ac0796ae5c01e12c4ab4a2bee74905068dd200fbc1ab86f9814578fb0
   languageName: node
   linkType: hard
 
@@ -3147,13 +3756,34 @@ __metadata:
   languageName: node
   linkType: hard
 
-"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2":
-  version: 1.0.2
-  resolution: "call-bind@npm:1.0.2"
+"cacache@npm:^18.0.0":
+  version: 18.0.0
+  resolution: "cacache@npm:18.0.0"
   dependencies:
-    function-bind: ^1.1.1
-    get-intrinsic: ^1.0.2
-  checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0
+    "@npmcli/fs": ^3.1.0
+    fs-minipass: ^3.0.0
+    glob: ^10.2.2
+    lru-cache: ^10.0.1
+    minipass: ^7.0.3
+    minipass-collect: ^1.0.2
+    minipass-flush: ^1.0.5
+    minipass-pipeline: ^1.2.4
+    p-map: ^4.0.0
+    ssri: ^10.0.0
+    tar: ^6.1.11
+    unique-filename: ^3.0.0
+  checksum: 2cd6bf15551abd4165acb3a4d1ef0593b3aa2fd6853ae16b5bb62199c2faecf27d36555a9545c0e07dd03347ec052e782923bdcece724a24611986aafb53e152
+  languageName: node
+  linkType: hard
+
+"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5":
+  version: 1.0.5
+  resolution: "call-bind@npm:1.0.5"
+  dependencies:
+    function-bind: ^1.1.2
+    get-intrinsic: ^1.2.1
+    set-function-length: ^1.1.1
+  checksum: 449e83ecbd4ba48e7eaac5af26fea3b50f8f6072202c2dd7c5a6e7a6308f2421abe5e13a3bbd55221087f76320c5e09f25a8fdad1bab2b77c68ae74d92234ea5
   languageName: node
   linkType: hard
 
@@ -3183,9 +3813,9 @@ __metadata:
   linkType: hard
 
 "caniuse-lite@npm:^1.0.30001541":
-  version: 1.0.30001550
-  resolution: "caniuse-lite@npm:1.0.30001550"
-  checksum: b48d5a0bd25d634d61f410e0130c5e6c328724285c3c6065644eb41f86a227f5db0eb77da25d0fe3c6c9b04bbd040c9c164bf8dc50dd75cb30fc578aaae562f1
+  version: 1.0.30001561
+  resolution: "caniuse-lite@npm:1.0.30001561"
+  checksum: 949829fe037e23346595614e01d362130245920503a12677f2506ce68e1240360113d6383febed41e8aa38cd0f5fd9c69c21b0af65a71c0246d560db489f1373
   languageName: node
   linkType: hard
 
@@ -3342,9 +3972,9 @@ __metadata:
   linkType: hard
 
 "cmd-shim@npm:^6.0.0":
-  version: 6.0.1
-  resolution: "cmd-shim@npm:6.0.1"
-  checksum: 359006b3a5bb4a0ff161a44ccc18fbba947db748ef0dd12273e476792e316a5edb0945d74bfa1e91cd88ce0511025fde87901eda092c479d83cfcd6734562683
+  version: 6.0.2
+  resolution: "cmd-shim@npm:6.0.2"
+  checksum: df3a01fc4d72a49b450985b991205e65774b28e7f74a2e4d2a11fd0df8732e3828f9e7b644050def3cd0be026cbd3ee46a1f50ce5f57d0b3fb5afe335bdfacde
   languageName: node
   linkType: hard
 
@@ -3422,6 +4052,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"commander@npm:2, commander@npm:^2.20.0":
+  version: 2.20.3
+  resolution: "commander@npm:2.20.3"
+  checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e
+  languageName: node
+  linkType: hard
+
+"commander@npm:7":
+  version: 7.2.0
+  resolution: "commander@npm:7.2.0"
+  checksum: 53501cbeee61d5157546c0bef0fedb6cdfc763a882136284bed9a07225f09a14b82d2a84e7637edfd1a679fb35ed9502fd58ef1d091e6287f60d790147f68ddc
+  languageName: node
+  linkType: hard
+
 "commander@npm:^10.0.1":
   version: 10.0.1
   resolution: "commander@npm:10.0.1"
@@ -3429,13 +4073,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"commander@npm:^2.20.0":
-  version: 2.20.3
-  resolution: "commander@npm:2.20.3"
-  checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e
-  languageName: node
-  linkType: hard
-
 "commander@npm:^9.4.1":
   version: 9.5.0
   resolution: "commander@npm:9.5.0"
@@ -3740,6 +4377,189 @@ __metadata:
   languageName: node
   linkType: hard
 
+"d3-array@npm:1 - 3, d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:2.5.0 - 3, d3-array@npm:^3.2.2":
+  version: 3.2.4
+  resolution: "d3-array@npm:3.2.4"
+  dependencies:
+    internmap: 1 - 2
+  checksum: a5976a6d6205f69208478bb44920dd7ce3e788c9dceb86b304dbe401a4bfb42ecc8b04c20facde486e9adcb488b5d1800d49393a3f81a23902b68158e12cddd0
+  languageName: node
+  linkType: hard
+
+"d3-array@npm:3.2.2":
+  version: 3.2.2
+  resolution: "d3-array@npm:3.2.2"
+  dependencies:
+    internmap: 1 - 2
+  checksum: 98af3db792685ceca5d9c3721efba0c567520da5532b2c7a590fd83627a598ea225d11c2cecbad404dc154120feb5ea6df0ded38f82ddf342c714cfd0c6143d1
+  languageName: node
+  linkType: hard
+
+"d3-color@npm:1 - 3, d3-color@npm:^3.1.0":
+  version: 3.1.0
+  resolution: "d3-color@npm:3.1.0"
+  checksum: 4931fbfda5d7c4b5cfa283a13c91a954f86e3b69d75ce588d06cde6c3628cebfc3af2069ccf225e982e8987c612aa7948b3932163ce15eb3c11cd7c003f3ee3b
+  languageName: node
+  linkType: hard
+
+"d3-delaunay@npm:^6.0.2":
+  version: 6.0.4
+  resolution: "d3-delaunay@npm:6.0.4"
+  dependencies:
+    delaunator: 5
+  checksum: ce6d267d5ef21a8aeadfe4606329fc80a22ab6e7748d47bc220bcc396ee8be84b77a5473033954c5ac4aa522d265ddc45d4165d30fe4787dd60a15ea66b9bbb4
+  languageName: node
+  linkType: hard
+
+"d3-dispatch@npm:1 - 3":
+  version: 3.0.1
+  resolution: "d3-dispatch@npm:3.0.1"
+  checksum: fdfd4a230f46463e28e5b22a45dd76d03be9345b605e1b5dc7d18bd7ebf504e6c00ae123fd6d03e23d9e2711e01f0e14ea89cd0632545b9f0c00b924ba4be223
+  languageName: node
+  linkType: hard
+
+"d3-dsv@npm:^3.0.1":
+  version: 3.0.1
+  resolution: "d3-dsv@npm:3.0.1"
+  dependencies:
+    commander: 7
+    iconv-lite: 0.6
+    rw: 1
+  bin:
+    csv2json: bin/dsv2json.js
+    csv2tsv: bin/dsv2dsv.js
+    dsv2dsv: bin/dsv2dsv.js
+    dsv2json: bin/dsv2json.js
+    json2csv: bin/json2dsv.js
+    json2dsv: bin/json2dsv.js
+    json2tsv: bin/json2dsv.js
+    tsv2csv: bin/dsv2dsv.js
+    tsv2json: bin/dsv2json.js
+  checksum: 5fc0723647269d5dccd181d74f2265920ab368a2868b0b4f55ffa2fecdfb7814390ea28622cd61ee5d9594ab262879509059544e9f815c54fe76fbfb4ffa4c8a
+  languageName: node
+  linkType: hard
+
+"d3-force@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "d3-force@npm:3.0.0"
+  dependencies:
+    d3-dispatch: 1 - 3
+    d3-quadtree: 1 - 3
+    d3-timer: 1 - 3
+  checksum: 6c7e96438cab62fa32aeadb0ade3297b62b51f81b1b38b0a60a5ec9fd627d74090c1189654d92df2250775f31b06812342f089f1d5947de9960a635ee3581def
+  languageName: node
+  linkType: hard
+
+"d3-format@npm:1 - 3, d3-format@npm:^3.1.0":
+  version: 3.1.0
+  resolution: "d3-format@npm:3.1.0"
+  checksum: f345ec3b8ad3cab19bff5dead395bd9f5590628eb97a389b1dd89f0b204c7c4fc1d9520f13231c2c7cf14b7c9a8cf10f8ef15bde2befbab41454a569bd706ca2
+  languageName: node
+  linkType: hard
+
+"d3-geo-projection@npm:^4.0.0":
+  version: 4.0.0
+  resolution: "d3-geo-projection@npm:4.0.0"
+  dependencies:
+    commander: 7
+    d3-array: 1 - 3
+    d3-geo: 1.12.0 - 3
+  bin:
+    geo2svg: bin/geo2svg.js
+    geograticule: bin/geograticule.js
+    geoproject: bin/geoproject.js
+    geoquantize: bin/geoquantize.js
+    geostitch: bin/geostitch.js
+  checksum: 631422b10dd78d1047ba5a3b073148bea27721060bd7087a5fa6c053ca80445d26432e505e0e3acbd6e0d76cf577c61bf9a5db70dabbc9310c493de1f7ff736d
+  languageName: node
+  linkType: hard
+
+"d3-geo@npm:1.12.0 - 3, d3-geo@npm:^3.1.0":
+  version: 3.1.0
+  resolution: "d3-geo@npm:3.1.0"
+  dependencies:
+    d3-array: 2.5.0 - 3
+  checksum: adf82b0c105c0c5951ae0a833d4dfc479a563791ad7938579fa14e1cffd623b469d8aa7a37dc413a327fb6ac56880f3da3f6c43d4abe3c923972dd98f34f37d1
+  languageName: node
+  linkType: hard
+
+"d3-hierarchy@npm:^3.1.2":
+  version: 3.1.2
+  resolution: "d3-hierarchy@npm:3.1.2"
+  checksum: 0fd946a8c5fd4686d43d3e11bbfc2037a145fda29d2261ccd0e36f70b66af6d7638e2c0c7112124d63fc3d3127197a00a6aecf676bd5bd392a94d7235a214263
+  languageName: node
+  linkType: hard
+
+"d3-interpolate@npm:1.2.0 - 3, d3-interpolate@npm:^3.0.1":
+  version: 3.0.1
+  resolution: "d3-interpolate@npm:3.0.1"
+  dependencies:
+    d3-color: 1 - 3
+  checksum: a42ba314e295e95e5365eff0f604834e67e4a3b3c7102458781c477bd67e9b24b6bb9d8e41ff5521050a3f2c7c0c4bbbb6e187fd586daa3980943095b267e78b
+  languageName: node
+  linkType: hard
+
+"d3-path@npm:^3.1.0":
+  version: 3.1.0
+  resolution: "d3-path@npm:3.1.0"
+  checksum: 2306f1bd9191e1eac895ec13e3064f732a85f243d6e627d242a313f9777756838a2215ea11562f0c7630c7c3b16a19ec1fe0948b1c82f3317fac55882f6ee5d8
+  languageName: node
+  linkType: hard
+
+"d3-quadtree@npm:1 - 3":
+  version: 3.0.1
+  resolution: "d3-quadtree@npm:3.0.1"
+  checksum: 5469d462763811475f34a7294d984f3eb100515b0585ca5b249656f6b1a6e99b20056a2d2e463cc9944b888896d2b1d07859c50f9c0cf23438df9cd2e3146066
+  languageName: node
+  linkType: hard
+
+"d3-scale@npm:^4.0.2":
+  version: 4.0.2
+  resolution: "d3-scale@npm:4.0.2"
+  dependencies:
+    d3-array: 2.10.0 - 3
+    d3-format: 1 - 3
+    d3-interpolate: 1.2.0 - 3
+    d3-time: 2.1.1 - 3
+    d3-time-format: 2 - 4
+  checksum: a9c770d283162c3bd11477c3d9d485d07f8db2071665f1a4ad23eec3e515e2cefbd369059ec677c9ac849877d1a765494e90e92051d4f21111aa56791c98729e
+  languageName: node
+  linkType: hard
+
+"d3-shape@npm:^3.2.0":
+  version: 3.2.0
+  resolution: "d3-shape@npm:3.2.0"
+  dependencies:
+    d3-path: ^3.1.0
+  checksum: de2af5fc9a93036a7b68581ca0bfc4aca2d5a328aa7ba7064c11aedd44d24f310c20c40157cb654359d4c15c3ef369f95ee53d71221017276e34172c7b719cfa
+  languageName: node
+  linkType: hard
+
+"d3-time-format@npm:2 - 4, d3-time-format@npm:^4.1.0":
+  version: 4.1.0
+  resolution: "d3-time-format@npm:4.1.0"
+  dependencies:
+    d3-time: 1 - 3
+  checksum: 7342bce28355378152bbd4db4e275405439cabba082d9cd01946d40581140481c8328456d91740b0fe513c51ec4a467f4471ffa390c7e0e30ea30e9ec98fcdf4
+  languageName: node
+  linkType: hard
+
+"d3-time@npm:1 - 3, d3-time@npm:2.1.1 - 3, d3-time@npm:^3.1.0":
+  version: 3.1.0
+  resolution: "d3-time@npm:3.1.0"
+  dependencies:
+    d3-array: 2 - 3
+  checksum: 613b435352a78d9f31b7f68540788186d8c331b63feca60ad21c88e9db1989fe888f97f242322ebd6365e45ec3fb206a4324cd4ca0dfffa1d9b5feb856ba00a7
+  languageName: node
+  linkType: hard
+
+"d3-timer@npm:1 - 3, d3-timer@npm:^3.0.1":
+  version: 3.0.1
+  resolution: "d3-timer@npm:3.0.1"
+  checksum: 1cfddf86d7bca22f73f2c427f52dfa35c49f50d64e187eb788dcad6e927625c636aa18ae4edd44d084eb9d1f81d8ca4ec305dae7f733c15846a824575b789d73
+  languageName: node
+  linkType: hard
+
 "dargs@npm:^7.0.0":
   version: 7.0.0
   resolution: "dargs@npm:7.0.0"
@@ -3777,6 +4597,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"debug@npm:^2.6.9":
+  version: 2.6.9
+  resolution: "debug@npm:2.6.9"
+  dependencies:
+    ms: 2.0.0
+  checksum: d2f51589ca66df60bf36e1fa6e4386b318c3f1e06772280eea5b1ae9fd3d05e9c2b7fd8a7d862457d00853c75b00451aa2d7459b924629ee385287a650f58fe6
+  languageName: node
+  linkType: hard
+
 "decamelize-keys@npm:^1.1.0":
   version: 1.1.1
   resolution: "decamelize-keys@npm:1.1.1"
@@ -3824,7 +4653,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"define-data-property@npm:^1.0.1":
+"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1":
   version: 1.1.1
   resolution: "define-data-property@npm:1.1.1"
   dependencies:
@@ -3869,6 +4698,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"delaunator@npm:5":
+  version: 5.0.0
+  resolution: "delaunator@npm:5.0.0"
+  dependencies:
+    robust-predicates: ^3.0.0
+  checksum: d6764188442b7f7c6bcacebd96edc00e35f542a96f1af3ef600e586bfb9849a3682c489c0ab423440c90bc4c7cac77f28761babff76fa29e193e1cf50a95b860
+  languageName: node
+  linkType: hard
+
 "delayed-stream@npm:~1.0.0":
   version: 1.0.0
   resolution: "delayed-stream@npm:1.0.0"
@@ -4016,9 +4854,9 @@ __metadata:
   linkType: hard
 
 "electron-to-chromium@npm:^1.4.535":
-  version: 1.4.557
-  resolution: "electron-to-chromium@npm:1.4.557"
-  checksum: 16f24e8d648489f0bfe7c2ffd4ada282bd68465862779f1f2e0afff5357e96536b075731c4b721dd27e2eae72008dc66d79e7bce6315e1c8f02947d41d988b78
+  version: 1.4.576
+  resolution: "electron-to-chromium@npm:1.4.576"
+  checksum: cf26065d95b0ecdd01f6daeec15b1e684a3b39680ae9f6d5d33c1391a6c1aa5ea2cb81cce699878e4fbb942328fc1076726f2d9ab1e8df9fbe02c84f1a5bbf1f
   languageName: node
   linkType: hard
 
@@ -4095,11 +4933,11 @@ __metadata:
   linkType: hard
 
 "envinfo@npm:^7.7.3, envinfo@npm:^7.7.4":
-  version: 7.10.0
-  resolution: "envinfo@npm:7.10.0"
+  version: 7.11.0
+  resolution: "envinfo@npm:7.11.0"
   bin:
     envinfo: dist/cli.js
-  checksum: 05e81a5768c42cbd5c580dc3f274db3401facadd53e9bd52e2aa49dfbb5d8b26f6181c25a6652d79618a6994185bd2b1c137673101690b147f758e4e71d42f7d
+  checksum: c45a7d20409d5f4cda72483b150d3816b15b434f2944d72c1495d8838bd7c4e7b2f32c12128ffb9b92b5f66f436237b8a525eb3a9a5da2d20013bc4effa28aef
   languageName: node
   linkType: hard
 
@@ -4120,24 +4958,24 @@ __metadata:
   linkType: hard
 
 "es-abstract@npm:^1.22.1":
-  version: 1.22.2
-  resolution: "es-abstract@npm:1.22.2"
+  version: 1.22.3
+  resolution: "es-abstract@npm:1.22.3"
   dependencies:
     array-buffer-byte-length: ^1.0.0
     arraybuffer.prototype.slice: ^1.0.2
     available-typed-arrays: ^1.0.5
-    call-bind: ^1.0.2
+    call-bind: ^1.0.5
     es-set-tostringtag: ^2.0.1
     es-to-primitive: ^1.2.1
     function.prototype.name: ^1.1.6
-    get-intrinsic: ^1.2.1
+    get-intrinsic: ^1.2.2
     get-symbol-description: ^1.0.0
     globalthis: ^1.0.3
     gopd: ^1.0.1
-    has: ^1.0.3
     has-property-descriptors: ^1.0.0
     has-proto: ^1.0.1
     has-symbols: ^1.0.3
+    hasown: ^2.0.0
     internal-slot: ^1.0.5
     is-array-buffer: ^3.0.2
     is-callable: ^1.2.7
@@ -4147,7 +4985,7 @@ __metadata:
     is-string: ^1.0.7
     is-typed-array: ^1.1.12
     is-weakref: ^1.0.2
-    object-inspect: ^1.12.3
+    object-inspect: ^1.13.1
     object-keys: ^1.1.1
     object.assign: ^4.1.4
     regexp.prototype.flags: ^1.5.1
@@ -4161,8 +4999,8 @@ __metadata:
     typed-array-byte-offset: ^1.0.0
     typed-array-length: ^1.0.4
     unbox-primitive: ^1.0.2
-    which-typed-array: ^1.1.11
-  checksum: cc70e592d360d7d729859013dee7a610c6b27ed8630df0547c16b0d16d9fe6505a70ee14d1af08d970fdd132b3f88c9ca7815ce72c9011608abf8ab0e55fc515
+    which-typed-array: ^1.1.13
+  checksum: b1bdc962856836f6e72be10b58dc128282bdf33771c7a38ae90419d920fc3b36cc5d2b70a222ad8016e3fc322c367bf4e9e89fc2bc79b7e933c05b218e83d79a
   languageName: node
   linkType: hard
 
@@ -4174,13 +5012,13 @@ __metadata:
   linkType: hard
 
 "es-set-tostringtag@npm:^2.0.1":
-  version: 2.0.1
-  resolution: "es-set-tostringtag@npm:2.0.1"
+  version: 2.0.2
+  resolution: "es-set-tostringtag@npm:2.0.2"
   dependencies:
-    get-intrinsic: ^1.1.3
-    has: ^1.0.3
+    get-intrinsic: ^1.2.2
     has-tostringtag: ^1.0.0
-  checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884
+    hasown: ^2.0.0
+  checksum: afcec3a4c9890ae14d7ec606204858441c801ff84f312538e1d1ccf1e5493c8b17bd672235df785f803756472cb4f2d49b87bde5237aef33411e74c22f194e07
   languageName: node
   linkType: hard
 
@@ -4270,16 +5108,17 @@ __metadata:
   linkType: hard
 
 "eslint@npm:^8.36.0":
-  version: 8.51.0
-  resolution: "eslint@npm:8.51.0"
+  version: 8.53.0
+  resolution: "eslint@npm:8.53.0"
   dependencies:
     "@eslint-community/eslint-utils": ^4.2.0
     "@eslint-community/regexpp": ^4.6.1
-    "@eslint/eslintrc": ^2.1.2
-    "@eslint/js": 8.51.0
-    "@humanwhocodes/config-array": ^0.11.11
+    "@eslint/eslintrc": ^2.1.3
+    "@eslint/js": 8.53.0
+    "@humanwhocodes/config-array": ^0.11.13
     "@humanwhocodes/module-importer": ^1.0.1
     "@nodelib/fs.walk": ^1.2.8
+    "@ungap/structured-clone": ^1.2.0
     ajv: ^6.12.4
     chalk: ^4.0.0
     cross-spawn: ^7.0.2
@@ -4312,7 +5151,7 @@ __metadata:
     text-table: ^0.2.0
   bin:
     eslint: bin/eslint.js
-  checksum: 214fa5d1fcb67af1b8992ce9584ccd85e1aa7a482f8b8ea5b96edc28fa838a18a3b69456db45fc1ed3ef95f1e9efa9714f737292dc681e572d471d02fda9649c
+  checksum: 2da808655c7aa4b33f8970ba30d96b453c3071cc4d6cd60d367163430677e32ff186b65270816b662d29139283138bff81f28dddeb2e73265495245a316ed02c
   languageName: node
   linkType: hard
 
@@ -4738,7 +5577,26 @@ __metadata:
   languageName: node
   linkType: hard
 
-"function-bind@npm:^1.1.1":
+"fsevents@npm:2.3.2":
+  version: 2.3.2
+  resolution: "fsevents@npm:2.3.2"
+  dependencies:
+    node-gyp: latest
+  checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f
+  conditions: os=darwin
+  languageName: node
+  linkType: hard
+
+"fsevents@patch:fsevents@2.3.2#~builtin<compat/fsevents>":
+  version: 2.3.2
+  resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin<compat/fsevents>::version=2.3.2&hash=df0bf1"
+  dependencies:
+    node-gyp: latest
+  conditions: os=darwin
+  languageName: node
+  linkType: hard
+
+"function-bind@npm:^1.1.2":
   version: 1.1.2
   resolution: "function-bind@npm:1.1.2"
   checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1
@@ -4803,15 +5661,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1":
-  version: 1.2.1
-  resolution: "get-intrinsic@npm:1.2.1"
+"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2":
+  version: 1.2.2
+  resolution: "get-intrinsic@npm:1.2.2"
   dependencies:
-    function-bind: ^1.1.1
-    has: ^1.0.3
+    function-bind: ^1.1.2
     has-proto: ^1.0.1
     has-symbols: ^1.0.3
-  checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f
+    hasown: ^2.0.0
+  checksum: 447ff0724df26829908dc033b62732359596fcf66027bc131ab37984afb33842d9cd458fd6cecadfe7eac22fd8a54b349799ed334cf2726025c921c7250e7417
   languageName: node
   linkType: hard
 
@@ -4964,7 +5822,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"glob@npm:^10.2.2":
+"glob@npm:^10.2.2, glob@npm:^10.3.10":
   version: 10.3.10
   resolution: "glob@npm:10.3.10"
   dependencies:
@@ -5168,11 +6026,11 @@ __metadata:
   linkType: hard
 
 "has-property-descriptors@npm:^1.0.0":
-  version: 1.0.0
-  resolution: "has-property-descriptors@npm:1.0.0"
+  version: 1.0.1
+  resolution: "has-property-descriptors@npm:1.0.1"
   dependencies:
-    get-intrinsic: ^1.1.1
-  checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb
+    get-intrinsic: ^1.2.2
+  checksum: 2bcc6bf6ec6af375add4e4b4ef586e43674850a91ad4d46666d0b28ba8e1fd69e424c7677d24d60f69470ad0afaa2f3197f508b20b0bb7dd99a8ab77ffc4b7c4
   languageName: node
   linkType: hard
 
@@ -5206,10 +6064,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"has@npm:^1.0.3":
-  version: 1.0.4
-  resolution: "has@npm:1.0.4"
-  checksum: 8a11ba062e0627c9578a1d08285401e39f1d071a9692ddf793199070edb5648b21c774dd733e2a181edd635bf6862731885f476f4ccf67c998d7a5ff7cef2550
+"hasown@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "hasown@npm:2.0.0"
+  dependencies:
+    function-bind: ^1.1.2
+  checksum: 6151c75ca12554565098641c98a40f4cc86b85b0fd5b6fe92360967e4605a4f9610f7757260b4e8098dd1c2ce7f4b095f2006fe72a570e3b6d2d28de0298c176
   languageName: node
   linkType: hard
 
@@ -5293,6 +6153,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"http-proxy-agent@npm:^7.0.0":
+  version: 7.0.0
+  resolution: "http-proxy-agent@npm:7.0.0"
+  dependencies:
+    agent-base: ^7.1.0
+    debug: ^4.3.4
+  checksum: 48d4fac997917e15f45094852b63b62a46d0c8a4f0b9c6c23ca26d27b8df8d178bed88389e604745e748bd9a01f5023e25093722777f0593c3f052009ff438b6
+  languageName: node
+  linkType: hard
+
 "https-proxy-agent@npm:^5.0.0":
   version: 5.0.1
   resolution: "https-proxy-agent@npm:5.0.1"
@@ -5303,6 +6173,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"https-proxy-agent@npm:^7.0.1":
+  version: 7.0.2
+  resolution: "https-proxy-agent@npm:7.0.2"
+  dependencies:
+    agent-base: ^7.0.2
+    debug: 4
+  checksum: 088969a0dd476ea7a0ed0a2cf1283013682b08f874c3bc6696c83fa061d2c157d29ef0ad3eb70a2046010bb7665573b2388d10fdcb3e410a66995e5248444292
+  languageName: node
+  linkType: hard
+
 "human-signals@npm:^2.1.0":
   version: 2.1.0
   resolution: "human-signals@npm:2.1.0"
@@ -5319,6 +6199,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"iconv-lite@npm:0.6, iconv-lite@npm:^0.6.2":
+  version: 0.6.3
+  resolution: "iconv-lite@npm:0.6.3"
+  dependencies:
+    safer-buffer: ">= 2.1.2 < 3.0.0"
+  checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf
+  languageName: node
+  linkType: hard
+
 "iconv-lite@npm:^0.4.24":
   version: 0.4.24
   resolution: "iconv-lite@npm:0.4.24"
@@ -5328,15 +6217,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"iconv-lite@npm:^0.6.2":
-  version: 0.6.3
-  resolution: "iconv-lite@npm:0.6.3"
-  dependencies:
-    safer-buffer: ">= 2.1.2 < 3.0.0"
-  checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf
-  languageName: node
-  linkType: hard
-
 "icss-utils@npm:^5.0.0, icss-utils@npm:^5.1.0":
   version: 5.1.0
   resolution: "icss-utils@npm:5.1.0"
@@ -5445,6 +6325,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"inherits@npm:2.0.3":
+  version: 2.0.3
+  resolution: "inherits@npm:2.0.3"
+  checksum: 78cb8d7d850d20a5e9a7f3620db31483aa00ad5f722ce03a55b110e5a723539b3716a3b463e2b96ce3fe286f33afc7c131fa2f91407528ba80cea98a7545d4c0
+  languageName: node
+  linkType: hard
+
 "ini@npm:^1.3.2, ini@npm:^1.3.4, ini@npm:^1.3.5":
   version: 1.3.8
   resolution: "ini@npm:1.3.8"
@@ -5514,13 +6401,20 @@ __metadata:
   linkType: hard
 
 "internal-slot@npm:^1.0.5":
-  version: 1.0.5
-  resolution: "internal-slot@npm:1.0.5"
+  version: 1.0.6
+  resolution: "internal-slot@npm:1.0.6"
   dependencies:
-    get-intrinsic: ^1.2.0
-    has: ^1.0.3
+    get-intrinsic: ^1.2.2
+    hasown: ^2.0.0
     side-channel: ^1.0.4
-  checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a
+  checksum: 7872454888047553ce97a3fa1da7cc054a28ec5400a9c2e9f4dbe4fe7c1d041cb8e8301467614b80d4246d50377aad2fb58860b294ed74d6700cc346b6f89549
+  languageName: node
+  linkType: hard
+
+"internmap@npm:1 - 2":
+  version: 2.0.3
+  resolution: "internmap@npm:2.0.3"
+  checksum: 7ca41ec6aba8f0072fc32fa8a023450a9f44503e2d8e403583c55714b25efd6390c38a87161ec456bf42d7bc83aab62eb28f5aef34876b1ac4e60693d5e1d241
   languageName: node
   linkType: hard
 
@@ -5594,11 +6488,11 @@ __metadata:
   linkType: hard
 
 "is-core-module@npm:^2.13.0, is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.1":
-  version: 2.13.0
-  resolution: "is-core-module@npm:2.13.0"
+  version: 2.13.1
+  resolution: "is-core-module@npm:2.13.1"
   dependencies:
-    has: ^1.0.3
-  checksum: 053ab101fb390bfeb2333360fd131387bed54e476b26860dc7f5a700bbf34a0ec4454f7c8c4d43e8a0030957e4b3db6e16d35e1890ea6fb654c833095e040355
+    hasown: ^2.0.0
+  checksum: 256559ee8a9488af90e4bad16f5583c6d59e92f0742e9e8bb4331e758521ee86b810b93bae44f390766ffbc518a0488b18d9dab7da9a5ff997d499efc9403f7c
   languageName: node
   linkType: hard
 
@@ -5848,6 +6742,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"isexe@npm:^3.1.1":
+  version: 3.1.1
+  resolution: "isexe@npm:3.1.1"
+  checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e
+  languageName: node
+  linkType: hard
+
 "isobject@npm:^3.0.1":
   version: 3.0.1
   resolution: "isobject@npm:3.0.1"
@@ -6006,6 +6907,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"json-stringify-pretty-compact@npm:~3.0.0":
+  version: 3.0.0
+  resolution: "json-stringify-pretty-compact@npm:3.0.0"
+  checksum: 01ab5c5c8260299414868d96db97f53aef93c290fe469edd9a1363818e795006e01c952fa2fd7b47cbbab506d5768998eccc25e1da4fa2ccfebd1788c6098791
+  languageName: node
+  linkType: hard
+
 "json-stringify-safe@npm:^5.0.1":
   version: 5.0.1
   resolution: "json-stringify-safe@npm:5.0.1"
@@ -6075,6 +6983,15 @@ __metadata:
   languageName: unknown
   linkType: soft
 
+"jupyterlab-jupytext-ui-tests@workspace:packages/jupyterlab-jupytext-extension/ui-tests":
+  version: 0.0.0-use.local
+  resolution: "jupyterlab-jupytext-ui-tests@workspace:packages/jupyterlab-jupytext-extension/ui-tests"
+  dependencies:
+    "@jupyterlab/galata": ^5.0.0
+    "@playwright/test": ^1.32.0
+  languageName: unknown
+  linkType: soft
+
 "jupyterlab-jupytext@workspace:packages/jupyterlab-jupytext-extension":
   version: 0.0.0-use.local
   resolution: "jupyterlab-jupytext@workspace:packages/jupyterlab-jupytext-extension"
@@ -6083,16 +7000,21 @@ __metadata:
     "@jupyterlab/apputils": ^4.0.0
     "@jupyterlab/builder": ^4.0.0
     "@jupyterlab/codeeditor": ^4.0.0
+    "@jupyterlab/docregistry": ^4.0.0
+    "@jupyterlab/filebrowser": ^4.0.0
+    "@jupyterlab/launcher": ^4.0.0
     "@jupyterlab/nbformat": ^4.0.0
     "@jupyterlab/notebook": ^4.0.0
     "@jupyterlab/rendermime": ^4.0.0
     "@jupyterlab/settingregistry": ^4.0.0
     "@jupyterlab/translation": ^4.0.0
     "@jupyterlab/ui-components": ^4.0.0
+    "@lumino/coreutils": ^2.0.0
+    "@lumino/disposable": ^2.0.0
     jupyterlab-rise: ^0.41.0
     npm-run-all: ^4.1.5
-    rimraf: ^3.0.2
-    typescript: ~5.0.1
+    rimraf: ^4.4.1
+    typescript: ~5.0.2
   languageName: unknown
   linkType: soft
 
@@ -6459,6 +7381,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0":
+  version: 10.0.1
+  resolution: "lru-cache@npm:10.0.1"
+  checksum: 06f8d0e1ceabd76bb6f644a26dbb0b4c471b79c7b514c13c6856113879b3bf369eb7b497dad4ff2b7e2636db202412394865b33c332100876d838ad1372f0181
+  languageName: node
+  linkType: hard
+
 "lru-cache@npm:^6.0.0":
   version: 6.0.0
   resolution: "lru-cache@npm:6.0.0"
@@ -6475,13 +7404,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"lru-cache@npm:^9.1.1 || ^10.0.0":
-  version: 10.0.1
-  resolution: "lru-cache@npm:10.0.1"
-  checksum: 06f8d0e1ceabd76bb6f644a26dbb0b4c471b79c7b514c13c6856113879b3bf369eb7b497dad4ff2b7e2636db202412394865b33c332100876d838ad1372f0181
-  languageName: node
-  linkType: hard
-
 "make-dir@npm:3.1.0":
   version: 3.1.0
   resolution: "make-dir@npm:3.1.0"
@@ -6501,7 +7423,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"make-fetch-happen@npm:^10.0.6":
+"make-fetch-happen@npm:^10.0.3, make-fetch-happen@npm:^10.0.6":
   version: 10.2.1
   resolution: "make-fetch-happen@npm:10.2.1"
   dependencies:
@@ -6525,7 +7447,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"make-fetch-happen@npm:^11.0.0, make-fetch-happen@npm:^11.0.1, make-fetch-happen@npm:^11.0.3, make-fetch-happen@npm:^11.1.1":
+"make-fetch-happen@npm:^11.0.0, make-fetch-happen@npm:^11.0.1, make-fetch-happen@npm:^11.1.1":
   version: 11.1.1
   resolution: "make-fetch-happen@npm:11.1.1"
   dependencies:
@@ -6548,6 +7470,25 @@ __metadata:
   languageName: node
   linkType: hard
 
+"make-fetch-happen@npm:^13.0.0":
+  version: 13.0.0
+  resolution: "make-fetch-happen@npm:13.0.0"
+  dependencies:
+    "@npmcli/agent": ^2.0.0
+    cacache: ^18.0.0
+    http-cache-semantics: ^4.1.1
+    is-lambda: ^1.0.1
+    minipass: ^7.0.2
+    minipass-fetch: ^3.0.0
+    minipass-flush: ^1.0.5
+    minipass-pipeline: ^1.2.4
+    negotiator: ^0.6.3
+    promise-retry: ^2.0.1
+    ssri: ^10.0.0
+  checksum: 7c7a6d381ce919dd83af398b66459a10e2fe8f4504f340d1d090d3fa3d1b0c93750220e1d898114c64467223504bd258612ba83efbc16f31b075cd56de24b4af
+  languageName: node
+  linkType: hard
+
 "map-obj@npm:^1.0.0":
   version: 1.0.1
   resolution: "map-obj@npm:1.0.1"
@@ -6869,7 +7810,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.3":
+"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3":
   version: 7.0.4
   resolution: "minipass@npm:7.0.4"
   checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21
@@ -6913,6 +7854,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"ms@npm:2.0.0":
+  version: 2.0.0
+  resolution: "ms@npm:2.0.0"
+  checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4
+  languageName: node
+  linkType: hard
+
 "ms@npm:2.1.2":
   version: 2.1.2
   resolution: "ms@npm:2.1.2"
@@ -6948,11 +7896,11 @@ __metadata:
   linkType: hard
 
 "nanoid@npm:^3.3.6":
-  version: 3.3.6
-  resolution: "nanoid@npm:3.3.6"
+  version: 3.3.7
+  resolution: "nanoid@npm:3.3.7"
   bin:
     nanoid: bin/nanoid.cjs
-  checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3
+  checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2
   languageName: node
   linkType: hard
 
@@ -7039,15 +7987,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"node-gyp@npm:^9.0.0, node-gyp@npm:latest":
-  version: 9.4.0
-  resolution: "node-gyp@npm:9.4.0"
+"node-gyp@npm:^9.0.0":
+  version: 9.4.1
+  resolution: "node-gyp@npm:9.4.1"
   dependencies:
     env-paths: ^2.2.0
     exponential-backoff: ^3.1.1
     glob: ^7.1.4
     graceful-fs: ^4.2.6
-    make-fetch-happen: ^11.0.3
+    make-fetch-happen: ^10.0.3
     nopt: ^6.0.0
     npmlog: ^6.0.0
     rimraf: ^3.0.2
@@ -7056,7 +8004,27 @@ __metadata:
     which: ^2.0.2
   bin:
     node-gyp: bin/node-gyp.js
-  checksum: 78b404e2e0639d64e145845f7f5a3cb20c0520cdaf6dda2f6e025e9b644077202ea7de1232396ba5bde3fee84cdc79604feebe6ba3ec84d464c85d407bb5da99
+  checksum: 8576c439e9e925ab50679f87b7dfa7aa6739e42822e2ad4e26c36341c0ba7163fdf5a946f0a67a476d2f24662bc40d6c97bd9e79ced4321506738e6b760a1577
+  languageName: node
+  linkType: hard
+
+"node-gyp@npm:latest":
+  version: 10.0.1
+  resolution: "node-gyp@npm:10.0.1"
+  dependencies:
+    env-paths: ^2.2.0
+    exponential-backoff: ^3.1.1
+    glob: ^10.3.10
+    graceful-fs: ^4.2.6
+    make-fetch-happen: ^13.0.0
+    nopt: ^7.0.0
+    proc-log: ^3.0.0
+    semver: ^7.3.5
+    tar: ^6.1.2
+    which: ^4.0.0
+  bin:
+    node-gyp: bin/node-gyp.js
+  checksum: 60a74e66d364903ce02049966303a57f898521d139860ac82744a5fdd9f7b7b3b61f75f284f3bfe6e6add3b8f1871ce305a1d41f775c7482de837b50c792223f
   languageName: node
   linkType: hard
 
@@ -7449,10 +8417,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0":
-  version: 1.13.0
-  resolution: "object-inspect@npm:1.13.0"
-  checksum: 21353e910a3079466cb44adca71d8bef15bd8b87e518eb68bb33d82c5c70b83193993edce432cc92268f7dd02c4a8ab338663a011844367d0bd0559f6dde1fed
+"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0":
+  version: 1.13.1
+  resolution: "object-inspect@npm:1.13.1"
+  checksum: 7d9fa9221de3311dcb5c7c307ee5dc011cdd31dc43624b7c184b3840514e118e05ef0002be5388304c416c0eb592feb46e983db12577fc47e47d5752fbbfb61f
   languageName: node
   linkType: hard
 
@@ -7873,6 +8841,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"path@npm:~0.12.7":
+  version: 0.12.7
+  resolution: "path@npm:0.12.7"
+  dependencies:
+    process: ^0.11.1
+    util: ^0.10.3
+  checksum: 5dedb71e78fc008fcba797defc0b4e1cf06c1f18e0a631e03ba5bb505136f587ff017afc14f9a3d481cbe77aeedff7dc0c1d2ce4d820c1ebf3c4281ca49423a1
+  languageName: node
+  linkType: hard
+
 "picocolors@npm:^1.0.0":
   version: 1.0.0
   resolution: "picocolors@npm:1.0.0"
@@ -7933,6 +8911,30 @@ __metadata:
   languageName: node
   linkType: hard
 
+"playwright-core@npm:1.39.0":
+  version: 1.39.0
+  resolution: "playwright-core@npm:1.39.0"
+  bin:
+    playwright-core: cli.js
+  checksum: 556e78dee4f9890facf2af8249972e0d6e01a5ae98737b0f6b0166c660a95ffee4cb79350335b1ef96430a0ef01d3669daae9099fa46c8d403d11c623988238b
+  languageName: node
+  linkType: hard
+
+"playwright@npm:1.39.0":
+  version: 1.39.0
+  resolution: "playwright@npm:1.39.0"
+  dependencies:
+    fsevents: 2.3.2
+    playwright-core: 1.39.0
+  dependenciesMeta:
+    fsevents:
+      optional: true
+  bin:
+    playwright: cli.js
+  checksum: 96d8ca5aa25465c1c5d554d0d6071981d55e22477800ff8f5d47a53ca75193d60ece2df538a01b7165b3277dd5493c67603a5acda713029df7fbd95ce2417bc9
+  languageName: node
+  linkType: hard
+
 "postcss-media-query-parser@npm:^0.2.3":
   version: 0.2.3
   resolution: "postcss-media-query-parser@npm:0.2.3"
@@ -8085,7 +9087,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"process@npm:^0.11.10":
+"process@npm:^0.11.1, process@npm:^0.11.10":
   version: 0.11.10
   resolution: "process@npm:0.11.10"
   checksum: bfcce49814f7d172a6e6a14d5fa3ac92cc3d0c3b9feb1279774708a719e19acd673995226351a082a9ae99978254e320ccda4240ddc474ba31a76c79491ca7c3
@@ -8165,9 +9167,9 @@ __metadata:
   linkType: hard
 
 "punycode@npm:^2.1.0, punycode@npm:^2.1.1":
-  version: 2.3.0
-  resolution: "punycode@npm:2.3.0"
-  checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200
+  version: 2.3.1
+  resolution: "punycode@npm:2.3.1"
+  checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2
   languageName: node
   linkType: hard
 
@@ -8377,20 +9379,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"readable-stream@npm:^4.1.0":
-  version: 4.4.2
-  resolution: "readable-stream@npm:4.4.2"
-  dependencies:
-    abort-controller: ^3.0.0
-    buffer: ^6.0.3
-    events: ^3.3.0
-    process: ^0.11.10
-    string_decoder: ^1.3.0
-  checksum: 6f4063763dbdb52658d22d3f49ca976420e1fbe16bbd241f744383715845350b196a2f08b8d6330f8e219153dff34b140aeefd6296da828e1041a7eab1f20d5e
-  languageName: node
-  linkType: hard
-
-"readable-stream@npm:~2.3.6":
+"readable-stream@npm:^2.1.4, readable-stream@npm:~2.3.6":
   version: 2.3.8
   resolution: "readable-stream@npm:2.3.8"
   dependencies:
@@ -8405,6 +9394,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"readable-stream@npm:^4.1.0":
+  version: 4.4.2
+  resolution: "readable-stream@npm:4.4.2"
+  dependencies:
+    abort-controller: ^3.0.0
+    buffer: ^6.0.3
+    events: ^3.3.0
+    process: ^0.11.10
+    string_decoder: ^1.3.0
+  checksum: 6f4063763dbdb52658d22d3f49ca976420e1fbe16bbd241f744383715845350b196a2f08b8d6330f8e219153dff34b140aeefd6296da828e1041a7eab1f20d5e
+  languageName: node
+  linkType: hard
+
 "rechoir@npm:^0.8.0":
   version: 0.8.0
   resolution: "rechoir@npm:0.8.0"
@@ -8424,6 +9426,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"regexp-match-indices@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "regexp-match-indices@npm:1.0.2"
+  dependencies:
+    regexp-tree: ^0.1.11
+  checksum: 8cc779f6cf8f404ead828d09970a7d4bd66bd78d43ab9eb2b5e65f2ef2ba1ed53536f5b5fa839fb90b350365fb44b6a851c7f16289afc3f37789c113ab2a7916
+  languageName: node
+  linkType: hard
+
+"regexp-tree@npm:^0.1.11":
+  version: 0.1.27
+  resolution: "regexp-tree@npm:0.1.27"
+  bin:
+    regexp-tree: bin/regexp-tree
+  checksum: 129aebb34dae22d6694ab2ac328be3f99105143737528ab072ef624d599afecbcfae1f5c96a166fa9e5f64fa1ecf30b411c4691e7924c3e11bbaf1712c260c54
+  languageName: node
+  linkType: hard
+
 "regexp.prototype.flags@npm:^1.5.1":
   version: 1.5.1
   resolution: "regexp.prototype.flags@npm:1.5.1"
@@ -8551,6 +9571,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"robust-predicates@npm:^3.0.0":
+  version: 3.0.2
+  resolution: "robust-predicates@npm:3.0.2"
+  checksum: 36854c1321548ceca96d36ad9d6e0a5a512986029ec6929ad6ed3ec1612c22cc8b46cc72d2c5674af42e8074a119d793f6f0ea3a5b51373e3ab926c64b172d7a
+  languageName: node
+  linkType: hard
+
 "run-async@npm:^2.4.0":
   version: 2.4.1
   resolution: "run-async@npm:2.4.1"
@@ -8567,6 +9594,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"rw@npm:1":
+  version: 1.3.3
+  resolution: "rw@npm:1.3.3"
+  checksum: c20d82421f5a71c86a13f76121b751553a99cd4a70ea27db86f9b23f33db941f3f06019c30f60d50c356d0bd674c8e74764ac146ea55e217c091bde6fba82aa3
+  languageName: node
+  linkType: hard
+
 "rxjs@npm:^7.5.5":
   version: 7.8.1
   resolution: "rxjs@npm:7.8.1"
@@ -8733,6 +9767,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"set-function-length@npm:^1.1.1":
+  version: 1.1.1
+  resolution: "set-function-length@npm:1.1.1"
+  dependencies:
+    define-data-property: ^1.1.1
+    get-intrinsic: ^1.2.1
+    gopd: ^1.0.1
+    has-property-descriptors: ^1.0.0
+  checksum: c131d7569cd7e110cafdfbfbb0557249b538477624dfac4fc18c376d879672fa52563b74029ca01f8f4583a8acb35bb1e873d573a24edb80d978a7ee607c6e06
+  languageName: node
+  linkType: hard
+
 "set-function-name@npm:^2.0.0":
   version: 2.0.1
   resolution: "set-function-name@npm:2.0.1"
@@ -8868,7 +9914,18 @@ __metadata:
   languageName: node
   linkType: hard
 
-"socks@npm:^2.6.2":
+"socks-proxy-agent@npm:^8.0.1":
+  version: 8.0.2
+  resolution: "socks-proxy-agent@npm:8.0.2"
+  dependencies:
+    agent-base: ^7.0.2
+    debug: ^4.3.4
+    socks: ^2.7.1
+  checksum: 4fb165df08f1f380881dcd887b3cdfdc1aba3797c76c1e9f51d29048be6e494c5b06d68e7aea2e23df4572428f27a3ec22b3d7c75c570c5346507433899a4b6d
+  languageName: node
+  linkType: hard
+
+"socks@npm:^2.6.2, socks@npm:^2.7.1":
   version: 2.7.1
   resolution: "socks@npm:2.7.1"
   dependencies:
@@ -9328,6 +10385,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"systeminformation@npm:^5.8.6":
+  version: 5.21.15
+  resolution: "systeminformation@npm:5.21.15"
+  bin:
+    systeminformation: lib/cli.js
+  checksum: 664d4821c23ab704c02fc020ac5c140849a415bdee4be8188256a6ef5d5f418b4373383636bfff94d10304e47b1765ba9c8bec04fd05ec81e7552e1e94593054
+  conditions: (os=darwin | os=linux | os=win32 | os=freebsd | os=openbsd | os=netbsd | os=sunos | os=android)
+  languageName: node
+  linkType: hard
+
 "table@npm:^6.8.1":
   version: 6.8.1
   resolution: "table@npm:6.8.1"
@@ -9439,8 +10506,8 @@ __metadata:
   linkType: hard
 
 "terser@npm:^5.16.8":
-  version: 5.22.0
-  resolution: "terser@npm:5.22.0"
+  version: 5.24.0
+  resolution: "terser@npm:5.24.0"
   dependencies:
     "@jridgewell/source-map": ^0.3.3
     acorn: ^8.8.2
@@ -9448,7 +10515,7 @@ __metadata:
     source-map-support: ~0.5.20
   bin:
     terser: bin/terser
-  checksum: ee95981c54ebd381e0b7f5872c646e7a05543e53960f8e0c2f240863c368989d43a3ca80b7e9f691683c92ba199eb4b91d61785fef0b9ca4a887eb55866001f4
+  checksum: d88f774b6fa711a234fcecefd7657f99189c367e17dbe95a51c2776d426ad0e4d98d1ffe6edfdf299877c7602e495bdd711d21b2caaec188410795e5447d0f6c
   languageName: node
   linkType: hard
 
@@ -9519,6 +10586,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"topojson-client@npm:^3.1.0":
+  version: 3.1.0
+  resolution: "topojson-client@npm:3.1.0"
+  dependencies:
+    commander: 2
+  bin:
+    topo2geo: bin/topo2geo
+    topomerge: bin/topomerge
+    topoquantize: bin/topoquantize
+  checksum: 8c029a4f18324ace0b8b55dd90edbd40c9e3c6de18bafbb5da37ca20ebf20e26fbd4420891acb3c2c264e214185f7557871f5651a9eee517028663be98d836de
+  languageName: node
+  linkType: hard
+
 "tr46@npm:^2.1.0":
   version: 2.1.0
   resolution: "tr46@npm:2.1.0"
@@ -9567,7 +10647,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0":
+"tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:~2.6.2":
   version: 2.6.2
   resolution: "tslib@npm:2.6.2"
   checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad
@@ -9718,7 +10798,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typescript@npm:~5.0.1":
+"typescript@npm:~5.0.2":
   version: 5.0.4
   resolution: "typescript@npm:5.0.4"
   bin:
@@ -9738,7 +10818,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typescript@patch:typescript@~5.0.1#~builtin<compat/typescript>":
+"typescript@patch:typescript@~5.0.2#~builtin<compat/typescript>":
   version: 5.0.4
   resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin<compat/typescript>::version=5.0.4&hash=85af82"
   bin:
@@ -9779,10 +10859,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"undici-types@npm:~5.25.1":
-  version: 5.25.3
-  resolution: "undici-types@npm:5.25.3"
-  checksum: ec9d2cc36520cbd9fbe3b3b6c682a87fe5be214699e1f57d1e3d9a2cb5be422e62735f06e0067dc325fd3dd7404c697e4d479f9147dc8a804e049e29f357f2ff
+"undici-types@npm:~5.26.4":
+  version: 5.26.5
+  resolution: "undici-types@npm:5.26.5"
+  checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487
   languageName: node
   linkType: hard
 
@@ -9832,16 +10912,16 @@ __metadata:
   linkType: hard
 
 "universal-user-agent@npm:^6.0.0":
-  version: 6.0.0
-  resolution: "universal-user-agent@npm:6.0.0"
-  checksum: 5092bbc80dd0d583cef0b62c17df0043193b74f425112ea6c1f69bc5eda21eeec7a08d8c4f793a277eb2202ffe9b44bec852fa3faff971234cd209874d1b79ef
+  version: 6.0.1
+  resolution: "universal-user-agent@npm:6.0.1"
+  checksum: fdc8e1ae48a05decfc7ded09b62071f571c7fe0bd793d700704c80cea316101d4eac15cc27ed2bb64f4ce166d2684777c3198b9ab16034f547abea0d3aa1c93c
   languageName: node
   linkType: hard
 
 "universalify@npm:^2.0.0":
-  version: 2.0.0
-  resolution: "universalify@npm:2.0.0"
-  checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44
+  version: 2.0.1
+  resolution: "universalify@npm:2.0.1"
+  checksum: ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60
   languageName: node
   linkType: hard
 
@@ -9892,6 +10972,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"util@npm:^0.10.3":
+  version: 0.10.4
+  resolution: "util@npm:0.10.4"
+  dependencies:
+    inherits: 2.0.3
+  checksum: 913f9a90d05a60e91f91af01b8bd37e06bca4cc02d7b49e01089f9d5b78be2fffd61fb1a41b517de7238c5fc7337fa939c62d1fb4eb82e014894c7bee6637aaf
+  languageName: node
+  linkType: hard
+
 "uuid@npm:8.3.2":
   version: 8.3.2
   resolution: "uuid@npm:8.3.2"
@@ -9992,6 +11081,403 @@ __metadata:
   languageName: node
   linkType: hard
 
+"vega-canvas@npm:^1.2.6, vega-canvas@npm:^1.2.7":
+  version: 1.2.7
+  resolution: "vega-canvas@npm:1.2.7"
+  checksum: 6ff92fcdf0c359f2f662909c859a7f4cb4a502436136ab2f4c02373c47a621996ec0eea23e2108f11d62a618be301de86cd8528b5058c2e207a53ddd7ff58d1b
+  languageName: node
+  linkType: hard
+
+"vega-crossfilter@npm:~4.1.1":
+  version: 4.1.1
+  resolution: "vega-crossfilter@npm:4.1.1"
+  dependencies:
+    d3-array: ^3.2.2
+    vega-dataflow: ^5.7.5
+    vega-util: ^1.17.1
+  checksum: e399f7e92d7ba273ad5c1a9e29d362a9ec7feaeacb976eff3aa205b318382fb37a9fac3150ec1cb806364cd2b2cb54d5f23aea3285db684df2b4c27836422464
+  languageName: node
+  linkType: hard
+
+"vega-dataflow@npm:^5.7.3, vega-dataflow@npm:^5.7.5, vega-dataflow@npm:~5.7.5":
+  version: 5.7.5
+  resolution: "vega-dataflow@npm:5.7.5"
+  dependencies:
+    vega-format: ^1.1.1
+    vega-loader: ^4.5.1
+    vega-util: ^1.17.1
+  checksum: 917ed63e88b0871169a883f68da127a404d88e50c9ed6fa3f063a706016b064594fb804a2bf99f09bc4a899819cac320bdde12467edc861af1acc024552dd202
+  languageName: node
+  linkType: hard
+
+"vega-encode@npm:~4.9.2":
+  version: 4.9.2
+  resolution: "vega-encode@npm:4.9.2"
+  dependencies:
+    d3-array: ^3.2.2
+    d3-interpolate: ^3.0.1
+    vega-dataflow: ^5.7.5
+    vega-scale: ^7.3.0
+    vega-util: ^1.17.1
+  checksum: fcba123d2efb865b4f6cf8e9d64e0752ebae163dcfe61013f4874f7fe6fce3003ea9dd83b89db3ffab2a1530532a7c902dd24dfec226eb53d08dcf69189f308d
+  languageName: node
+  linkType: hard
+
+"vega-event-selector@npm:^3.0.1, vega-event-selector@npm:~3.0.1":
+  version: 3.0.1
+  resolution: "vega-event-selector@npm:3.0.1"
+  checksum: 66d09b5800a19a9b0c75f28811b140a1a2e70e84be6d6f87c568cdbce6e17c8e195f130f4e3de5d6dc737142d1f46f4fe7645177e154582cc8ba27c6845b54e8
+  languageName: node
+  linkType: hard
+
+"vega-expression@npm:^5.0.1, vega-expression@npm:^5.1.0, vega-expression@npm:~5.1.0":
+  version: 5.1.0
+  resolution: "vega-expression@npm:5.1.0"
+  dependencies:
+    "@types/estree": ^1.0.0
+    vega-util: ^1.17.1
+  checksum: 0355ebb6edd8f2ccc2dcf277a29b42b13f971725443212ce8a64cb8a02049f75f0add7ca9afcd3bc6744b93be791b526e7f983d9080d5052e9b0ca55bd488ae5
+  languageName: node
+  linkType: hard
+
+"vega-force@npm:~4.2.0":
+  version: 4.2.0
+  resolution: "vega-force@npm:4.2.0"
+  dependencies:
+    d3-force: ^3.0.0
+    vega-dataflow: ^5.7.5
+    vega-util: ^1.17.1
+  checksum: 8a371ca8d0892bc3e932cc279bbf54fe8b88e2b384c42f8df9877c801191953f3ee3e2f516f675a69ecb052ed081232dfb3438989620e8ad5c2a316ccee60277
+  languageName: node
+  linkType: hard
+
+"vega-format@npm:^1.1.1, vega-format@npm:~1.1.1":
+  version: 1.1.1
+  resolution: "vega-format@npm:1.1.1"
+  dependencies:
+    d3-array: ^3.2.2
+    d3-format: ^3.1.0
+    d3-time-format: ^4.1.0
+    vega-time: ^2.1.1
+    vega-util: ^1.17.1
+  checksum: d506acb8611a6340ff419ebf308a758a54aaf3cf141863553df83980dcf8dc7bf806bee257d11a52d43682d159d7be03ab8a92bdd4d018d8c9f39a70c45cb197
+  languageName: node
+  linkType: hard
+
+"vega-functions@npm:^5.13.1, vega-functions@npm:~5.13.2":
+  version: 5.13.2
+  resolution: "vega-functions@npm:5.13.2"
+  dependencies:
+    d3-array: ^3.2.2
+    d3-color: ^3.1.0
+    d3-geo: ^3.1.0
+    vega-dataflow: ^5.7.5
+    vega-expression: ^5.1.0
+    vega-scale: ^7.3.0
+    vega-scenegraph: ^4.10.2
+    vega-selections: ^5.4.1
+    vega-statistics: ^1.8.1
+    vega-time: ^2.1.1
+    vega-util: ^1.17.1
+  checksum: 178498cf93c3d9ef392fb57a5c7992dbb9118c546a6acb4cff9783f911fb30dbf50634cbfd6e3a9bc358c4aec9a571bd55f9cf3de551213cd386f152ac882986
+  languageName: node
+  linkType: hard
+
+"vega-geo@npm:~4.4.1":
+  version: 4.4.1
+  resolution: "vega-geo@npm:4.4.1"
+  dependencies:
+    d3-array: ^3.2.2
+    d3-color: ^3.1.0
+    d3-geo: ^3.1.0
+    vega-canvas: ^1.2.7
+    vega-dataflow: ^5.7.5
+    vega-projection: ^1.6.0
+    vega-statistics: ^1.8.1
+    vega-util: ^1.17.1
+  checksum: e9c62d9134c2449a1a80cd5cb71ed6dc455d893a36fdcb1a696bcae3897670c32687cf14a0f366b0ec76905e5be406131dc671e5d607ffcbef74e94b8c697007
+  languageName: node
+  linkType: hard
+
+"vega-hierarchy@npm:~4.1.1":
+  version: 4.1.1
+  resolution: "vega-hierarchy@npm:4.1.1"
+  dependencies:
+    d3-hierarchy: ^3.1.2
+    vega-dataflow: ^5.7.5
+    vega-util: ^1.17.1
+  checksum: beb23948922f1b52bf03b836d71d3a5a36db3a6bfe2af74b6a5fc45a2e2e877226313e2389772be62a459728467618175d8c02a07e88330844fdec45fd5f69ac
+  languageName: node
+  linkType: hard
+
+"vega-label@npm:~1.2.1":
+  version: 1.2.1
+  resolution: "vega-label@npm:1.2.1"
+  dependencies:
+    vega-canvas: ^1.2.6
+    vega-dataflow: ^5.7.3
+    vega-scenegraph: ^4.9.2
+    vega-util: ^1.15.2
+  checksum: 2704c99328ead677441e746acd8f4529301437d08b2758933fc13353d2eab9af353e4ebcc4ff1f09f41d600401b097e2df3c9e8e56d4861e5216222dd9e29185
+  languageName: node
+  linkType: hard
+
+"vega-lite@npm:^5.6.1":
+  version: 5.16.1
+  resolution: "vega-lite@npm:5.16.1"
+  dependencies:
+    json-stringify-pretty-compact: ~3.0.0
+    tslib: ~2.6.2
+    vega-event-selector: ~3.0.1
+    vega-expression: ~5.1.0
+    vega-util: ~1.17.2
+    yargs: ~17.7.2
+  peerDependencies:
+    vega: ^5.24.0
+  bin:
+    vl2pdf: bin/vl2pdf
+    vl2png: bin/vl2png
+    vl2svg: bin/vl2svg
+    vl2vg: bin/vl2vg
+  checksum: 4de40199b664700d9a8f0fa3571f5addc32e07c1ca12ef36c9238e23368abfa601ead7a27874249d6a31a7bea1cdf0a867d486dd6ff98e1d0ac4a910df418550
+  languageName: node
+  linkType: hard
+
+"vega-loader@npm:^4.5.1, vega-loader@npm:~4.5.1":
+  version: 4.5.1
+  resolution: "vega-loader@npm:4.5.1"
+  dependencies:
+    d3-dsv: ^3.0.1
+    node-fetch: ^2.6.7
+    topojson-client: ^3.1.0
+    vega-format: ^1.1.1
+    vega-util: ^1.17.1
+  checksum: 95f6eebc75a97665cf34faaea431934047e1b2e9d7532f48f62dab4884d606a7d9da53962e1631a5790a7a867f720581852a3db9be1a7f667882062f6c102ee0
+  languageName: node
+  linkType: hard
+
+"vega-parser@npm:~6.2.0":
+  version: 6.2.0
+  resolution: "vega-parser@npm:6.2.0"
+  dependencies:
+    vega-dataflow: ^5.7.5
+    vega-event-selector: ^3.0.1
+    vega-functions: ^5.13.1
+    vega-scale: ^7.3.0
+    vega-util: ^1.17.1
+  checksum: 19872153c16aab30c4df338e0df7bd331e0bf74c7c6afce5428df555b9bdb0c4acf76b54092cacd4726a1349912ea803c90e1b30d53f4a02044e0559873969a7
+  languageName: node
+  linkType: hard
+
+"vega-projection@npm:^1.6.0, vega-projection@npm:~1.6.0":
+  version: 1.6.0
+  resolution: "vega-projection@npm:1.6.0"
+  dependencies:
+    d3-geo: ^3.1.0
+    d3-geo-projection: ^4.0.0
+    vega-scale: ^7.3.0
+  checksum: 9c52848e294ff68051fe9f44fa536656c4e6be3d474bd3359e21aa154ab282755eaee624ac31b1ca01816227900e1d81a6d191e36f46e47525ed6648397f0fa0
+  languageName: node
+  linkType: hard
+
+"vega-regression@npm:~1.2.0":
+  version: 1.2.0
+  resolution: "vega-regression@npm:1.2.0"
+  dependencies:
+    d3-array: ^3.2.2
+    vega-dataflow: ^5.7.3
+    vega-statistics: ^1.9.0
+    vega-util: ^1.15.2
+  checksum: 5f79db18c7849b465550e00ca8fec9d896aa3cf6d6279daac8b862beb632d841dcb6a93136d6b827c37e3d1cbd2bb2f7dec58f96c572763870c2d38f2cc4e0b3
+  languageName: node
+  linkType: hard
+
+"vega-runtime@npm:^6.1.4, vega-runtime@npm:~6.1.4":
+  version: 6.1.4
+  resolution: "vega-runtime@npm:6.1.4"
+  dependencies:
+    vega-dataflow: ^5.7.5
+    vega-util: ^1.17.1
+  checksum: a1da40ddb3109f1ced8e61d2e7b52784fbb29936ee4c47cb5630dbbeb12ef6e0c3cd3cd189c34377f82402bf19c61dd148d90330fec743b8667635ac48e4ba29
+  languageName: node
+  linkType: hard
+
+"vega-scale@npm:^7.3.0, vega-scale@npm:~7.3.0":
+  version: 7.3.0
+  resolution: "vega-scale@npm:7.3.0"
+  dependencies:
+    d3-array: ^3.2.2
+    d3-interpolate: ^3.0.1
+    d3-scale: ^4.0.2
+    vega-time: ^2.1.1
+    vega-util: ^1.17.1
+  checksum: 8e434f27a51a913dd18374ec0d2bc33758eda7db1ee6342721644f977e705268b8df6b3e89813774d776d03a0cd24f91d4d59f9e80951f67dfbbf8637f5a69ad
+  languageName: node
+  linkType: hard
+
+"vega-scenegraph@npm:^4.10.2, vega-scenegraph@npm:^4.9.2, vega-scenegraph@npm:~4.10.2":
+  version: 4.10.2
+  resolution: "vega-scenegraph@npm:4.10.2"
+  dependencies:
+    d3-path: ^3.1.0
+    d3-shape: ^3.2.0
+    vega-canvas: ^1.2.7
+    vega-loader: ^4.5.1
+    vega-scale: ^7.3.0
+    vega-util: ^1.17.1
+  checksum: 6caf3e298297b918c8b6a72f019e51e2bfbaecd316e4d1c37d855ac9366d177cdbf16e9c8857c5ccde128bcd9645af7ee7dc81111bcd743d192e1a3b9a9d7185
+  languageName: node
+  linkType: hard
+
+"vega-selections@npm:^5.4.1":
+  version: 5.4.1
+  resolution: "vega-selections@npm:5.4.1"
+  dependencies:
+    d3-array: 3.2.2
+    vega-expression: ^5.0.1
+    vega-util: ^1.17.1
+  checksum: c594d41ec3886af94976e4dc4e152bea9b3975a22d435aa38dac2aab105851cb83fd4aa0f1e81a47f8bc0bea1677af93816331e3ed084ab3ec2e51b3544c109f
+  languageName: node
+  linkType: hard
+
+"vega-statistics@npm:^1.7.9, vega-statistics@npm:^1.8.1, vega-statistics@npm:^1.9.0, vega-statistics@npm:~1.9.0":
+  version: 1.9.0
+  resolution: "vega-statistics@npm:1.9.0"
+  dependencies:
+    d3-array: ^3.2.2
+  checksum: bbf2ea088c5a6a662c6aed1bf57996c06a82a98228730ada8a97e57824a6ed391999ea974f16dcde6e73bf88799976d91aff748842848d38ab45dbb9fafba3f9
+  languageName: node
+  linkType: hard
+
+"vega-time@npm:^2.1.1, vega-time@npm:~2.1.1":
+  version: 2.1.1
+  resolution: "vega-time@npm:2.1.1"
+  dependencies:
+    d3-array: ^3.2.2
+    d3-time: ^3.1.0
+    vega-util: ^1.17.1
+  checksum: 3d6a50f779be4b5e7f27bd2aae766035c29e59e03e62d2e96b94a2f759ed3104c1102c1006dd416e7b819ee501880ae7a722c2fa9aabf9efac86503c1aada14a
+  languageName: node
+  linkType: hard
+
+"vega-transforms@npm:~4.10.2":
+  version: 4.10.2
+  resolution: "vega-transforms@npm:4.10.2"
+  dependencies:
+    d3-array: ^3.2.2
+    vega-dataflow: ^5.7.5
+    vega-statistics: ^1.8.1
+    vega-time: ^2.1.1
+    vega-util: ^1.17.1
+  checksum: 2dbe4c767542a5dc4dbb453fd1317b00912e47dbdb3de637259b2552497dd8039c20c795318ad57341eb0d30b69712c55a2da16dc9ad2329a68c35fb75b4fee6
+  languageName: node
+  linkType: hard
+
+"vega-typings@npm:~0.24.0":
+  version: 0.24.2
+  resolution: "vega-typings@npm:0.24.2"
+  dependencies:
+    "@types/geojson": 7946.0.4
+    vega-event-selector: ^3.0.1
+    vega-expression: ^5.0.1
+    vega-util: ^1.17.1
+  checksum: 9c06430b2c8a5e6a8b29448333aa95b0946aa69c181933f52eb69f0e3594a0f308be7760f0febe13253a0b7414721841fce790b2b3812a7fb3b0a3f0391e6ace
+  languageName: node
+  linkType: hard
+
+"vega-util@npm:^1.15.2, vega-util@npm:^1.17.1, vega-util@npm:~1.17.2":
+  version: 1.17.2
+  resolution: "vega-util@npm:1.17.2"
+  checksum: 5d681cb1a6ffda7af1b74df7c1c46a32f1d874daef54f9c9c65c7d7c7bfc4271dc6d9b1c1c7a853b14eb6e4cc8ec811b0132cd3ea25fa85259eac92e1b4f07fa
+  languageName: node
+  linkType: hard
+
+"vega-view-transforms@npm:~4.5.9":
+  version: 4.5.9
+  resolution: "vega-view-transforms@npm:4.5.9"
+  dependencies:
+    vega-dataflow: ^5.7.5
+    vega-scenegraph: ^4.10.2
+    vega-util: ^1.17.1
+  checksum: aeeaf3c2f1a02b1303c16a586dbcb20f208c101d06d7e988e18ab71fb67d87be5d8ff228ebf25971535d6e41dc816168cfa68b8676e7250df07a40aefdea32a7
+  languageName: node
+  linkType: hard
+
+"vega-view@npm:~5.11.1":
+  version: 5.11.1
+  resolution: "vega-view@npm:5.11.1"
+  dependencies:
+    d3-array: ^3.2.2
+    d3-timer: ^3.0.1
+    vega-dataflow: ^5.7.5
+    vega-format: ^1.1.1
+    vega-functions: ^5.13.1
+    vega-runtime: ^6.1.4
+    vega-scenegraph: ^4.10.2
+    vega-util: ^1.17.1
+  checksum: 82ddc74593b3a359d0b3458bc06573673ff9bf13f84020cb36fb4676c5d7f547e9650eb6faaa76799fbcedd27bcd266603dbd08c420e2d2229cc6b9f48a4a66d
+  languageName: node
+  linkType: hard
+
+"vega-voronoi@npm:~4.2.1":
+  version: 4.2.1
+  resolution: "vega-voronoi@npm:4.2.1"
+  dependencies:
+    d3-delaunay: ^6.0.2
+    vega-dataflow: ^5.7.5
+    vega-util: ^1.17.1
+  checksum: f618174ad5f451c507a80e373288bb2c0da7a8a908d62f885bc77b354c4334504ae2d1042742f68ad419ade7b548aeca9ca1042ae5541bebd7f5297afc23bb35
+  languageName: node
+  linkType: hard
+
+"vega-wordcloud@npm:~4.1.4":
+  version: 4.1.4
+  resolution: "vega-wordcloud@npm:4.1.4"
+  dependencies:
+    vega-canvas: ^1.2.7
+    vega-dataflow: ^5.7.5
+    vega-scale: ^7.3.0
+    vega-statistics: ^1.8.1
+    vega-util: ^1.17.1
+  checksum: 34d1882651d3a2f34ce40a6eaeed700de126f627cdf041ec2bcc7ada46d7b4b68a38a2974236eec87ee876d9abd095af7ab17e7698b0e2fbc831460767969d7a
+  languageName: node
+  linkType: hard
+
+"vega@npm:^5.20.0":
+  version: 5.25.0
+  resolution: "vega@npm:5.25.0"
+  dependencies:
+    vega-crossfilter: ~4.1.1
+    vega-dataflow: ~5.7.5
+    vega-encode: ~4.9.2
+    vega-event-selector: ~3.0.1
+    vega-expression: ~5.1.0
+    vega-force: ~4.2.0
+    vega-format: ~1.1.1
+    vega-functions: ~5.13.2
+    vega-geo: ~4.4.1
+    vega-hierarchy: ~4.1.1
+    vega-label: ~1.2.1
+    vega-loader: ~4.5.1
+    vega-parser: ~6.2.0
+    vega-projection: ~1.6.0
+    vega-regression: ~1.2.0
+    vega-runtime: ~6.1.4
+    vega-scale: ~7.3.0
+    vega-scenegraph: ~4.10.2
+    vega-statistics: ~1.9.0
+    vega-time: ~2.1.1
+    vega-transforms: ~4.10.2
+    vega-typings: ~0.24.0
+    vega-util: ~1.17.2
+    vega-view: ~5.11.1
+    vega-view-transforms: ~4.5.9
+    vega-voronoi: ~4.2.1
+    vega-wordcloud: ~4.1.4
+  checksum: ddc7b1f2a70c72b842e111d32bdd8ff050992a50e385e8ddc6e35c02e7c481a652383c81c547b7ebfd31cda04ab9f9acf0a8cc47c6bd19b91765b254aac30d24
+  languageName: node
+  linkType: hard
+
 "vscode-jsonrpc@npm:8.2.0, vscode-jsonrpc@npm:^8.0.2":
   version: 8.2.0
   resolution: "vscode-jsonrpc@npm:8.2.0"
@@ -10217,16 +11703,16 @@ __metadata:
   languageName: node
   linkType: hard
 
-"which-typed-array@npm:^1.1.11":
-  version: 1.1.11
-  resolution: "which-typed-array@npm:1.1.11"
+"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.13":
+  version: 1.1.13
+  resolution: "which-typed-array@npm:1.1.13"
   dependencies:
     available-typed-arrays: ^1.0.5
-    call-bind: ^1.0.2
+    call-bind: ^1.0.4
     for-each: ^0.3.3
     gopd: ^1.0.1
     has-tostringtag: ^1.0.0
-  checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206
+  checksum: 3828a0d5d72c800e369d447e54c7620742a4cc0c9baf1b5e8c17e9b6ff90d8d861a3a6dd4800f1953dbf80e5e5cec954a289e5b4a223e3bee4aeb1f8c5f33309
   languageName: node
   linkType: hard
 
@@ -10263,6 +11749,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"which@npm:^4.0.0":
+  version: 4.0.0
+  resolution: "which@npm:4.0.0"
+  dependencies:
+    isexe: ^3.1.1
+  bin:
+    node-which: bin/which.js
+  checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651
+  languageName: node
+  linkType: hard
+
 "wide-align@npm:^1.1.5":
   version: 1.1.5
   resolution: "wide-align@npm:1.1.5"
@@ -10494,7 +11991,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"yargs@npm:^17.6.2":
+"yargs@npm:^17.6.2, yargs@npm:~17.7.2":
   version: 17.7.2
   resolution: "yargs@npm:17.7.2"
   dependencies:


Test patch:
diff --git a/.github/workflows/step_tests-ui.yml b/.github/workflows/step_tests-ui.yml
new file mode 100644
index 000000000..5cbb050c1
--- /dev/null
+++ b/.github/workflows/step_tests-ui.yml
@@ -0,0 +1,46 @@
+name: test-ui
+run-name: Run UI tests with Galata
+
+on:
+  workflow_call:
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}-ui-test
+  cancel-in-progress: true
+
+jobs:
+  test-ui:
+    continue-on-error: false
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+
+      - name: Base Setup
+        uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
+
+      - name: Install from source (required for the pre-commit tests)
+        run: python -m pip install -e '.[test-cov]'
+
+      - name: Install galata
+        working-directory: jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests
+        env:
+          PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
+        run: jlpm install
+
+      - name: Install browser
+        working-directory: jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests
+        run: jlpm playwright install chromium
+
+      - name: Integration tests
+        working-directory: jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests
+        run: jlpm playwright test
+
+      - name: Upload UI Test artifacts
+        if: failure()
+        uses: actions/upload-artifact@v3
+        with:
+          name: ui-test-output
+          path: |
+            jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/test-results
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/README.md b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/README.md
new file mode 100644
index 000000000..a17c158d9
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/README.md
@@ -0,0 +1,167 @@
+# Integration Testing
+
+This folder contains the integration tests of the extension.
+
+They are defined using [Playwright](https://playwright.dev/docs/intro) test runner
+and [Galata](https://github.com/jupyterlab/jupyterlab/tree/main/galata) helper.
+
+The Playwright configuration is defined in [playwright.config.js](./playwright.config.js).
+
+The JupyterLab server configuration to use for the integration test is defined
+in [jupyter_server_test_config.py](./jupyter_server_test_config.py).
+
+The default configuration will produce video for failing tests and an HTML report.
+
+> There is a new experimental UI mode that you may fall in love with; see [that video](https://www.youtube.com/watch?v=jF0yA-JLQW0).
+
+## Run the tests
+
+> All commands are assumed to be executed from the _main-menu_ directory
+
+To run the tests, you need to:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Execute the [Playwright](https://playwright.dev/docs/intro) tests:
+
+```sh
+cd ./ui-tests
+jlpm playwright test
+```
+
+Test results will be shown in the terminal. In case of any test failures, the test report
+will be opened in your browser at the end of the tests execution; see
+[Playwright documentation](https://playwright.dev/docs/test-reporters#html-reporter)
+for configuring that behavior.
+
+## Update the tests snapshots
+
+> All commands are assumed to be executed from the _main-menu_ directory
+
+If you are comparing snapshots to validate your tests, you may need to update
+the reference snapshots stored in the repository. To do that, you need to:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Execute the [Playwright](https://playwright.dev/docs/intro) command:
+
+```sh
+cd ./ui-tests
+jlpm playwright test -u
+```
+
+> Some discrepancy may occurs between the snapshots generated on your computer and
+> the one generated on the CI. To ease updating the snapshots on a PR, you can
+> type `please update playwright snapshots` to trigger the update by a bot on the CI.
+> Once the bot has computed new snapshots, it will commit them to the PR branch.
+
+## Create tests
+
+> All commands are assumed to be executed from the _main-menu_ directory
+
+To create tests, the easiest way is to use the code generator tool of playwright:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Start the server:
+
+```sh
+cd ./ui-tests
+jlpm start
+```
+
+4. Execute the [Playwright code generator](https://playwright.dev/docs/codegen) in **another terminal**:
+
+```sh
+cd ./ui-tests
+jlpm playwright codegen localhost:8888
+```
+
+## Debug tests
+
+> All commands are assumed to be executed from the _main-menu_ directory
+
+To debug tests, a good way is to use the inspector tool of playwright:
+
+1. Compile the extension:
+
+```sh
+jlpm install
+jlpm build:prod
+```
+
+> Check the extension is installed in JupyterLab.
+
+2. Install test dependencies (needed only once):
+
+```sh
+cd ./ui-tests
+jlpm install
+jlpm playwright install
+cd ..
+```
+
+3. Execute the Playwright tests in [debug mode](https://playwright.dev/docs/debug):
+
+```sh
+cd ./ui-tests
+jlpm playwright test --debug
+```
+
+## Upgrade Playwright and the browsers
+
+To update the web browser versions, you must update the package `@playwright/test`:
+
+```sh
+cd ./ui-tests
+jlpm up "@playwright/test"
+jlpm playwright install
+```
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/jupyter_server_test_config.py b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/jupyter_server_test_config.py
new file mode 100644
index 000000000..301b94c23
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/jupyter_server_test_config.py
@@ -0,0 +1,12 @@
+"""Server configuration for integration tests.
+
+!! Never use this configuration in production because it
+opens the server to the world and provide access to JupyterLab
+JavaScript objects through the global window variable.
+"""
+from jupyterlab.galata import configure_jupyter_server
+
+configure_jupyter_server(c)  # noqa: F821
+
+# Uncomment to set server log level to debug level
+# c.ServerApp.log_level = "DEBUG"
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/package.json b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/package.json
new file mode 100644
index 000000000..3cb137857
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/package.json
@@ -0,0 +1,15 @@
+{
+    "name": "jupyterlab-jupytext-ui-tests",
+    "version": "1.4.0",
+    "description": "Basic Integration Tests",
+    "private": true,
+    "scripts": {
+        "start": "jupyter lab --config jupyter_server_test_config.py",
+        "test": "jlpm playwright test",
+        "test:update": "jlpm playwright test --update-snapshots"
+    },
+    "devDependencies": {
+        "@jupyterlab/galata": "^5.0.0",
+        "@playwright/test": "^1.32.0"
+    }
+}
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/playwright.config.js b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/playwright.config.js
new file mode 100644
index 000000000..52f61336e
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/playwright.config.js
@@ -0,0 +1,38 @@
+/**
+ * Configuration for Playwright using default from @jupyterlab/galata
+ */
+const baseConfig = require('@jupyterlab/galata/lib/playwright-config');
+
+module.exports = {
+  ...baseConfig,
+  projects: [
+    {
+      name: 'jupytext',
+      testMatch: 'tests/*.ts',
+      testIgnore: '**/.ipynb_checkpoints/**',
+      timeout: 60000,
+      // use: {
+      //   launchOptions: {
+      //     // Force slow motion
+      //     slowMo: 1000,
+      //   },
+      // },
+    },
+  ],
+  // Visual comparison of screenshots can be flaky. Use a tolerance
+  expect: {
+    toMatchSnapshot: {
+      maxDiffPixelRatio: 0.02,
+    },
+  },
+  webServer: {
+    command: 'jlpm start',
+    url: 'http://localhost:8888/lab',
+    timeout: 120 * 1000,
+    reuseExistingServer: !process.env.CI,
+  },
+  // Switch to 'always' to keep raw assets for all tests
+  preserveOutput: 'failures-only', // Breaks HTML report if use.video == 'on'
+  // Try one retry as some tests are flaky
+  // retries: process.env.CI ? 1 : 0,
+};
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts
new file mode 100644
index 000000000..2ec21b2d5
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts
@@ -0,0 +1,27 @@
+import { expect, test } from '@jupyterlab/galata';
+
+// Main Jupytext menu
+const jupytextMenu = [
+  'Jupytext',
+  'Jupytext>New Text Notebook',
+  'Jupytext>Pair Notebook',
+];
+
+test.describe('Jupytext Menu Tests', () => {
+  test.use({ autoGoto: false });
+
+  jupytextMenu.forEach((menuPath) => {
+    test(`Open menu item ${menuPath}`, async ({ page }) => {
+      await page.goto();
+      await page.menu.open(menuPath);
+      expect(await page.menu.isOpen(menuPath)).toBeTruthy();
+
+      const imageName = `opened-jupytext-menu-${menuPath.replace(
+        />/g,
+        '-'
+      )}.png`;
+      const menu = await page.menu.getOpenMenu();
+      expect(await menu!.screenshot()).toMatchSnapshot(imageName.toLowerCase());
+    });
+  });
+});
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-jupytext-linux.png
new file mode 100644
index 000000000..b3752b77e
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-new-text-notebook-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-new-text-notebook-jupytext-linux.png
new file mode 100644
index 000000000..9607460ca
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-new-text-notebook-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-pair-notebook-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-pair-notebook-jupytext-linux.png
new file mode 100644
index 000000000..b46dc3d37
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-pair-notebook-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts
new file mode 100644
index 000000000..5bf16f212
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts
@@ -0,0 +1,101 @@
+import { expect, test } from '@jupyterlab/galata';
+
+// Define all formats that we would like to test
+// We dont test Rmd and qmd formats here. Rmd cannot be
+// opened by JupyterLab in the markdown format.
+// Quarto must be available for qmd to work
+const formats = [
+  { label: 'Light Script', extension: '.py' },
+  { label: 'Percent Script', extension: '.py' },
+  { label: 'Hydrogen Script', extension: '.py' },
+  { label: 'Nomarker Script', extension: '.py' },
+  { label: 'Markdown', extension: '.md' },
+  { label: 'MyST Markdown', extension: '.md' },
+];
+
+// Get all possible menuPaths
+const createNewMenuPaths = formats.map((format) => {
+  return {
+    menuPath: `Jupytext>New Text Notebook>${format.label}`,
+    extension: format.extension,
+  };
+});
+
+const pairMenuPaths = formats.map((format) => {
+  return {
+    menuPath: `Jupytext>Pair Notebook>Pair Notebook with ${format.label}`,
+    extension: format.extension,
+  };
+});
+
+// Name of notebook file
+const fileName = 'notebook.ipynb';
+
+/**
+ * Helper function to populate notebook cells and run them
+ */
+async function populateNotebook(page) {
+  await page.notebook.setCell(0, 'raw', 'Just a raw cell');
+  await page.notebook.addCell(
+    'markdown',
+    '## This is **bold** and *italic* [link to jupyter.org!](http://jupyter.org)'
+  );
+  await page.notebook.runCell(1, true);
+  await page.notebook.addCell('code', '2 ** 3');
+  await page.notebook.runCell(2, true);
+}
+
+test.describe('Jupytext Create Text Notebooks from Menu Tests', () => {
+  createNewMenuPaths.forEach((paths) => {
+    test(`Open menu item ${paths.menuPath}`, async ({ page }) => {
+      // await page.goto();
+      // Create new text notebook by clicking menupath item
+      await page.menu.clickMenuItem(paths.menuPath);
+
+      // Wait for the kernel dialog and accept it
+      await page.waitForSelector('.jp-Dialog');
+      await page.click('.jp-Dialog .jp-mod-accept');
+
+      // Populate page
+      await populateNotebook(page);
+      // Save notebook
+      await page.notebook.save();
+
+      //   // Compare screenshots
+      //   let imageName = `opened-${paths.menuPath.replace(/>/g, '-')}.png`;
+      //   expect(await page.screenshot()).toMatchSnapshot(imageName.toLowerCase());
+
+      // Try to open saved text notebook with Editor factory
+      await page.filebrowser.open(`Untitled${paths.extension}`, 'Editor');
+
+      // Compare text notebook contents
+      const imageName = `opened-${paths.menuPath.replace(/>/g, '-')}-text.png`;
+      expect(await page.screenshot()).toMatchSnapshot(imageName.toLowerCase());
+    });
+  });
+});
+
+test.describe('Jupytext Pair Notebooks from Menu Tests', () => {
+  // Before each test start a new notebook and add some cell data
+  test.beforeEach(async ({ page }) => {
+    await page.notebook.createNew(fileName);
+    await populateNotebook(page);
+  });
+
+  pairMenuPaths.forEach((paths) => {
+    test(`Open menu item ${paths.menuPath}`, async ({ page }) => {
+      // Click pairing command
+      await page.menu.clickMenuItem(paths.menuPath);
+      // Wait until we save notebook. Once we save it, paired file appears
+      await page.notebook.save();
+      // Try to open paired file
+      await page.filebrowser.open(fileName.replace('.ipynb', paths.extension));
+
+      const imageName = `paired-jupytext-${paths.menuPath.replace(
+        />/g,
+        '-'
+      )}.png`;
+      expect(await page.screenshot()).toMatchSnapshot(imageName.toLowerCase());
+    });
+  });
+});
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-hydrogen-script-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-hydrogen-script-text-jupytext-linux.png
new file mode 100644
index 000000000..02ea8dee7
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-hydrogen-script-text-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-light-script-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-light-script-text-jupytext-linux.png
new file mode 100644
index 000000000..264b3d4ee
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-light-script-text-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-markdown-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-markdown-text-jupytext-linux.png
new file mode 100644
index 000000000..2c1ad656e
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-markdown-text-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-myst-markdown-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-myst-markdown-text-jupytext-linux.png
new file mode 100644
index 000000000..c4ee4723c
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-myst-markdown-text-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-nomarker-script-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-nomarker-script-text-jupytext-linux.png
new file mode 100644
index 000000000..3b4e95c00
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-nomarker-script-text-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-percent-script-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-percent-script-text-jupytext-linux.png
new file mode 100644
index 000000000..000bf25dd
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-percent-script-text-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-hydrogen-script-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-hydrogen-script-jupytext-linux.png
new file mode 100644
index 000000000..4c6b5eacb
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-hydrogen-script-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-light-script-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-light-script-jupytext-linux.png
new file mode 100644
index 000000000..a99d41b88
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-light-script-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-markdown-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-markdown-jupytext-linux.png
new file mode 100644
index 000000000..1de955e18
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-markdown-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-myst-markdown-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-myst-markdown-jupytext-linux.png
new file mode 100644
index 000000000..3156a8b45
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-myst-markdown-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-nomarker-script-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-nomarker-script-jupytext-linux.png
new file mode 100644
index 000000000..9a5d75adc
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-nomarker-script-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-percent-script-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-percent-script-jupytext-linux.png
new file mode 100644
index 000000000..6e0c61c4c
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/paired-jupytext-jupytext-pair-notebook-pair-notebook-with-percent-script-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts
new file mode 100644
index 000000000..21d51230b
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts
@@ -0,0 +1,27 @@
+import { expect, test } from '@jupyterlab/galata';
+
+test('Open the settings editor with a Jupytext query', async ({ page }) => {
+  await page.evaluate(async () => {
+    await window.jupyterapp.commands.execute('settingeditor:open', {
+      query: 'Jupytext',
+    });
+  });
+
+  expect(
+    await page.locator('.jp-PluginList .jp-FilterBox input').inputValue()
+  ).toEqual('Jupytext');
+
+  await expect(page.locator('.jp-SettingsForm')).toHaveCount(1);
+
+  const pluginList = page.locator('.jp-PluginList');
+
+  expect(await pluginList.screenshot()).toMatchSnapshot(
+    'jupytext-settings-plugin-list.png'
+  );
+
+  const settingsPanel = page.locator('.jp-SettingsPanel');
+
+  expect(await settingsPanel.screenshot()).toMatchSnapshot(
+    'jupytext-settings-panel.png'
+  );
+});
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-panel-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-panel-jupytext-linux.png
new file mode 100644
index 000000000..6c275d46c
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-panel-jupytext-linux.png differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-plugin-list-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-plugin-list-jupytext-linux.png
new file mode 100644
index 000000000..f04e45c9d
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-plugin-list-jupytext-linux.png differ
diff --git a/tests/test_contentsmanager.py b/tests/test_contentsmanager.py
index 1a9ec42e4..19556a671 100644
--- a/tests/test_contentsmanager.py
+++ b/tests/test_contentsmanager.py
@@ -1857,12 +1857,22 @@ def test_new_untitled(tmpdir):
     assert untitled
     assert ext == "ipynb"
 
+    # Jupytext related files
     assert cm.new_untitled(type="notebook", ext=".md")["path"] == untitled + "1.md"
     assert cm.new_untitled(type="notebook", ext=".py")["path"] == untitled + "2.py"
-    assert cm.new_untitled(type="notebook")["path"] == untitled + "3.ipynb"
+    assert cm.new_untitled(type="notebook", ext=".md:myst")["path"] == untitled + "3.md"
     assert (
         cm.new_untitled(type="notebook", ext=".py:percent")["path"] == untitled + "4.py"
     )
+    assert cm.new_untitled(type="notebook", ext=".Rmd")["path"] == untitled + "5.Rmd"
+
+    # Test native formats that should not be changed by Jupytext and model should
+    # not contain any Jupytext metadata neither file name should start with Uppercase
+    for ext in [".py", ".md"]:
+        model = cm.new_untitled(type="file", ext=ext)
+        assert model["content"] is None
+        assert model["path"] == f"untitled{ext}"
+    assert cm.new_untitled(type="directory")["path"] == "Untitled Folder"
 
 
 def test_nested_prefix(tmpdir):