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

New approach for JupyterLab 3 #605

Closed
wants to merge 49 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0637903
First stand-alone app
fcollonval Nov 12, 2021
b9151e4
Lab like app
fcollonval Nov 15, 2021
bdb826e
Be sure Reveal layout takes over
fcollonval Nov 15, 2021
ecedeb9
Pass notebook path as part of URL
fcollonval Nov 15, 2021
a152583
Fix loading included files
fcollonval Nov 15, 2021
94069ca
Add widgets test case
fcollonval Nov 15, 2021
06177d7
Reduce mandatory plugin list
fcollonval Nov 15, 2021
d792bc5
Add JupyterLab extension
fcollonval Nov 15, 2021
643638b
Fix webpack shared scope
fcollonval Nov 15, 2021
4361160
Rename classic to nbextension
fcollonval Nov 15, 2021
cd75d28
Restructure the repo as one python pkg
fcollonval Nov 15, 2021
c5a4a45
Remove symlink
fcollonval Nov 15, 2021
ee6dea9
Keep License and Readme as symlink
fcollonval Nov 15, 2021
62e1e6a
Improve binder environment
fcollonval Nov 15, 2021
d9b9cd8
Lint code
fcollonval Nov 15, 2021
646b7e3
Fix python sdist bdist
fcollonval Nov 15, 2021
77d9cde
Pin package to JLab 3.0
fcollonval Nov 15, 2021
42a9d9a
Fix for binder
fcollonval Nov 16, 2021
9bd084b
Add folder structure description
fcollonval Nov 17, 2021
3413608
Correct git pre-commit hook
fcollonval Nov 20, 2021
f564c90
Lint code
fcollonval Nov 20, 2021
4091fec
Upgrade yarn.lock
fcollonval Nov 20, 2021
105449d
- Remove toolbar
fcollonval Nov 20, 2021
5203b1f
Preview side-by-side in JLab
fcollonval Nov 20, 2021
fb8048e
Fix `watch` script
fcollonval Nov 21, 2021
c53a797
Add note about `watch` script and update folder structure
fcollonval Nov 21, 2021
823c24f
develop doc: remove obsolete intro, and reformat
parmentelat Nov 20, 2021
a95f20d
a test notebook
parmentelat Nov 20, 2021
b21f231
an attempt at re-implementing the logic for all 5 slide types
parmentelat Nov 21, 2021
acae3fd
Merge pull request #1 from parmentelat/ft/jlab3
fcollonval Nov 22, 2021
d51d867
Multiple enhancements
fcollonval Nov 26, 2021
7ee2bd1
Chalkboard and notes are working
fcollonval Nov 26, 2021
e46ba5c
Propagate activeCell from JLab to Rise
fcollonval Nov 26, 2021
d18dcd9
Add support for autolaunch
fcollonval Nov 26, 2021
efc3f5c
Fix open in new tab in preview
fcollonval Nov 26, 2021
c35a620
Fix yarn install and building steps, otherwise they failed when you s…
damianavila Nov 28, 2021
73f8937
Update icon
fcollonval Nov 30, 2021
03f6228
Fix fullscreen
fcollonval Nov 30, 2021
c95a034
Remove some ugly styles
fcollonval Nov 30, 2021
fd518eb
Support auto fullscreen
fcollonval Dec 10, 2021
8f5be28
Load colocalized CSS style sheet
fcollonval Dec 10, 2021
301c815
Load collocated `rise.css`
fcollonval Dec 10, 2021
78956a2
add mention to pip install -e . in the dev doc
parmentelat Dec 11, 2021
6845408
tweak dev doc again
parmentelat Dec 13, 2021
c6c2f41
add more samples of all the title levels
parmentelat Dec 13, 2021
e774243
a very rough attempt at fixing font sizes
parmentelat Dec 13, 2021
03071d4
fixing the size of <li> elements (#4)
parmentelat Jun 30, 2022
30554a9
fix speaker notes (#5)
YiqinZhang Aug 5, 2022
023fd81
Fix toggle all RISE buttons on slides (#6)
YiqinZhang Sep 20, 2022
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
Prev Previous commit
Next Next commit
Support auto fullscreen
  • Loading branch information
fcollonval committed Dec 10, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit fd518eba4ff7fe29f7ee50f47442be32f7783ff0
13 changes: 9 additions & 4 deletions packages/application/src/plugins/rise.ts
Original file line number Diff line number Diff line change
@@ -69,6 +69,7 @@ export const plugin: JupyterFrontEndPlugin<void> = {

// Remove active cell from argument
url.searchParams.delete('activeCellIndex');
url.searchParams.delete('fullscreen');
window.history.pushState(null, '', url.toString());

Promise.all([
@@ -122,7 +123,13 @@ export const plugin: JupyterFrontEndPlugin<void> = {

// We wait for the notebook to be loaded to get the settings from the metadata.
Rise.loadConfiguration(settings, notebookPanel.model);
Rise.revealMode(notebookPanel, app.commands, trans);
Rise.revealMode(notebookPanel, app.commands, trans)
.catch(reason => {
console.error(
'Fail to update the notebook with Reveal.JS.',
reason
);
});

Signal.disconnectAll(this);
}
@@ -886,9 +893,7 @@ namespace Rise {
// Attempt to load css with the same path as notebook
document.head.insertAdjacentHTML(
'beforeend',
`<link rel="stylesheet" href="${PageConfig.getOption(
'fullStaticUrl'
)}/${name_css}" id="rise-notebook-css" />`
`<link rel="stylesheet" href="${PageConfig.getBaseUrl()}/${name_css}" id="rise-notebook-css" />`
);

// Asynchronously import reveal
4 changes: 2 additions & 2 deletions packages/lab/schema/plugin.json
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
"command": "RISE:preview"
},
{
"command": "RISE:slideshow"
"command": "RISE:fullscreen-plugin"
},
{ "type": "separator" }
]
@@ -21,7 +21,7 @@
},
"jupyter.lab.shortcuts": [
{
"command": "RISE:slideshow",
"command": "RISE:preview",
"keys": ["Alt R"],
"selector": ".jp-Notebook:focus"
},
5 changes: 5 additions & 0 deletions packages/lab/src/icons.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { LabIcon } from '@jupyterlab/ui-components';

import RISESvg from '../style/slideshow.svg';
import fullScreenSvg from '../style/fullscreen.svg';

export const RISEIcon = new LabIcon({ name: 'RISE', svgstr: RISESvg });
export const fullScreenIcon = new LabIcon({
name: 'RISE:fullScreen',
svgstr: fullScreenSvg
});
114 changes: 88 additions & 26 deletions packages/lab/src/index.ts
Original file line number Diff line number Diff line change
@@ -7,10 +7,11 @@ import {
import {
CommandToolbarButton,
ICommandPalette,
showDialog,
WidgetTracker
} from '@jupyterlab/apputils';

import { IChangedArgs, PageConfig, URLExt } from '@jupyterlab/coreutils';
import { PageConfig, URLExt } from '@jupyterlab/coreutils';

import { DocumentRegistry } from '@jupyterlab/docregistry';

@@ -25,9 +26,11 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry';

import { ITranslator } from '@jupyterlab/translation';

import { toArray } from '@lumino/algorithm';

import { ReadonlyPartialJSONObject } from '@lumino/coreutils';

import { RISEIcon } from './icons';
import { fullScreenIcon, RISEIcon } from './icons';

import {
RisePreview,
@@ -45,6 +48,7 @@ namespace CommandIDs {
* Open the current notebook in a new browser tab
*/
export const openRise = 'RISE:slideshow';
export const riseFullScreen = 'RISE:fullscreen-plugin';
/**
* Open the current notebook in a IFrame within JupyterLab
*/
@@ -104,6 +108,7 @@ const plugin: JupyterFrontEndPlugin<IRisePreviewTracker> = {

if (restorer) {
restorer.restore(tracker, {
// Need to modify to handle auto full screen
command: 'docmanager:open',
args: panel => ({
path: panel.context.path,
@@ -157,7 +162,6 @@ const plugin: JupyterFrontEndPlugin<IRisePreviewTracker> = {
caption: trans.__(
'Open the current notebook in a new browser tab as an RevealJS slideshow.'
),
icon: RISEIcon,
execute: async () => {
const current = notebookTracker.currentWidget;
if (!current) {
@@ -203,11 +207,76 @@ const plugin: JupyterFrontEndPlugin<IRisePreviewTracker> = {
widget.disposed.connect(() => {
current.content.activeCellChanged.disconnect(updateActiveIndex);
});

if (args['fullscreen'] === true) {
widget.ready
.then(() => {
showDialog({
title: trans.__('Switch to full screen'),
body: trans.__(
'The slideshow is set to automatically open in full screen. Your web browser requires your confirmation to do so.'
)
}).then(result => {
if (result.button.accept) {
commands.execute(CommandIDs.riseFullScreen, {
id: widget.id
});
}
});
})
.catch(reason => {
console.log(reason);
});
}
}
},
isEnabled
});

commands.addCommand(CommandIDs.riseFullScreen, {
label: trans.__('Full screen slideshow'),
caption: trans.__('Toggle full screen the current active slideshow'),
icon: fullScreenIcon,
execute: async args => {
if (args['id']) {
app.shell.activateById(args['id'] as string);
}
const current = args['id']
? toArray(app.shell.widgets('main')).find(
widget => widget.id === args['id']
)
: app.shell.currentWidget;
if (current && tracker.has(current)) {
const iframe = (current as RisePreview).content.node.querySelector(
'iframe'
);
if (iframe) {
if (
!document.fullscreenElement &&
!iframe.contentDocument?.fullscreenElement
) {
const goFullScreen = () => {
iframe?.contentWindow?.document
.querySelector('div.reveal')
?.requestFullscreen();
};
if (iframe.contentDocument?.readyState === 'complete') {
goFullScreen();
} else {
iframe.contentWindow?.addEventListener('load', goFullScreen);
}
} else {
if (document.exitFullscreen) {
await document.exitFullscreen();
}
}
}
}
},
isEnabled: () =>
!!app.shell.currentWidget && tracker.has(app.shell.currentWidget)
});

commands.addCommand(CommandIDs.riseSetSlideType, {
label: args => trans.__('Toggle slideshow %1 type', args['type']),
caption: args =>
@@ -275,31 +344,24 @@ const plugin: JupyterFrontEndPlugin<IRisePreviewTracker> = {
})
);

const isNotebookModelReady = (
_: any,
change: IChangedArgs<any, any, string>
) => {
if (change.name === 'dirty' && change.newValue === false) {
panel.model?.stateChanged.disconnect(isNotebookModelReady);

let autolaunch: boolean =
// @ts-expect-error Unknown type
(panel.content.model?.metadata.get('rise') ?? {})['autolaunch'] ??
false;
if (settings) {
// @ts-expect-error unknown type
autolaunch |= settings.get('autolaunch').composite;
}

if (autolaunch) {
commands.execute(CommandIDs.risePreview);
}
}
};

// Don't trigger auto launch in stand-alone Rise application.
if (app.name !== 'Rise') {
panel.model?.stateChanged.connect(isNotebookModelReady);
await panel.context.ready;

let autolaunch: boolean =
// @ts-expect-error Unknown type
(panel.content.model?.metadata.get('rise') ?? {})['autolaunch'] ??
false;
if (settings) {
// @ts-expect-error unknown type
autolaunch |= settings.get('autolaunch').composite;
}

if (autolaunch) {
await commands.execute(CommandIDs.risePreview, {
fullscreen: true
});
}
}
}
);
63 changes: 50 additions & 13 deletions packages/lab/src/preview.ts
Original file line number Diff line number Diff line change
@@ -13,19 +13,21 @@ import {

import { INotebookModel } from '@jupyterlab/notebook';

import { ITranslator, nullTranslator } from '@jupyterlab/translation';

import { refreshIcon } from '@jupyterlab/ui-components';

import { CommandRegistry } from '@lumino/commands';

import { Token } from '@lumino/coreutils';
import { PromiseDelegate, Token } from '@lumino/coreutils';

import { Message } from '@lumino/messaging';

import { Signal } from '@lumino/signaling';

import { Widget } from '@lumino/widgets';

import { RISEIcon } from './icons';
import { fullScreenIcon, RISEIcon } from './icons';

/**
* A class that tracks Rise Preview widgets.
@@ -62,10 +64,17 @@ export class RisePreview extends DocumentWidget<IFrame, INotebookModel> {
})
});

const { getRiseUrl, context, renderOnSave } = options;
this._ready = new PromiseDelegate<void>();
// `setActiveCellIndex` needs to be called at least once.
// after instantiation.
this._ready.reject('Not loading at instantiation.');

const { getRiseUrl, context, renderOnSave, translator } = options;
this.getRiseUrl = getRiseUrl;
this._path = context.path;

const trans = (translator ?? nullTranslator).load('rise');

this.content.title.icon = RISEIcon;

this._renderOnSave = renderOnSave ?? false;
@@ -76,7 +85,7 @@ export class RisePreview extends DocumentWidget<IFrame, INotebookModel> {

const reloadButton = new ToolbarButton({
icon: refreshIcon,
tooltip: 'Reload Preview',
tooltip: trans.__('Reload Preview'),
onClick: () => {
this.reload();
}
@@ -86,16 +95,17 @@ export class RisePreview extends DocumentWidget<IFrame, INotebookModel> {
checked: this._renderOnSave,
onChange: (event: Event) => {
this._renderOnSave = (event.target as any)?.checked ?? false;
}
},
translator
});

this.toolbar.addItem(
'open',
'fullscreen',
new ToolbarButton({
icon: RISEIcon,
tooltip: 'Open in a new browser tab',
icon: fullScreenIcon,
tooltip: trans.__('Open the slideshow in full screen'),
onClick: () => {
options.commands.execute('RISE:slideshow');
options.commands.execute('RISE:fullscreen-plugin');
}
})
);
@@ -116,6 +126,13 @@ export class RisePreview extends DocumentWidget<IFrame, INotebookModel> {
this.toolbar.addItem('reload', reloadButton);
}

/**
* Promise that resolves when the slideshow is ready
*/
get ready(): Promise<void> {
return this._ready.promise;
}

/**
* Dispose the preview widget.
*/
@@ -145,10 +162,22 @@ export class RisePreview extends DocumentWidget<IFrame, INotebookModel> {
}

setActiveCellIndex(index: number, reload = true): void {
const iframe = this.content.node.querySelector('iframe')!;
if (reload) {
this._ready = new PromiseDelegate<void>();
const setReady = () => {
iframe.contentWindow!.removeEventListener('load', setReady);
const waitForReveal = setInterval(() => {
if (iframe.contentDocument!.querySelector('.reveal')) {
clearInterval(waitForReveal);
this._ready.resolve();
}
}, 500);
};

this.content.url = this.getRiseUrl(this.path, index);
iframe.contentWindow!.addEventListener('load', setReady);
} else {
const iframe = this.content.node.querySelector('iframe')!;
if (iframe.contentWindow) {
iframe.contentWindow.history.pushState(
null,
@@ -169,8 +198,9 @@ export class RisePreview extends DocumentWidget<IFrame, INotebookModel> {
}
}

private _renderOnSave: boolean;
protected getRiseUrl: (path: string, index?: number) => string;
private _ready: PromiseDelegate<void>;
private _renderOnSave: boolean;
private _path: string;
}

@@ -190,7 +220,7 @@ export namespace RisePreview {
/**
* The Rise URL function.
*/
getRiseUrl: (path: string) => string;
getRiseUrl: (path: string, index?: number) => string;

/**
* Whether to reload the preview on context saved.
@@ -242,6 +272,10 @@ namespace Private {
* Callback on checked status changes
*/
onChange?: (ev: Event) => void;
/**
* Translator
*/
translator?: ITranslator;
}
}

@@ -250,10 +284,13 @@ namespace Private {
*/
export class CheckBox extends Widget {
constructor(options: CheckBox.IOptions = {}) {
const trans = (options.translator ?? nullTranslator).load('rise');
const node = document.createElement('label');
node.insertAdjacentHTML(
'afterbegin',
'<input name="renderOnSave" type="checkbox"></input>Render on Save'
`<input name="renderOnSave" type="checkbox"></input>${trans.__(
'Render on Save'
)}`
Comment on lines +291 to +293

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest adding a class name + style to move the checkbox a little bit down (e.g. top: 4px; position: relative;) as it is currently out of line:

Screenshot from 2022-06-21 21-07-11

);
super({ node });
this.input = node.childNodes.item(0) as HTMLInputElement;
Loading
Oops, something went wrong.