Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for RISE slides in JupyterLab #1126

Merged
merged 1 commit into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading