Repository: mwouts/jupytext (language: python)

Issue:
Create language specific Text notebooks
#1154 will add ability to create text notebooks from JupyterLab interface. However, we are making a strong assumption that these text notebooks are based on Python kernel. So, when user creates a Text notebook with Percent format, it will always create a file with `.py` extension.

We should be able to create text notebook for any arbitrary kernel (see [comment](https://github.com/mwouts/jupytext/pull/1154#issuecomment-1804738058)) and so we should be able to create file with arbitrary file extensions.  However, it is a bit tricky now as we choose kernel _only after_ creating a file. 

- One solution would be to have different launcher icons for different kernels and then it will be user's responsibility to choose correct kernel. This would duplicate the launcher icons though in the launcher. 
- Another solution would be to have a input modal dialog when user clicks a text notebook launcher icon and then user chooses text notebook target kernel and based on this input we create a file with correct extension.


Discussion / hints:
Hi @mahendrapaipuri , seeing this screenshot in the docs:
![image](https://github.com/mwouts/jupytext/assets/29915202/830bca4d-50fb-4d28-9703-7efc5aa889c1)
made me think that maybe we could simply have a `R:percent` format for the people that wish to create R-percent notebooks?

Two more remarks/questions
- Maybe we could iterate through the available kernels to determine which languages should be made available by default
- Is it possible to use an icon that would correspond to the file extension? E.g. `.py`, `.R` or `.md`?
> Maybe we could iterate through the available kernels to determine which languages should be made available by default

If I understand you correctly, your suggestion is that we add formats _programatically_ by iterating through available kernels. That solution is orthogonal to configuring formats _via_ Settings, right?! I mean, the formats will be added automatically based on available kernels and those formats will not appear in the Settings. And users will not have a way to _disable_ those automatically added formats. Does it make sense?

An option here is to add another Settings parameter like "Detect formats automatically" and we add the format icons based on available kernels if users choose this setting. This option should take precedence over configuring formats manually. And not using this setting, users will be able to add formats manually as in the screenshot in the above comment. What do you think?

> Is it possible to use an icon that would correspond to the file extension? 

Yes, should be possible.

> made me think that maybe we could simply have a R:percent format for the people that wish to create R-percent notebooks?

Yes, this is a simpler solution. Users will have to do this only once given that their `JUPYTER_CONFIG_DIR` is persisted.

When are you aiming to make `1.16.0` release? It would be nice to sort this out before the release!
Hey there 
It's my understanding too that json settings and programmatically building UI contents are mutually exclusive, at least I ran into this issue when tweaking MainMenu in another context 
Personally I would argue to keep it simple for a first iteration of this feature, but this is because I am way less comfortable with extensions than @mahendrapaipuri :) 
In any case I cannot thank you enough:), and am very sorry to not being able to help more as I am currently afk for a couple of weeks..
Hi @mahendrapaipuri , @parmentelat , thank you for your inputs!

Yes, I see what you mean! Then my preference, if that makes sense/if that's feasible, would be to keep the setting pages as is, but then programmatically replace `auto:` with every extension for which a kernel exists in both `auto:percent` and `auto:light`. I am a big fan of default settings that are useful to everyone (i.e. settings that you don't need to know of!)

Re the icon, that would be great! Also maybe we might need to include the language or at least the extension in the entry if we have e.g. both 'Python percent notebook' and 'R percent notebook' in the launcher.

> When are you aiming to make 1.16.0 release? It would be nice to sort this out before the release!

