Skip to content

Commit

Permalink
Add support for RISE slides in JupyterLab
Browse files Browse the repository at this point in the history
  • Loading branch information
fcollonval authored and mwouts committed Sep 16, 2023
1 parent 28cc7de commit c2d6281
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 89 deletions.
38 changes: 36 additions & 2 deletions packages/labextension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,52 @@
},
"extension": true,
"schemaDir": "schema",
"outputDir": "../../jupytext/labextension"
"outputDir": "../../jupytext/labextension",
"sharedPackages": {
"jupyterlab-rise": {
"singleton": true
}
}
},
"dependencies": {
"@jupyterlab/application": "^3.0.0",
"@jupyterlab/apputils": "^3.0.0",
"@jupyterlab/codeeditor": "^3.0.0",
"@jupyterlab/nbformat": "^3.0.0",
"@jupyterlab/notebook": "^3.0.0"
"@jupyterlab/notebook": "^3.0.0",
"@jupyterlab/rendermime": "^3.0.0",
"@jupyterlab/settingregistry": "^3.0.0",
"@jupyterlab/translation": "^3.0.0",
"@jupyterlab/ui-components": "^3.0.0",
"jupyterlab-rise": "^0.3.0"
},
"devDependencies": {
"@jupyterlab/builder": "^3.0.0",
"npm-run-all": "^4.1.5",
"rimraf": "^3.0.2",
"typescript": "~4.0.3"
},
"resolutions": {
"@jupyterlab/application": "3.5.0",
"@jupyterlab/apputils": "3.5.0",
"@jupyterlab/builder": "3.5.0",
"@jupyterlab/codeeditor": "3.5.0",
"@jupyterlab/codemirror": "3.5.0",
"@jupyterlab/coreutils": "5.5.0",
"@jupyterlab/docregistry": "3.5.0",
"@jupyterlab/mainmenu": "3.5.0",
"@jupyterlab/nbformat": "3.5.0",
"@jupyterlab/notebook": "3.5.0",
"@jupyterlab/observables": "4.5.0",
"@jupyterlab/settingregistry": "3.5.0",
"@jupyterlab/services": "6.5.0",
"@jupyterlab/shared-models": "3.5.0",
"@jupyterlab/statusbar": "3.5.0",
"@jupyterlab/statedb": "3.5.0",
"@jupyterlab/rendermime": "3.5.0",
"@jupyterlab/rendermime-interfaces": "3.5.0",
"@jupyterlab/translation": "3.5.0",
"@jupyterlab/ui-components": "3.5.0",
"jupyterlab-rise": "0.3.0"
}
}
151 changes: 84 additions & 67 deletions packages/labextension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ import {

import { markdownIcon } from "@jupyterlab/ui-components";

import { IRisePreviewFactory } from 'jupyterlab-rise';


interface IJupytextFormat {
/**
* Conversion format
Expand Down Expand Up @@ -120,13 +123,6 @@ function get_jupytext_formats(notebook_tracker: INotebookTracker): Array<string>

const model = notebook_tracker.currentWidget.context.model;

// xxx not sure this is useful
// if metadata.get("jupytext") used to return something that is void
// then we're in the clear
// if (! JLAB4)
// if (! (model.metadata as any)?.has("jupytext"))
// return [];

const jupytext: IJupytextSection = (JLAB4
? (model as any).getMetadata("jupytext")
: (model.metadata as any)?.get('jupytext')) as IJupytextSection;
Expand Down Expand Up @@ -175,15 +171,14 @@ function get_selected_formats(notebook_tracker: INotebookTracker): Array<string>
formats.push(notebook_extension);
else {
let format_name = 'light';
// xxx same here, the test is probably not needed
// if (notebook_tracker.currentWidget.context.model.metadata.has("jupytext")) {

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;
// }

formats.push('auto:' + format_name);
}
return formats;
Expand All @@ -195,10 +190,7 @@ function get_selected_formats(notebook_tracker: INotebookTracker): Array<string>
const extension: JupyterFrontEndPlugin<void> = {
id: "jupyterlab-jupytext",
autoStart: true,
// IEditorTracker and IMarkdownViewerTracker are optionally requested only
// to ensure this is called after they are activated and we properly overwrite
// the default factory for non-notebook file format
optional: [ITranslator, ICommandPalette],
optional: [ITranslator, ICommandPalette, IRisePreviewFactory],
requires: [
NotebookPanel.IContentFactory,
IEditorServices,
Expand All @@ -207,10 +199,7 @@ const extension: JupyterFrontEndPlugin<void> = {
INotebookWidgetFactory,
INotebookTracker,
ISettingRegistry,
IToolbarWidgetRegistry,
ICommandPalette,
ITranslator,

IToolbarWidgetRegistry
],
activate: (
app: JupyterFrontEnd,
Expand All @@ -220,10 +209,11 @@ const extension: JupyterFrontEndPlugin<void> = {
sessionContextDialogs: ISessionContextDialogs,
notebookFactory: NotebookWidgetFactory.IFactory,
notebookTracker: INotebookTracker,
settingRegistry: ISettingRegistry | null,
settingRegistry: ISettingRegistry,
toolbarRegistry: IToolbarWidgetRegistry,
palette: ICommandPalette | 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
Expand All @@ -233,7 +223,7 @@ const extension: JupyterFrontEndPlugin<void> = {
if (app.name == "JupyterLab") {
const app_numbers = app.version.match(/[0-9]+/);
if (app_numbers) {
JLAB4 = parseInt(app_numbers[0]) >= 4;
JLAB4 = parseInt(app_numbers[0], 10) >= 4;
}
}
console.log("JupyterLab extension jupytext is activating...");
Expand Down Expand Up @@ -280,10 +270,10 @@ const extension: JupyterFrontEndPlugin<void> = {
if ( notebookTracker.currentWidget === null)
return;
const model = notebookTracker.currentWidget.context.model;
const jupytext: IJupytextSection = (JLAB4
let jupytext: IJupytextSection = (JLAB4
? (model as any).getMetadata('jupytext')
: (model.metadata as any)?.get('jupytext')
) as IJupytextSection;
) as IJupytextSection | undefined;
let formats: Array<string> = get_selected_formats(notebookTracker);

// Toggle the selected format
Expand Down Expand Up @@ -344,7 +334,7 @@ const extension: JupyterFrontEndPlugin<void> = {
if (formats.length === 1) {
if (notebook_extension !== 'auto')
formats = [];
else if (jupytext && jupytext.text_representation) {
else if (jupytext?.text_representation) {
const format_name = formats[0].split(':')[1];
jupytext.text_representation.format_name = format_name;
formats = [];
Expand All @@ -359,24 +349,25 @@ const extension: JupyterFrontEndPlugin<void> = {
if (jupytext.formats) {
delete jupytext.formats;
}

if (Object.keys(jupytext).length == 0) {
const model = notebookTracker.currentWidget.context.model;
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 {
const model = notebookTracker.currentWidget.context.model;
JLAB4
? (model as any).setMetadata("jupytext", { formats: formats.join() })
: (model.metadata as any)?.set( { formats: formats.join() });
}
jupytext = { formats: formats.join() }
if(!JLAB4)
(model.metadata as any)?.set("jupytext", jupytext);
}
if (JLAB4)
(model as any).setMetadata('jupytext', jupytext);
}
});

Expand Down Expand Up @@ -457,20 +448,21 @@ const extension: JupyterFrontEndPlugin<void> = {
? (model as any).getMetadata("jupytext")
: (model.metadata as any)?.get("jupytext")
if (!jupytext_metadata)
return false;
return;

const jupytext: IJupytextSection = (jupytext_metadata as unknown) as IJupytextSection;
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;
return
} else {
jupytext.notebook_metadata_filter = '-all';
if (jupytext.cell_metadata_filter === undefined)
jupytext.cell_metadata_filter = '-all';
}

jupytext.notebook_metadata_filter = '-all'
if (jupytext.cell_metadata_filter === undefined)
jupytext.cell_metadata_filter = '-all';
if (JLAB4)
(model as any).setMetadata('jupytext', jupytext);
}
});

Expand All @@ -481,27 +473,42 @@ const extension: JupyterFrontEndPlugin<void> = {
});

// Define file types
app.docRegistry.addFileType({
name: "myst",
displayName: trans.__("MyST Markdown Notebook"),
extensions: [".myst", ".mystnb", ".mnb"],
icon: markdownIcon
});

app.docRegistry.addFileType({
name: "r-markdown",
displayName: trans.__("R Markdown Notebook"),
// Extension file are transformed to lower case...
extensions: [".rmd"],
icon: markdownIcon
});

app.docRegistry.addFileType({
name: "quarto",
displayName: trans.__("Quarto Notebook"),
extensions: [".qmd"],
icon: markdownIcon
});
app.docRegistry.addFileType(
{
name: "myst",
displayName: trans.__("MyST Markdown Notebook"),
extensions: [".myst", ".mystnb", ".mnb"],
icon: markdownIcon
},
[
'Notebook'
]
);

app.docRegistry.addFileType(
{
name: "r-markdown",
displayName: trans.__("R Markdown Notebook"),
// Extension file are transformed to lower case...
extensions: [".rmd"],
icon: markdownIcon
},
[
'Notebook'
]
);

app.docRegistry.addFileType(
{
name: "quarto",
displayName: trans.__("Quarto Notebook"),
extensions: [".qmd"],
icon: markdownIcon
},
[
'Notebook'
]
);

// the way to create the toolbar factory is different in JupyterLab 3 and 4
let toolbarFactory
Expand All @@ -527,7 +534,8 @@ const extension: JupyterFrontEndPlugin<void> = {
// 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"), // mandatory in jlab4 (not in jlab3)
// @ts-expect-error Available in jlab4+
label: trans.__("Jupytext Notebook"),
fileTypes: ["markdown", "myst", "r-markdown", "quarto", "julia", "python", "r"],
modelName: notebookFactory.modelName ?? "notebook",
preferKernel: notebookFactory.preferKernel ?? true,
Expand All @@ -537,10 +545,10 @@ const extension: JupyterFrontEndPlugin<void> = {
editorConfig: notebookFactory.editorConfig,
notebookConfig: notebookFactory.notebookConfig,
mimeTypeService: editorServices.mimeTypeService,
// sessionDialogs: sessionContextDialogs,
sessionDialogs: sessionContextDialogs,
toolbarFactory: toolbarFactory,
// translator?: ITranslator,
} as NotebookWidgetFactory.IOptions<NotebookPanel>);
translator,
});
app.docRegistry.addWidgetFactory(factory);

// Register widget created with the new factory in the notebook tracker
Expand All @@ -559,15 +567,24 @@ const extension: JupyterFrontEndPlugin<void> = {

// Notify the widget tracker if restore data needs to update.
widget.context.pathChanged.connect(() => {
// Trick using private API
// @ts-ignore
// @ts-expect-error Trick using private API
void notebookTracker.save(widget);
});
// Add the notebook panel to the tracker.
// Trick using private API
// @ts-ignore
// @ts-expect-error Trick using private API
void notebookTracker.add(widget);
});

// 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');
}
},
};

Expand Down
Loading

0 comments on commit c2d6281

Please sign in to comment.