Oh I think the test reorganization (#1136) might be ready by the end of this week, we can release any time after that, so I'd say one or two weeks. I do agree with you that it would be very nice to include the current issue in the release, and I'd be happy to give you more time if you'd like to propose something!



> Yes, I see what you mean! Then my preference, if that makes sense/if that's feasible, would be to keep the setting pages as is, but then programmatically replace auto: with every extension for which a kernel exists in both auto:percent and auto:light

I can see two options here

- Users will have to use extension (like `py:light`, `R:light`) in the format in Settings rather than `auto:light`. This is more clear as users know which formats they have exactly.  It will be like in the screenshot but instead of `auto:{light,percent}`, it will be more explicit like `py:{light,percent}`.  If user still uses `auto:`, we will replace it by `py:` by default because we know Python kernel is always there.
-  If we want to keep using `auto:light` and programatically create different extension files, we should use a input modal dialog. When user clicks the launcher icon, an input dialog appears (similar to the one that asks to choose kernel) and users choose which extension (`py`, `R`,...) file they would like create and then we programatically replace `auto` with that extension and create a file. This way one launcher icon can create several file formats. But there will be two input modals, one for choosing file extension and one for choosing kernel, which sort of disturbs the UX.

Personally, I feel the first option would be more clear for users and we can document in the Settings page that `auto` will be replaced by `py` by default. Let me know what you both think.

> Re the icon, that would be great! Also maybe we might need to include the language or at least the extension in the entry if we have e.g. both 'Python percent notebook' and 'R percent notebook' in the launcher.

Agree and this should not be an issue.

> I'd be happy to give you more time if you'd like to propose something!

I will give it a go this weekend. It **should not** be very complicated (unless I am missing something). Let's see and try to fix it before the release.

Fix patch:
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/package.json b/jupyterlab/packages/jupyterlab-jupytext-extension/package.json
index 92bea16b3..ae0725974 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/package.json
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/package.json
@@ -77,8 +77,10 @@
     "@jupyterlab/settingregistry": "^4.0.0",
     "@jupyterlab/translation": "^4.0.0",
     "@jupyterlab/ui-components": "^4.0.0",
+    "@lumino/commands": "^2.0.0",
     "@lumino/coreutils": "^2.0.0",
     "@lumino/disposable": "^2.0.0",
+    "buffer": "^6.0.3",
     "jupyterlab-rise": "^0.41.0"
   },
   "devDependencies": {
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/schema/plugin.json b/jupyterlab/packages/jupyterlab-jupytext-extension/schema/plugin.json
index 2dcfc2b60..90bcf8d5c 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/schema/plugin.json
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/schema/plugin.json
@@ -1,182 +1,54 @@
 {
   "title": "Jupytext",
-  "description": "Jupytext Menu Settings.",
+  "description": "List of Jupytext Text Notebook formats that will be added to launcher.\n\nWhen any of percent, light, hydrogen and nomarker formats is selected, all the available kernel variants will be added to launcher. For instance, if Python and R kernels are installed, percent format will add launcher items to create Python and R Text notebooks with percent format.\n\nRefresh the current browser tab for the changes to take effect.",
   "properties": {
+    "auto:percent": {
+      "type": "boolean",
+      "title": "Text Notebook as Script in Percent Format",
+      "default": true
+    },
+    "auto:light": {
+      "type": "boolean",
+      "title": "Text Notebook as Script in Light Format",
+      "default": false
+    },
+    "auto:hydrogen": {
+      "type": "boolean",
+      "title": "Text Notebook as Script in Hydrogen Format",
+      "default": false
+    },
+    "auto:nomarker": {
+      "type": "boolean",
+      "title": "Text Notebook as Script in Nomarker Format",
+      "default": false
+    },
+    "md:myst": {
+      "type": "boolean",
+      "title": "MyST Markdown Notebook",
+      "default": true
+    },
+    "md": {
+      "type": "boolean",
+      "title": "Markdown Notebook",
+      "default": false
+    },
+    "Rmd": {
+      "type": "boolean",
+      "title": "R Markdown Notebook",
+      "default": false
+    },
+    "qmd": {
+      "type": "boolean",
+      "title": "Quarto Markdown Notebook",
+      "default": false
+    },
     "category": {
       "title": "Category",
-      "description": "Category under which Jupytext Text Notebooks will be placed. Refresh the current browser tab for the changes to take effect.",
+      "description": "Category under which Jupytext Text Notebooks will be placed.",
       "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/jupytext.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/commands.ts
similarity index 95%
rename from jupyterlab/packages/jupyterlab-jupytext-extension/src/jupytext.ts
rename to jupyterlab/packages/jupyterlab-jupytext-extension/src/commands.ts
index faab6b3d2..7e49778e4 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/src/jupytext.ts
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/commands.ts
@@ -8,23 +8,10 @@ import { TranslationBundle } from '@jupyterlab/translation';
 
 import {
   LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS,
-  ALL_JUPYTEXT_FORMATS,
-  ALL_JUPYTEXT_FORMAT_EXTENSIONS,
-  IJupytextFormat,
+  JUPYTEXT_FORMATS,
   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
  */
@@ -137,8 +124,8 @@ export function isPairCommandToggled(
   const selectedFormats = getSelectedFormats(notebookTracker);
 
   if (format === 'custom') {
-    for (const selecFormat of selectedFormats) {
-      if (!ALL_JUPYTEXT_FORMAT_EXTENSIONS.includes(selecFormat)) {
+    for (const selectedFormat of selectedFormats) {
+      if (!JUPYTEXT_FORMATS.includes(selectedFormat)) {
         return true;
       }
     }
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/factory.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/factory.ts
new file mode 100644
index 000000000..fa50d38bf
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/factory.ts
@@ -0,0 +1,101 @@
+import {
+  IToolbarWidgetRegistry,
+  createToolbarFactory,
+} from '@jupyterlab/apputils';
+
+import {
+  INotebookTracker,
+  NotebookPanel,
+  NotebookWidgetFactory,
+} from '@jupyterlab/notebook';
+
+import { ISettingRegistry } from '@jupyterlab/settingregistry';
+
+import { DocumentRegistry } from '@jupyterlab/docregistry';
+
+import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
+
+import { IEditorServices } from '@jupyterlab/codeeditor';
+
+import { ITranslator, TranslationBundle } from '@jupyterlab/translation';
+
+import { IRisePreviewFactory } from 'jupyterlab-rise';
+
+import { FACTORY, FILE_TYPES } from './tokens';
+
+export function createFactory(
+  kernelFileTypeNames: string[],
+  toolbarRegistry: IToolbarWidgetRegistry,
+  settingRegistry: ISettingRegistry,
+  docRegistry: DocumentRegistry,
+  notebookTracker: INotebookTracker,
+  notebookFactory: NotebookWidgetFactory.IFactory,
+  contentFactory: NotebookPanel.IContentFactory,
+  editorServices: IEditorServices,
+  rendermime: IRenderMimeRegistry,
+  translator: ITranslator,
+  trans: TranslationBundle,
+  riseFactory: IRisePreviewFactory | null
+) {
+  const allFileTypes = FILE_TYPES.concat(kernelFileTypeNames);
+  // 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
+  const factory = new NotebookWidgetFactory({
+    name: FACTORY,
+    label: trans.__(FACTORY),
+    fileTypes: allFileTypes,
+    modelName: notebookFactory.modelName ?? 'notebook',
+    preferKernel: notebookFactory.preferKernel ?? true,
+    canStartKernel: notebookFactory.canStartKernel ?? true,
+    rendermime,
+    contentFactory,
+    editorConfig: notebookFactory.editorConfig,
+    notebookConfig: notebookFactory.notebookConfig,
+    mimeTypeService: editorServices.mimeTypeService,
+    toolbarFactory: toolbarFactory,
+    translator,
+  });
+  docRegistry.addWidgetFactory(factory);
+
+  // Register widget created with the new factory in the notebook tracker
+  // This is required to activate notebook commands (and therefore shortcuts)
+  let id = 0; // The ID counter for notebook panels.
+  const ft = docRegistry.getFileType('notebook');
+
+  factory.widgetCreated.connect((sender, widget) => {
+    // If the notebook panel does not have an ID, assign it one.
+    widget.id = widget.id || `notebook-jupytext-${++id}`;
+
+    // Set up the title icon
+    widget.title.icon = ft?.icon;
+    widget.title.iconClass = ft?.iconClass ?? '';
+    widget.title.iconLabel = ft?.iconLabel ?? '';
+
+    // Notify the widget tracker if restore data needs to update.
+    widget.context.pathChanged.connect(() => {
+      // @ts-expect-error Trick using private API
+      void notebookTracker.save(widget);
+    });
+    // Add the notebook panel to the tracker.
+    //   @ts-expect-error Trick using private API
+    void notebookTracker.add(widget);
+  });
+
+  // Add support for RISE slides
+  if (riseFactory) {
+    for (const fileType of allFileTypes) {
+      riseFactory.addFileType(fileType);
+    }
+  }
+}
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts
index a700e7ed4..9efdfb136 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/index.ts
@@ -6,12 +6,13 @@ import {
 import {
   ICommandPalette,
   IToolbarWidgetRegistry,
-  createToolbarFactory,
   showErrorMessage,
 } from '@jupyterlab/apputils';
 
 import { IDocumentManager } from '@jupyterlab/docmanager';
 
+import { IEditorLanguageRegistry } from '@jupyterlab/codemirror';
+
 import { Contents } from '@jupyterlab/services';
 
 import { ILauncher } from '@jupyterlab/launcher';
@@ -31,36 +32,48 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
 
 import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
 
-import { IDocumentWidget } from '@jupyterlab/docregistry';
+import { IMainMenu } from '@jupyterlab/mainmenu';
 
 import { ITranslator, nullTranslator } from '@jupyterlab/translation';
 
-import { markdownIcon, notebookIcon } from '@jupyterlab/ui-components';
+import { LabIcon } from '@jupyterlab/ui-components';
 
 import { DisposableSet } from '@lumino/disposable';
 
+import { Menu } from '@lumino/widgets';
+
 import { JSONExt, ReadonlyJSONValue } from '@lumino/coreutils';
 
 import { IRisePreviewFactory } from 'jupyterlab-rise';
 
 import {
   JUPYTEXT_EXTENSION_ID,
-  FILE_TYPES,
+  JUPYTEXT_PAIR_COMMANDS_FILETYPE_DATA,
+  JUPYTEXT_FORMATS,
   TEXT_NOTEBOOKS_LAUNCHER_ICONS,
-  FACTORY,
   CommandIDs,
-  IJupytextFormat,
+  IFileTypeData,
+  JupytextIcon,
 } from './tokens';
 
 import {
-  getAvailJupytextFormats,
   isPairCommandToggled,
   isPairCommandEnabled,
   executePairCommand,
   isMetadataCommandToggled,
   isMetadataCommandEnabled,
   executeMetadataCommand,
-} from './jupytext';
+} from './commands';
+
+import { registerFileTypes } from './registry';
+
+import { createFactory } from './factory';
+
+import {
+  getAvailableKernelFileTypes,
+  getAvailableCreateTextNotebookCommands,
+  createNewTextNotebook,
+} from './utils';
 
 /**
  * Initialization data for the jupyterlab-jupytext extension.
@@ -70,6 +83,7 @@ const extension: JupyterFrontEndPlugin<void> = {
   autoStart: true,
   optional: [
     ILauncher,
+    IMainMenu,
     IDefaultFileBrowser,
     ITranslator,
     ICommandPalette,
@@ -79,6 +93,7 @@ const extension: JupyterFrontEndPlugin<void> = {
     NotebookPanel.IContentFactory,
     IEditorServices,
     IDocumentManager,
+    IEditorLanguageRegistry,
     IRenderMimeRegistry,
     INotebookWidgetFactory,
     INotebookTracker,
@@ -90,12 +105,14 @@ const extension: JupyterFrontEndPlugin<void> = {
     contentFactory: NotebookPanel.IContentFactory,
     editorServices: IEditorServices,
     docManager: IDocumentManager,
+    languages: IEditorLanguageRegistry,
     rendermime: IRenderMimeRegistry,
     notebookFactory: NotebookWidgetFactory.IFactory,
     notebookTracker: INotebookTracker,
     settingRegistry: ISettingRegistry,
     toolbarRegistry: IToolbarWidgetRegistry,
     launcher: ILauncher | null,
+    mainmenu: IMainMenu | null,
     defaultBrowser: IDefaultFileBrowser | null,
     translator: ITranslator | null,
     palette: ICommandPalette | null,
@@ -105,46 +122,136 @@ const extension: JupyterFrontEndPlugin<void> = {
     const trans = (translator ?? nullTranslator).load('jupytext');
 
     // Load settings
-    let launcherItems = TEXT_NOTEBOOKS_LAUNCHER_ICONS;
+    const includeFormats = TEXT_NOTEBOOKS_LAUNCHER_ICONS;
     let launcherItemsCategory = 'Jupytext';
     if (settingRegistry) {
       const settings = await settingRegistry.load(extension.id);
-      launcherItems = settings.get('format').composite as string[];
+      for (const format of JUPYTEXT_FORMATS) {
+        const addFormat = settings.get(format).composite as boolean;
+        if (addFormat && !includeFormats.includes(format)) {
+          includeFormats.push(format);
+        } else if (!addFormat && includeFormats.includes(format)) {
+          includeFormats.splice(includeFormats.indexOf(format), 1);
+        }
+      }
       launcherItemsCategory = settings.get('category').composite as string;
     }
 
     // Unpack necessary components
     const { commands, serviceManager, docRegistry } = app;
 
+    // Initialise Jupytext menu and add it to main menu
+    const jupytextMenu = new Menu({ commands: app.commands });
+    mainmenu.addMenu(jupytextMenu, true, { rank: 40 });
+    jupytextMenu.id = 'jp-mainmenu-jupytext-menu';
+    jupytextMenu.title.label = trans.__('Jupytext');
+
+    // Initialise Jupytext create notebook submenu and add it to Jupytext menu
+    const jupytextCreateMenu = new Menu({ commands: app.commands });
+    jupytextCreateMenu.id = 'jp-mainmenu-jupytext-new-menu';
+    jupytextCreateMenu.title.label = trans.__('New Text Notebook');
+    jupytextMenu.addItem({
+      type: 'submenu',
+      submenu: jupytextCreateMenu,
+    });
+
+    // Initialise Jupytext pair notebook submenu and add it to Jupytext menu
+    const jupytextPairMenu = new Menu({ commands: app.commands });
+    jupytextPairMenu.id = 'jp-mainmenu-jupytext-pair-menu';
+    jupytextPairMenu.title.label = trans.__('Pair Notebook');
+    jupytextMenu.addItem({
+      type: 'submenu',
+      submenu: jupytextPairMenu,
+    });
+
+    // Add a separator
+    jupytextMenu.addItem({
+      type: 'separator',
+    });
+
     // 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 = `jupytext:${format}`;
-      commands.addCommand(command, {
-        label: args['label'],
-        isToggled: () => {
-          return isPairCommandToggled(format, notebookTracker);
-        },
-        isEnabled: () => {
-          return isPairCommandEnabled(format, notebookTracker);
-        },
-        execute: () => {
-          return executePairCommand(command, format, notebookTracker, trans);
-        },
-      });
+    let rank = 0;
+    const separatorIndex: number[] = [];
+    JUPYTEXT_PAIR_COMMANDS_FILETYPE_DATA.forEach(
+      (value: IFileTypeData[], key: string) => {
+        value.map((fileType: IFileTypeData) => {
+          const format = key;
+          const command = `jupytext:pair-nb-with-${format}`;
+          commands.addCommand(command, {
+            label: (args) => {
+              if (args.isPalette) {
+                return (
+                  (fileType.paletteLabel as string) ?? trans.__('Pair notebook')
+                );
+              }
+              return (fileType.caption as string) ?? trans.__('Pair notebook');
+            },
+            caption: trans.__(fileType.caption),
+            icon: (args) => {
+              if (args.isPalette) {
+                return undefined;
+              } else {
+                return fileType.iconName
+                  ? LabIcon.resolve({
+                      icon: fileType.iconName as string,
+                    })
+                  : undefined;
+              }
+            },
+            isToggled: () => {
+              return isPairCommandToggled(format, notebookTracker);
+            },
+            isEnabled: () => {
+              return isPairCommandEnabled(format, notebookTracker);
+            },
+            execute: () => {
+              return executePairCommand(
+                command,
+                format,
+                notebookTracker,
+                trans
+              );
+            },
+          });
 
-      console.log(
-        'Registering pairing command=' + command + ' with rank=' + (rank + 1)
-      );
-      palette?.addItem({ command, rank: rank + 2, category: 'Jupytext' });
+          console.debug(
+            'Registering pairing command=' + command + ' with rank=' + rank
+          );
+          palette?.addItem({
+            command,
+            args: { isPalette: true },
+            rank: rank + 1,
+            category: 'Jupytext',
+          });
+          // Add to jupytext pair menu
+          jupytextPairMenu.addItem({
+            command: command,
+          });
+          if (fileType.separator) {
+            separatorIndex.push(rank);
+          }
+          rank += 1;
+        });
+      }
+    );
+
+    // Add separators in jupytext pair menu
+    separatorIndex.map((index, idx) => {
+      jupytextPairMenu.insertItem(index + idx + 1, {
+        type: 'separator',
+      });
     });
 
     // Metadata in text representation
     commands.addCommand(CommandIDs.metadata, {
       label: trans.__('Include Metadata'),
+      icon: (args) => {
+        if (args.isPalette) {
+          return undefined;
+        } else {
+          return JupytextIcon;
+        }
+      },
       isToggled: () => {
         return isMetadataCommandToggled(notebookTracker);
       },
@@ -157,140 +264,65 @@ const extension: JupyterFrontEndPlugin<void> = {
     });
     palette?.addItem({
       command: CommandIDs.metadata,
-      rank: JUPYTEXT_FORMATS.length + 3,
+      args: { isPalette: true },
+      rank: 98,
       category: 'Jupytext',
     });
+    jupytextMenu.addItem({
+      type: 'separator',
+    });
+    jupytextMenu.addItem({
+      command: CommandIDs.metadata,
+    });
 
     // Register Jupytext FAQ command
     commands.addCommand(CommandIDs.faq, {
       label: trans.__('Jupytext FAQ'),
+      icon: (args) => {
+        if (args.isPalette) {
+          return undefined;
+        } else {
+          return JupytextIcon;
+        }
+      },
       execute: () => {
         window.open('https://jupytext.readthedocs.io/en/latest/faq.html');
       },
     });
     palette?.addItem({
       command: CommandIDs.faq,
+      args: { isPalette: true },
       rank: 99,
       category: 'Jupytext',
     });
+    jupytextMenu.addItem({
+      command: CommandIDs.faq,
+    });
 
     // Register Jupytext Reference
     commands.addCommand(CommandIDs.reference, {
       label: trans.__('Jupytext Reference'),
+      icon: (args) => {
+        if (args.isPalette) {
+          return undefined;
+        } else {
+          return JupytextIcon;
+        }
+      },
       execute: () => {
         window.open('https://jupytext.readthedocs.io/en/latest/');
       },
     });
     palette?.addItem({
-      command: CommandIDs.faq,
+      command: CommandIDs.reference,
+      args: { isPalette: true },
       rank: 100,
       category: 'Jupytext',
     });
-
-    // Define file types
-    docRegistry.addFileType(
-      {
-        name: 'myst',
-        contentType: 'notebook',
-        displayName: trans.__('MyST Markdown Notebook'),
-        extensions: ['.myst', '.mystnb', '.mnb'],
-        icon: markdownIcon,
-      },
-      ['Notebook']
-    );
-
-    docRegistry.addFileType(
-      {
-        name: 'r-markdown',
-        contentType: 'notebook',
-        displayName: trans.__('R Markdown Notebook'),
-        // Extension file are transformed to lower case...
-        extensions: ['.Rmd'],
-        icon: markdownIcon,
-      },
-      ['Notebook']
-    );
-
-    docRegistry.addFileType(
-      {
-        name: 'quarto',
-        contentType: 'notebook',
-        displayName: trans.__('Quarto Notebook'),
-        extensions: ['.qmd'],
-        icon: markdownIcon,
-      },
-      ['Notebook']
-    );
-
-    // 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
-    const factory = new NotebookWidgetFactory({
-      name: FACTORY,
-      label: trans.__(FACTORY),
-      fileTypes: FILE_TYPES,
-      modelName: notebookFactory.modelName ?? 'notebook',
-      preferKernel: notebookFactory.preferKernel ?? true,
-      canStartKernel: notebookFactory.canStartKernel ?? true,
-      rendermime,
-      contentFactory,
-      editorConfig: notebookFactory.editorConfig,
-      notebookConfig: notebookFactory.notebookConfig,
-      mimeTypeService: editorServices.mimeTypeService,
-      toolbarFactory: toolbarFactory,
-      translator,
-    });
-    docRegistry.addWidgetFactory(factory);
-
-    // Register widget created with the new factory in the notebook tracker
-    // This is required to activate notebook commands (and therefore shortcuts)
-    let id = 0; // The ID counter for notebook panels.
-    const ft = docRegistry.getFileType('notebook');
-
-    factory.widgetCreated.connect((sender, widget) => {
-      // If the notebook panel does not have an ID, assign it one.
-      widget.id = widget.id || `notebook-jupytext-${++id}`;
-
-      // Set up the title icon
-      widget.title.icon = ft?.icon;
-      widget.title.iconClass = ft?.iconClass ?? '';
-      widget.title.iconLabel = ft?.iconLabel ?? '';
-
-      // Notify the widget tracker if restore data needs to update.
-      widget.context.pathChanged.connect(() => {
-        // @ts-expect-error Trick using private API
-        void notebookTracker.save(widget);
-      });
-      // Add the notebook panel to the tracker.
-      //   @ts-expect-error Trick using private API
-      void notebookTracker.add(widget);
+    jupytextMenu.addItem({
+      command: CommandIDs.reference,
     });
 
-    // Add support for RISE slides
-    if (riseFactory) {
-      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
@@ -323,43 +355,121 @@ const extension: JupyterFrontEndPlugin<void> = {
       category: 'Jupytext',
     });
 
+    // Get a map of available kernels in current widget
+    const availableKernels = await getAvailableKernelFileTypes(
+      languages,
+      serviceManager
+    );
+
+    // Get a map of all create text notebook commands
+    const createTextNotebookCommands =
+      await getAvailableCreateTextNotebookCommands(
+        includeFormats,
+        availableKernels
+      );
+
+    // Register Jupytext text notebooks file types
+    registerFileTypes(docRegistry, trans);
+
+    // Get all kernel file types to add to Jupytext factory
+    const kernelFileTypeNames = [];
+    for (const kernelFileTypes of availableKernels.values()) {
+      for (const kernelFileType of kernelFileTypes) {
+        kernelFileTypeNames.push(kernelFileType.kernelName);
+      }
+    }
+    // Create a factory for Jupytext
+    createFactory(
+      kernelFileTypeNames,
+      toolbarRegistry,
+      settingRegistry,
+      docRegistry,
+      notebookTracker,
+      notebookFactory,
+      contentFactory,
+      editorServices,
+      rendermime,
+      translator,
+      trans,
+      riseFactory
+    );
+
     // 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);
+    createTextNotebookCommands.forEach((fileTypes: IFileTypeData[], _) => {
+      fileTypes.map((fileType: IFileTypeData) => {
+        const format = fileType.fileExt;
+        const command = `jupytext:create-new-text-notebook-${format}`;
+        const iconName = fileType.iconName;
+        const kernelIcon = fileType.kernelIcon;
         commands.addCommand(command, {
           label: (args) => {
             if (args['isLauncher']) {
-              return trans.__(label);
+              return trans.__(fileType.launcherLabel);
             }
-            if (args['isPalette'] || args['isContextMenu']) {
-              return trans.__(`New Text Notebook with ${label}`);
+            return trans.__(fileType.paletteLabel);
+          },
+          caption: trans.__(fileType.caption),
+          icon: (args) => {
+            if (args.isPalette) {
+              return undefined;
+            } else {
+              if (iconName) {
+                return LabIcon.resolve({
+                  icon: iconName as string,
+                });
+              }
+              if (kernelIcon) {
+                return kernelIcon;
+              }
+              return LabIcon.resolve({
+                icon: 'ui-components:kernel',
+              });
             }
-            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);
+            return createNewTextNotebook(
+              cwd,
+              kernelId,
+              kernelName,
+              format,
+              commands
+            );
           },
         });
+
+        console.debug(
+          'Registering create new text notebook command=' +
+            command +
+            ' with rank=' +
+            rank
+        );
         palette?.addItem({
           command,
           args: { isPalette: true },
-          rank: rank + 51,
+          rank: rank,
           category: 'Jupytext',
         });
+        if (includeFormats.includes(format)) {
+          jupytextCreateMenu.addItem({
+            command: command,
+            args: { isMainMenu: true },
+          });
+          // Add separator after each kernel type
+          if (fileType.separator) {
+            jupytextCreateMenu.addItem({
+              type: 'separator',
+            });
+          }
+        }
 
         // Add a launcher item if the launcher is available.
-        if (launcher && launcherItems.includes(jupytextFormat.format)) {
+        if (launcher && includeFormats.includes(format)) {
           void serviceManager.ready.then(() => {
             let disposables: DisposableSet | null = null;
             const onSpecsChanged = () => {
@@ -372,57 +482,32 @@ const extension: JupyterFrontEndPlugin<void> = {
                 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,
-                    },
-                  })
-                );
-              }
+              const kernelIconUrl =
+                specs.kernelspecs[fileType.kernelName]?.resources['logo-svg'] ||
+                specs.kernelspecs[fileType.kernelName]?.resources['logo-64x64'];
+              disposables.add(
+                launcher.add({
+                  command: command,
+                  args: { isLauncher: true, kernelName: fileType.kernelName },
+                  category: trans.__(launcherItemsCategory),
+                  rank: rank++,
+                  kernelIconUrl,
+                  metadata: {
+                    kernel: JSONExt.deepCopy(
+                      specs.kernelspecs[fileType.kernelName]?.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
+        // Increment rank
+        rank += 1;
       });
-      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/registry.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/registry.ts
new file mode 100644
index 000000000..5a628c9a7
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/registry.ts
@@ -0,0 +1,45 @@
+import { DocumentRegistry } from '@jupyterlab/docregistry';
+
+import { TranslationBundle } from '@jupyterlab/translation';
+
+import { markdownIcon } from '@jupyterlab/ui-components';
+
+export function registerFileTypes(
+  docRegistry: DocumentRegistry,
+  trans: TranslationBundle
+) {
+  // Add markdown file types to registry
+  docRegistry.addFileType(
+    {
+      name: 'myst',
+      contentType: 'notebook',
+      displayName: trans.__('MyST Markdown Notebook'),
+      extensions: ['.myst', '.mystnb', '.mnb'],
+      icon: markdownIcon,
+    },
+    ['Notebook']
+  );
+
+  docRegistry.addFileType(
+    {
+      name: 'r-markdown',
+      contentType: 'notebook',
+      displayName: trans.__('R Markdown Notebook'),
+      // Extension file are transformed to lower case...
+      extensions: ['.Rmd'],
+      icon: markdownIcon,
+    },
+    ['Notebook']
+  );
+
+  docRegistry.addFileType(
+    {
+      name: 'quarto',
+      contentType: 'notebook',
+      displayName: trans.__('Quarto Notebook'),
+      extensions: ['.qmd'],
+      icon: markdownIcon,
+    },
+    ['Notebook']
+  );
+}
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/svg.d.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/svg.d.ts
new file mode 100644
index 000000000..b497c6ec4
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/svg.d.ts
@@ -0,0 +1,4 @@
+declare module '*.svg' {
+  const image: string;
+  export default image;
+}
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/tokens.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/tokens.ts
index 2a3c22cbe..8dcaeef02 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/src/tokens.ts
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/tokens.ts
@@ -1,3 +1,9 @@
+import { ServerConnection } from '@jupyterlab/services';
+
+import { LabIcon } from '@jupyterlab/ui-components';
+
+import jupytextSvgstr from '../style/icons/logo.svg';
+
 /**
  * Jupytext extension ID
  */
@@ -19,7 +25,7 @@ export const LANGUAGE_INDEPENDENT_NOTEBOOK_EXTENSIONS = [
 ];
 
 /**
- * Supported file types.
+ * Supported file types names.
  */
 export const FILE_TYPES = [
   'markdown',
@@ -47,71 +53,315 @@ export namespace CommandIDs {
 }
 
 /**
- * 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',
-  },
-];
+ * Jupytext logo icon
+ */
+export const JupytextIcon = new LabIcon({
+  name: `${NS}:icon:logo`,
+  svgstr: jupytextSvgstr,
+});
+
+/**
+ * Current Jupyter server settings
+ */
+export const SERVER_SETTINGS = ServerConnection.makeSettings();
+
+/**
+ * Supported Jupytext pairings along with metadata.
+ */
+export const JUPYTEXT_PAIR_COMMANDS_FILETYPE_DATA = new Map<
+  string,
+  IFileTypeData[]
+>([
+  [
+    'ipynb',
+    [
+      {
+        fileExt: 'ipynb',
+        paletteLabel: 'Pair with ipynb',
+        caption: 'Pair Notebook with ipynb document',
+        iconName: 'ui-components:notebook',
+        separator: true,
+      },
+    ],
+  ],
+  [
+    'auto:light',
+    [
+      {
+        fileExt: 'auto:light',
+        paletteLabel: 'Pair with light script',
+        caption: 'Pair Notebook with Light Format',
+        iconName: 'ui-components:text-editor',
+      },
+    ],
+  ],
+  [
+    'auto:percent',
+    [
+      {
+        fileExt: 'auto:percent',
+        paletteLabel: 'Pair with percent script',
+        caption: 'Pair Notebook with Percent Format',
+        iconName: 'ui-components:text-editor',
+      },
+    ],
+  ],
+  [
+    'auto:hydrogen',
+    [
+      {
+        fileExt: 'auto:hydrogen',
+        paletteLabel: 'Pair with hydrogen script',
+        caption: 'Pair Notebook with Hydrogen Format',
+        iconName: 'ui-components:text-editor',
+      },
+    ],
+  ],
+  [
+    'auto:nomarker',
+    [
+      {
+        fileExt: 'auto:nomarker',
+        paletteLabel: 'Pair with nomarker script',
+        caption: 'Pair Notebook with Nomarker Format',
+        iconName: 'ui-components:text-editor',
+        separator: true,
+      },
+    ],
+  ],
+  [
+    'md',
+    [
+      {
+        fileExt: 'md',
+        paletteLabel: 'Pair with md',
+        caption: 'Pair Notebook with Markdown',
+        iconName: 'ui-components:markdown',
+      },
+    ],
+  ],
+  [
+    'md:myst',
+    [
+      {
+        fileExt: 'md:myst',
+        paletteLabel: 'Pair with myst md',
+        caption: 'Pair Notebook with MyST Markdown',
+        iconName: 'ui-components:markdown',
+        separator: true,
+      },
+    ],
+  ],
+  [
+    'Rmd',
+    [
+      {
+        fileExt: 'Rmd',
+        paletteLabel: 'Pair with Rmd',
+        caption: 'Pair Notebook with R Markdown',
+        iconName: 'ui-components:markdown',
+      },
+    ],
+  ],
+  [
+    'qmd',
+    [
+      {
+        fileExt: 'qmd',
+        paletteLabel: 'Pair with qmd',
+        caption: 'Pair Notebook with Quarto (qmd)',
+        iconName: 'ui-components:markdown',
+        separator: true,
+      },
+    ],
+  ],
+  [
+    'custom',
+    [
+      {
+        fileExt: 'custom',
+        paletteLabel: 'Custom pair',
+        caption: 'Custom Pairing',
+        iconName: 'ui-components:text-editor',
+      },
+    ],
+  ],
+  [
+    'none',
+    [
+      {
+        fileExt: 'none',
+        paletteLabel: 'Unpair',
+        caption: 'Unpair Current Notebook',
+      },
+    ],
+  ],
+]);
+
+/**
+ * Supported kernels file types metadata
+ */
+export const AUTO_LANGUAGE_FILETYPE_DATA = new Map<string, IFileTypeData[]>([
+  [
+    'python',
+    [
+      {
+        fileExt: 'py',
+        paletteLabel: 'New Python Text Notebook',
+        caption: 'Create a new Python Text Notebook',
+        iconName: 'ui-components:python',
+        launcherLabel: 'Python',
+        kernelName: 'python3',
+      },
+    ],
+  ],
+  [
+    'julia',
+    [
+      {
+        fileExt: 'jl',
+        paletteLabel: 'New Julia Text Notebook',
+        caption: 'Create a new Julia Text Notebook',
+        iconName: 'ui-components:julia',
+        launcherLabel: 'Julia',
+        kernelName: 'julia',
+      },
+    ],
+  ],
+  [
+    'R',
+    [
+      {
+        fileExt: 'R',
+        paletteLabel: 'New R Text Notebook',
+        caption: 'Create a new R Text Notebook',
+        iconName: 'ui-components:r-kernel',
+        launcherLabel: 'R',
+        kernelName: 'ir',
+      },
+    ],
+  ],
+]);
+
+/**
+ * Supported Jupytext create new text notebooks file types
+ */
+export const JUPYTEXT_CREATE_TEXT_NOTEBOOK_FILETYPE_DATA = new Map<
+  string,
+  IFileTypeData[]
+>([
+  [
+    'auto:light',
+    [
+      {
+        fileExt: 'auto:light',
+        paletteLabel: 'Light Format',
+        caption: 'Light Format',
+        launcherLabel: 'Light Format',
+      },
+    ],
+  ],
+  [
+    'auto:percent',
+    [
+      {
+        fileExt: 'auto:percent',
+        paletteLabel: 'Percent Format',
+        caption: 'Percent Format',
+        launcherLabel: 'Percent Format',
+      },
+    ],
+  ],
+  [
+    'auto:hydrogen',
+    [
+      {
+        fileExt: 'auto:hydrogen',
+        paletteLabel: 'Hydrogen Format',
+        caption: 'Hydrogen Format',
+        launcherLabel: 'Hydrogen Format',
+      },
+    ],
+  ],
+  [
+    'auto:nomarker',
+    [
+      {
+        fileExt: 'auto:nomarker',
+        paletteLabel: 'Nomarker Format',
+        caption: 'Nomarker Format',
+        launcherLabel: 'Nomarker Format',
+      },
+    ],
+  ],
+  [
+    'md',
+    [
+      {
+        fileExt: 'md',
+        paletteLabel: 'New Markdown Text Notebook',
+        caption: 'Create a new Markdown Text Notebook',
+        iconName: 'ui-components:markdown',
+        launcherLabel: 'Markdown',
+      },
+    ],
+  ],
+  [
+    'md:myst',
+    [
+      {
+        fileExt: 'md:myst',
+        paletteLabel: 'New MyST Markdown Text Notebook',
+        caption: 'Create a new MyST Markdown Text Notebook',
+        iconName: 'ui-components:markdown',
+        launcherLabel: 'MyST Markdown',
+      },
+    ],
+  ],
+  [
+    'Rmd',
+    [
+      {
+        fileExt: 'Rmd',
+        paletteLabel: 'New R Markdown Text Notebook',
+        caption: 'Create a new R Markdown Text Notebook',
+        iconName: 'ui-components:markdown',
+        launcherLabel: 'R Markdown',
+      },
+    ],
+  ],
+  [
+    'qmd',
+    [
+      {
+        fileExt: 'qmd',
+        paletteLabel: 'New Quarto Markdown Text Notebook',
+        caption: 'Create a new Quarto Markdown Text Notebook',
+        iconName: 'ui-components:markdown',
+        launcherLabel: 'Quarto Markdown',
+      },
+    ],
+  ],
+]);
 
 /**
  * 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);
-});
+export const JUPYTEXT_FORMATS = Array.from(
+  JUPYTEXT_PAIR_COMMANDS_FILETYPE_DATA.keys()
+)
+  .map((format) => {
+    return 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) => {
+export const TEXT_NOTEBOOKS_LAUNCHER_ICONS = JUPYTEXT_FORMATS.filter(
+  (format) => {
     return ![
       'ipynb',
       'auto:hydrogen',
@@ -120,14 +370,21 @@ export const TEXT_NOTEBOOKS_LAUNCHER_ICONS =
       'custom',
       'none',
     ].includes(format);
-  });
+  }
+);
 
 /**
- * An interface for Jupytext format
+ * An interface for file type metadata
  */
-export interface IJupytextFormat {
-  format: string;
-  label: string;
+export interface IFileTypeData {
+  fileExt: string;
+  paletteLabel: string;
+  caption: string;
+  iconName?: string;
+  kernelIcon?: LabIcon;
+  launcherLabel?: string;
+  kernelName?: string;
+  separator?: boolean;
 }
 
 /**
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/src/utils.ts b/jupyterlab/packages/jupyterlab-jupytext-extension/src/utils.ts
new file mode 100644
index 000000000..6000c09a1
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/src/utils.ts
@@ -0,0 +1,231 @@
+import { IEditorLanguageRegistry } from '@jupyterlab/codemirror';
+
+import {
+  ServiceManager,
+  ServerConnection,
+  KernelSpec,
+} from '@jupyterlab/services';
+
+import { LabIcon } from '@jupyterlab/ui-components';
+
+import { URLExt } from '@jupyterlab/coreutils';
+
+import { IDocumentWidget } from '@jupyterlab/docregistry';
+
+import { CommandRegistry } from '@lumino/commands';
+
+import { Buffer } from 'buffer';
+
+import {
+  NS,
+  SERVER_SETTINGS,
+  AUTO_LANGUAGE_FILETYPE_DATA,
+  JUPYTEXT_CREATE_TEXT_NOTEBOOK_FILETYPE_DATA,
+  FACTORY,
+  CommandIDs,
+  IFileTypeData,
+} from './tokens';
+
+/**
+ * Get kernel icon SVG string
+ */
+async function getKernelIconBase64String(
+  kernelIconUrl: string
+): Promise<string> {
+  // Seems like URL prefix is already included in kernelIconUrl. We need to strip
+  // it off as baseUrl will already has url prefix.
+  kernelIconUrl = `/kernelspecs${kernelIconUrl.split('kernelspecs')[1]}`;
+  const url = URLExt.join(SERVER_SETTINGS.baseUrl, kernelIconUrl);
+  const response = await ServerConnection.makeRequest(url, {}, SERVER_SETTINGS);
+  const blob = await response.arrayBuffer();
+  const contentType = response.headers.get('content-type');
+  return `data:${contentType};base64,${Buffer.from(blob).toString('base64')}`;
+}
+
+/**
+ * Make a SVG string from base64 image
+ */
+function base64ToSvgStr(width: number, imageBase64: string): string {
+  return `<svg xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none" viewBox="0 0 ${width} ${width}">
+  <g id="layer1">
+     <image xlink:href="${imageBase64}"/>
+  </g>
+</svg>`.replace(/\n/g, ' ');
+}
+
+/**
+ * Get kernel Icon
+ */
+async function getKernelIcon(
+  specModel: KernelSpec.ISpecModel
+): Promise<LabIcon> {
+  // First check for logo-svg
+  if (specModel.resources['logo-svg']) {
+    const svgStr = await getKernelIconBase64String(
+      specModel.resources['logo-svg']
+    );
+    return new LabIcon({
+      name: `${NS}:icon:${specModel.name}`,
+      svgstr: svgStr,
+    });
+  }
+  // Else check if 64x64 kernel icon is availble
+  if (specModel.resources['logo-64x64']) {
+    const iconBase64String = await getKernelIconBase64String(
+      specModel.resources['logo-64x64']
+    );
+    return new LabIcon({
+      name: `${NS}:icon:${specModel.name}`,
+      svgstr: base64ToSvgStr(64, iconBase64String),
+    });
+  }
+  // Finally check for 32x32 kernel icon
+  if (specModel.resources['logo-32x32']) {
+    const iconBase64String = await getKernelIconBase64String(
+      specModel.resources['logo-32x32']
+    );
+    return new LabIcon({
+      name: `${NS}:icon:${specModel.name}`,
+      svgstr: base64ToSvgStr(32, iconBase64String),
+    });
+  }
+  // If not found, make a generic kernel icon
+  return LabIcon.resolve({
+    icon: 'ui-components:kernel',
+  });
+}
+
+/**
+ * Get all available kernel file types so that we replace auto format
+ * with these file extensions
+ */
+export async function getAvailableKernelFileTypes(
+  languages: IEditorLanguageRegistry,
+  serviceManager: ServiceManager.IManager
+): Promise<Map<string, IFileTypeData[]>> {
+  const specsManager = serviceManager.kernelspecs;
+  await specsManager.ready;
+  const fileTypes = new Map<string, IFileTypeData[]>();
+  const specs = specsManager.specs?.kernelspecs ?? {};
+  for (const [spec, specModel] of Object.entries(specs)) {
+    if (specModel) {
+      // First check if kernel is one of the pre-defined types (Python, R, Julia)
+      const exts = AUTO_LANGUAGE_FILETYPE_DATA.get(specModel.language);
+      if (exts !== undefined) {
+        fileTypes.set(specModel.language, exts);
+      } else {
+        // If not, try to get languageInfo from codemirror languages
+        const languageInfo = languages.findByName(specModel.language);
+        // If we managed to find the language, construct the FileTypeData
+        // Here we make an assumption that first extension in
+        // languageInfo.extensions is the most common one.
+        if (languageInfo) {
+          // We attempt to get kernelIcon here for specModel.resources
+          // If none provided, we return generic kernel icon
+          const kernelIcon = await getKernelIcon(specModel);
+          const exts: IFileTypeData[] = [
+            {
+              fileExt: languageInfo.extensions[0],
+              paletteLabel: `New ${languageInfo.displayName} Text Notebook`,
+              caption: `Create a new ${languageInfo.displayName} Text Notebook`,
+              kernelIcon: kernelIcon,
+              launcherLabel: specModel.display_name || languageInfo.displayName,
+              kernelName: spec,
+            },
+          ];
+          fileTypes.set(specModel.language, exts);
+        }
+      }
+    }
+  }
+  return fileTypes;
+}
+
+/**
+ * Get all available 'Create New Text Notebook' commands based on configured
+ * formats and available kernels
+ */
+export async function getAvailableCreateTextNotebookCommands(
+  launcherItems: string[],
+  availableKernels: Map<string, IFileTypeData[]>
+): Promise<Map<string, IFileTypeData[]>> {
+  const numKernels = availableKernels.size;
+
+  // Initialise a map of 'Create New Text Notebook' command filetypes
+  const createTextNotebookCommands = new Map<string, IFileTypeData[]>();
+
+  // Iterate through all JUPYTEXT_CREATE_TEXT_NOTEBOOK_FILETYPE_DATA file types.
+  JUPYTEXT_CREATE_TEXT_NOTEBOOK_FILETYPE_DATA.forEach(
+    (fileTypes: IFileTypeData[], format: string) => {
+      fileTypes.map((fileType: IFileTypeData) => {
+        // If format is auto, we need to add all currently available kernels
+        // For instance if there are Python and R kernels available, format
+        // auto:light will be replaced by py:light and R:light
+        if (format.startsWith('auto')) {
+          const formatType = format.split(':')[1];
+          let mapIndex = 0;
+          availableKernels.forEach(
+            (kernelFileTypes: IFileTypeData[], kernelKey: string) => {
+              const updatedKernelKey = `${kernelKey}:${formatType}`;
+              createTextNotebookCommands.set(updatedKernelKey, []);
+              mapIndex += 1;
+              kernelFileTypes.map((kernelFileType) => {
+                // Merge fileType object from kernel and Jupytext format and push
+                // it to createTextNotebookCommands
+                const updatedKernelFileType = { ...kernelFileType };
+                updatedKernelFileType.fileExt = `${updatedKernelFileType.fileExt}:${formatType}`;
+                updatedKernelFileType.paletteLabel = `${updatedKernelFileType.paletteLabel} with ${fileType.paletteLabel}`;
+                updatedKernelFileType.caption = `${updatedKernelFileType.caption} with ${fileType.caption}`;
+                updatedKernelFileType.launcherLabel = `${updatedKernelFileType.launcherLabel} - ${fileType.launcherLabel}`;
+                if (numKernels === mapIndex) {
+                  updatedKernelFileType.separator = true;
+                }
+                createTextNotebookCommands
+                  .get(updatedKernelKey)
+                  .push(updatedKernelFileType);
+                if (launcherItems.includes(format)) {
+                  launcherItems.push(updatedKernelFileType.fileExt);
+                }
+              });
+            }
+          );
+        } else {
+          if (createTextNotebookCommands.get(format) === undefined) {
+            createTextNotebookCommands.set(format, []);
+          }
+          createTextNotebookCommands.get(format).push(fileType);
+        }
+      });
+    }
+  );
+  return createTextNotebookCommands;
+}
+
+/**
+ * Create New Text Notebook file
+ */
+export const createNewTextNotebook = async (
+  cwd: string,
+  kernelId: string,
+  kernelName: string,
+  format: string,
+  commands: CommandRegistry
+) => {
+  const model = await commands.execute(CommandIDs.newUntitled, {
+    path: cwd,
+    type: 'notebook',
+    // We should not have auto in format at this point. If somehow we end up having
+    // it, ensure we replace by py as Python kernel exists always
+    ext: format.replace('auto', '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/style/icons/logo.svg b/jupyterlab/packages/jupyterlab-jupytext-extension/style/icons/logo.svg
new file mode 100644
index 000000000..57d6f4a52
--- /dev/null
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/style/icons/logo.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124 91">
+  <g fill="none" fill-rule="nonzero">
+    <path fill="#FACDAF" d="M.4 21.6v-3.2h15.64v3.2z"/>
+    <path fill="#FA6400" d="M38.64 5.16c-.827 0-1.453-.213-1.88-.64-.427-.427-.64-1.053-.64-1.88 0-.8.22-1.413.66-1.84.44-.427 1.06-.64 1.86-.64s1.413.213 1.84.64c.427.427.64 1.04.64 1.84 0 .827-.213 1.453-.64 1.88-.427.427-1.04.64-1.84.64zm2.04 4.04L40.64 32c0 1.707-.373 3.2-1.12 4.48-.747 1.28-1.72 2.253-2.92 2.92-1.2.667-2.493 1-3.88 1-1.573 0-3.053-.44-4.44-1.32-1.387-.88-2.493-2.213-3.32-4L28 33.72c.48 1.093 1.147 1.92 2 2.48s1.773.84 2.76.84c1.253 0 2.313-.44 3.18-1.32.867-.88 1.3-2.12 1.3-3.72V12l-4.68-.28V9.2h8.12zM66.48 32v-3.28c-.613 1.173-1.547 2.113-2.8 2.82a8.093 8.093 0 01-4.04 1.06c-1.6 0-3.04-.507-4.32-1.52-1.28-1.013-2.28-2.373-3-4.08-.72-1.707-1.08-3.56-1.08-5.56V9.16h3.64v13.12c0 1.28.273 2.48.82 3.6.547 1.12 1.3 2.02 2.26 2.7.96.68 2.053 1.02 3.28 1.02 1.147 0 2.067-.32 2.76-.96.693-.64 1.52-1.747 2.48-3.32V9.2h3.4v20.16c0 1.227.08 2.107.24 2.64h-3.64zM87.64 8.56c3.2 0 5.687 1 7.46 3 1.773 2 2.66 4.8 2.66 8.4 0 2.507-.433 4.713-1.3 6.62s-2.1 3.38-3.7 4.42-3.44 1.56-5.52 1.56c-2.56 0-4.707-.76-6.44-2.28V40l-3.4.24V9.2h4.28c-.213.293-.413.713-.6 1.26A4.777 4.777 0 0080.8 12v.08c.853-1.227 1.827-2.12 2.92-2.68s2.4-.84 3.92-.84zm-.4 20.76c1.36 0 2.58-.4 3.66-1.2 1.08-.8 1.927-1.907 2.54-3.32.613-1.413.92-3.027.92-4.84 0-2.533-.613-4.567-1.84-6.1-1.227-1.533-2.853-2.3-4.88-2.3-1.44 0-2.753.393-3.94 1.18-1.187.787-2.153 1.9-2.9 3.34v9.72c.507 1.227 1.28 2.12 2.32 2.68 1.04.56 2.413.84 4.12.84zM119.6 9.2h3.8l-.136.533c-.655 2.483-1.876 6.146-3.664 10.987-1.92 5.2-3.853 10.04-5.8 14.52-1.28 3.52-3.493 5.28-6.64 5.28-.64 0-1.24-.053-1.8-.16l.56-2.64c.213.053.52.08.92.08 1.813 0 3.16-1.187 4.04-3.56l1.4-3.44h-.04l-.263-.686c-.538-1.384-1.317-3.263-2.337-5.636l-1.22-2.829c-1.266-2.964-2.24-5.348-2.92-7.149-.76-2.013-1.26-3.78-1.5-5.3h3.36c.22 1.418.67 3.037 1.349 4.858l.191.502c.787 2.027 1.94 4.813 3.46 8.36l1.56 3.6a282.728 282.728 0 003.66-10.18l.305-.917c.88-2.685 1.451-4.759 1.715-6.223z"/>
+    <path fill="#BDDD9B" d="M16.04 79.24H9.72v6.24h-3.2v-6.24H.32v-3.2h6.2V69.8h3.2v6.24h6.32z"/>
+    <path fill="#6DD400" d="M44.76 85.28c-.827 1.787-1.933 3.12-3.32 4-1.387.88-2.867 1.32-4.44 1.32a7.861 7.861 0 01-3.88-1c-1.2-.667-2.173-1.64-2.92-2.92-.747-1.28-1.12-2.773-1.12-4.48V70.28H25.4V67.2h3.68v-8.64l3.4.24v8.4h9.72v3.08h-9.72V82.2c0 1.6.433 2.84 1.3 3.72.867.88 1.927 1.32 3.18 1.32.987 0 1.907-.28 2.76-.84.853-.56 1.52-1.387 2-2.48l3.04 1.36zm16.4-18.72c3.227 0 5.66 1.013 7.3 3.04 1.64 2.027 2.393 4.893 2.26 8.6l-.08 1.96-16.24.08c0 1.413.287 2.673.86 3.78.573 1.107 1.38 1.967 2.42 2.58s2.227.92 3.56.92c1.093 0 2.193-.247 3.3-.74 1.107-.493 2.26-1.327 3.46-2.5l2.04 2.48a11.684 11.684 0 01-3.94 2.84c-1.507.667-3.087 1-4.74 1-2 0-3.787-.44-5.36-1.32a9.391 9.391 0 01-3.68-3.68c-.88-1.573-1.32-3.36-1.32-5.36V78.2c.107-3.733 1.02-6.607 2.74-8.62 1.72-2.013 4.193-3.02 7.42-3.02zm5.92 10.6c.133-2.453-.333-4.333-1.4-5.64-1.067-1.307-2.693-1.96-4.88-1.96-2.107 0-3.667.653-4.68 1.96s-1.573 3.187-1.68 5.64h12.64zM93.04 90l-5.6-8.8L81.8 90h-3.64l7.52-11.6-7.12-11.2h3.6l5.32 8.36 5.4-8.36h3.6l-7.2 11.2 7.4 11.6h-3.64zm30.92-4.72c-.827 1.787-1.933 3.12-3.32 4-1.387.88-2.867 1.32-4.44 1.32a7.861 7.861 0 01-3.88-1c-1.2-.667-2.173-1.64-2.92-2.92-.747-1.28-1.12-2.773-1.12-4.48V70.28h-3.68V67.2h3.68v-8.64l3.4.24v8.4h9.72v3.08h-9.72V82.2c0 1.6.433 2.84 1.3 3.72.867.88 1.927 1.32 3.18 1.32.987 0 1.907-.28 2.76-.84.853-.56 1.52-1.387 2-2.48l3.04 1.36z"/>
+  </g>
+</svg>
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/tsconfig.json b/jupyterlab/packages/jupyterlab-jupytext-extension/tsconfig.json
index 5527c4f34..6573a69e1 100644
--- a/jupyterlab/packages/jupyterlab-jupytext-extension/tsconfig.json
+++ b/jupyterlab/packages/jupyterlab-jupytext-extension/tsconfig.json
@@ -11,7 +11,7 @@
     "strict": true,
     "strictNullChecks": false,
     "target": "es2018",
-    "types": [],
+    "types": ["node"],
     "esModuleInterop": true
   },
   "include": ["src/*"]
diff --git a/jupyterlab/yarn.lock b/jupyterlab/yarn.lock
index 4bd48a121..a5d935990 100644
--- a/jupyterlab/yarn.lock
+++ b/jupyterlab/yarn.lock
@@ -1501,7 +1501,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@lumino/commands@npm:^2.0.1, @lumino/commands@npm:^2.1.3, @lumino/commands@npm:^2.2.0":
+"@lumino/commands@npm:^2.0.0, @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:
@@ -7009,8 +7009,10 @@ __metadata:
     "@jupyterlab/settingregistry": ^4.0.0
     "@jupyterlab/translation": ^4.0.0
     "@jupyterlab/ui-components": ^4.0.0
+    "@lumino/commands": ^2.0.0
     "@lumino/coreutils": ^2.0.0
     "@lumino/disposable": ^2.0.0
+    buffer: ^6.0.3
     jupyterlab-rise: ^0.41.0
     npm-run-all: ^4.1.5
     rimraf: ^4.4.1


Test patch:
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
index b3752b77e..4728b47b6 100644
Binary files a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-jupytext-linux.png 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
index 9607460ca..2df3771ba 100644
Binary files a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-new-text-notebook-jupytext-linux.png 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
index b46dc3d37..088256ba7 100644
Binary files a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-menu.spec.ts-snapshots/opened-jupytext-menu-jupytext-pair-notebook-jupytext-linux.png 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
index 5bf16f212..b0b563d8c 100644
--- 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
@@ -1,33 +1,60 @@
 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
+// Currently we are enabling only percent and MyST formats in Jupytext menu
+// So test for only these cases.
+// If we update our test env with more external kernels, we can include them
+// here in the formats
 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' },
+  {
+    createLabel: 'New Python Text Notebook with Percent Format',
+    pairLabel: 'Percent Format',
+    extension: '.py',
+  },
+  {
+    createLabel: 'New MyST Markdown Text Notebook',
+    pairLabel: 'MyST Markdown',
+    extension: '.md',
+  },
+  // {
+  //   createLabel: 'New Python Text Notebook with Light Format',
+  //   pairLabel: 'Light Format',
+  //   extension: '.py',
+  // },
+  // {
+  //   createLabel: 'New Python Text Notebook with Hydrogen Format',
+  //   pairLabel: 'Hydrogen Format',
+  //   extension: '.py',
+  // },
+  // {
+  //   createLabel: 'New Python Text Notebook with Nomarker Format',
+  //   pairLabel: 'Nomarker Format',
+  //   extension: '.py',
+  // },
+  // {
+  //   createLabel: 'New Markdown Text Notebook',
+  //   pairLabel: 'Markdown',
+  //   extension: '.md',
+  // },
 ];
 
 // Get all possible menuPaths
 const createNewMenuPaths = formats.map((format) => {
   return {
-    menuPath: `Jupytext>New Text Notebook>${format.label}`,
+    menuPath: `Jupytext>New Text Notebook>${format.createLabel}`,
     extension: format.extension,
   };
 });
 
 const pairMenuPaths = formats.map((format) => {
   return {
-    menuPath: `Jupytext>Pair Notebook>Pair Notebook with ${format.label}`,
+    menuPath: `Jupytext>Pair Notebook>Pair Notebook with ${format.pairLabel}`,
     extension: format.extension,
   };
 });
 
+// Toggle metadata
+const toggleMetadataPath = 'Jupytext>Include Metadata';
+
 // Name of notebook file
 const fileName = 'notebook.ipynb';
 
@@ -58,13 +85,15 @@ test.describe('Jupytext Create Text Notebooks from Menu Tests', () => {
 
       // Populate page
       await populateNotebook(page);
+      // Toggle Include Metadata. It is enabled by default.
+      // It is to avoid having Jupytext version in metadata in snapshot
+      // If we include it, for every version bump we need to update snapshots as
+      // version changes which will fail UI tests. Just do not include metadata
+      // which will ensure smooth version bumping
+      await page.menu.clickMenuItem(toggleMetadataPath);
       // 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');
 
@@ -86,6 +115,12 @@ test.describe('Jupytext Pair Notebooks from Menu Tests', () => {
     test(`Open menu item ${paths.menuPath}`, async ({ page }) => {
       // Click pairing command
       await page.menu.clickMenuItem(paths.menuPath);
+      // Toggle Include Metadata. It is enabled by default.
+      // It is to avoid having Jupytext version in metadata in snapshot
+      // If we include it, for every version bump we need to update snapshots as
+      // version changes which will fail UI tests. Just do not include metadata
+      // which will ensure smooth version bumping
+      await page.menu.clickMenuItem(toggleMetadataPath);
       // Wait until we save notebook. Once we save it, paired file appears
       await page.notebook.save();
       // Try to open paired file
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
deleted file mode 100644
index 02ea8dee7..000000000
Binary files 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 and /dev/null 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
deleted file mode 100644
index 264b3d4ee..000000000
Binary files 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 and /dev/null 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
deleted file mode 100644
index 2c1ad656e..000000000
Binary files a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-markdown-text-jupytext-linux.png and /dev/null 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
deleted file mode 100644
index c4ee4723c..000000000
Binary files 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 and /dev/null differ
diff --git a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-new-myst-markdown-text-notebook-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-new-myst-markdown-text-notebook-text-jupytext-linux.png
new file mode 100644
index 000000000..9f203b18a
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-new-myst-markdown-text-notebook-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-new-python-text-notebook-with-percent-format-text-jupytext-linux.png b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-new-python-text-notebook-with-percent-format-text-jupytext-linux.png
new file mode 100644
index 000000000..1a58072ea
Binary files /dev/null and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-notebook.spec.ts-snapshots/opened-jupytext-new-text-notebook-new-python-text-notebook-with-percent-format-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
deleted file mode 100644
index 3b4e95c00..000000000
Binary files 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 and /dev/null 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
deleted file mode 100644
index 000bf25dd..000000000
Binary files 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 and /dev/null 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
deleted file mode 100644
index 4c6b5eacb..000000000
Binary files 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 and /dev/null 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
deleted file mode 100644
index a99d41b88..000000000
Binary files 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 and /dev/null 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
deleted file mode 100644
index 1de955e18..000000000
Binary files 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 and /dev/null 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
index 3156a8b45..dcd146a35 100644
Binary files 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 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
deleted file mode 100644
index 9a5d75adc..000000000
Binary files 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 and /dev/null 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-format-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-format-jupytext-linux.png
new file mode 100644
index 000000000..2b1e5ad0f
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-format-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
deleted file mode 100644
index 6e0c61c4c..000000000
Binary files 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 and /dev/null differ
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
index 6c275d46c..a0bf53443 100644
Binary files a/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-panel-jupytext-linux.png and b/jupyterlab/packages/jupyterlab-jupytext-extension/ui-tests/tests/jupytext-settings.spec.ts-snapshots/jupytext-settings-panel-jupytext-linux.png differ