Skip to content

Commit 3d6e124

Browse files
authored
Finalize simple notebook (#353)
* Finalize simple notebook * More robust keyboard shortcut capture --------- Co-authored-by: Frédéric Collonval <[email protected]>
1 parent d89a2da commit 3d6e124

File tree

2 files changed

+152
-93
lines changed

2 files changed

+152
-93
lines changed

packages/react/src/components/notebook/BaseNotebook.tsx

Lines changed: 71 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
import {
4444
RenderMimeRegistry,
4545
standardRendererFactories,
46+
type IRenderMime,
4647
} from '@jupyterlab/rendermime';
4748
import type {
4849
Contents,
@@ -72,7 +73,7 @@ import {
7273
WidgetManager,
7374
} from '../../jupyter';
7475
import type { OnSessionConnection } from '../../state';
75-
import { newUuid, sleep } from '../../utils';
76+
import { newUuid } from '../../utils';
7677
import { Lumino } from '../lumino';
7778
import { Loader } from '../utils';
7879
import type { DatalayerNotebookExtension } from './Notebook';
@@ -105,6 +106,10 @@ export interface IBaseNotebookProps {
105106
* Kernel ID to connect to
106107
*/
107108
kernelId?: string;
109+
/**
110+
* Additional cell output renderers.
111+
*/
112+
renderers?: IRenderMime.IRendererFactory[];
108113
/**
109114
* Service manager
110115
*/
@@ -140,6 +145,7 @@ export function BaseNotebook(props: IBaseNotebookProps): JSX.Element {
140145
commands,
141146
extensions = DEFAULT_EXTENSIONS,
142147
kernelId,
148+
renderers,
143149
serviceManager,
144150
model,
145151
onSessionConnectionChanged,
@@ -156,7 +162,10 @@ export function BaseNotebook(props: IBaseNotebookProps): JSX.Element {
156162
[props.path]
157163
);
158164
// Features
159-
const features = useMemo(() => new CommonFeatures({ commands }), [commands]);
165+
const features = useMemo(
166+
() => new CommonFeatures({ commands, renderers }),
167+
[commands, renderers]
168+
);
160169

161170
// Content factory
162171
const contentFactory = useMemo(
@@ -458,15 +467,20 @@ export function BaseNotebook(props: IBaseNotebookProps): JSX.Element {
458467
};
459468
}, [completer, panel, tracker]);
460469

461-
const onKeyDown = useCallback(
462-
(event: any) => {
470+
useEffect(() => {
471+
const onKeyDown = (event: any) => {
463472
features.commands.processKeydownEvent(event);
464-
},
465-
[features.commands]
466-
);
473+
};
467474

468-
// FIXME
469-
// - connect signals - see adapter - fix kernel transfer
475+
// FIXME It would be better to add the listener to the Box wrapping the panel
476+
// but this requires the use of the latest version of JupyterLab/Lumino that
477+
// capture event at bubbling phase for keyboard shortcuts rather than at capture phase.
478+
document.addEventListener('keydown', onKeyDown, true);
479+
480+
return () => {
481+
document.removeEventListener('keydown', onKeyDown, true);
482+
};
483+
}, [features.commands]);
470484

471485
return (
472486
<>
@@ -478,7 +492,7 @@ export function BaseNotebook(props: IBaseNotebookProps): JSX.Element {
478492
{isLoading ? (
479493
<Loader key="notebook-loader" />
480494
) : panel ? (
481-
<Box sx={{ height: '100%' }} onKeyDownCapture={onKeyDown}>
495+
<Box sx={{ height: '100%' }}>
482496
<Lumino id={id} key="notebook-container">
483497
{panel}
484498
</Lumino>
@@ -489,6 +503,7 @@ export function BaseNotebook(props: IBaseNotebookProps): JSX.Element {
489503
variant="critical"
490504
description="Unable to create the notebook view."
491505
hideTitle={true}
506+
title="Error"
492507
/>
493508
)}
494509
</>
@@ -679,18 +694,15 @@ export function useNotebookModel(options: {
679694

680695
// If sessionId has expired - reset the client model
681696
if (event.code === 4002) {
697+
disposable.clear();
682698
provider?.destroy();
683-
ready.reject('Connection closed.');
684-
ready = new PromiseDelegate();
685699
if (isMounted) {
686-
Promise.all([connect(), ready.promise, sleep(500)]).catch(
687-
error => {
688-
console.error(
689-
'Failed to setup collaboration connection.',
690-
error
691-
);
692-
}
693-
);
700+
connect().catch(error => {
701+
console.error(
702+
'Failed to setup collaboration connection.',
703+
error
704+
);
705+
});
694706
}
695707
}
696708

@@ -706,6 +718,9 @@ export function useNotebookModel(options: {
706718
};
707719

708720
const connect = async () => {
721+
ready.reject('Connection closed.');
722+
ready = new PromiseDelegate();
723+
709724
sharedModel = new YNotebook();
710725
const { ydoc, awareness } = sharedModel;
711726
let roomURL = '';
@@ -752,40 +767,35 @@ export function useNotebookModel(options: {
752767
provider.on('sync', onSync);
753768
provider.on('connection-close', onConnectionClose);
754769
console.log('Collaboration is setup with websocket provider.');
755-
// Create a new model using the one synchronize with the collaboration room
756-
const model = new NotebookModel({
757-
collaborationEnabled: true,
758-
disableDocumentWideUndoRedo: true,
759-
sharedModel,
760-
});
761-
model.readOnly = readonly;
762-
setModel(model);
770+
771+
await ready.promise;
772+
const dispose = () => {
773+
provider?.off('sync', onSync);
774+
provider?.off('connection-close', onConnectionClose);
775+
provider?.disconnect();
776+
provider?.destroy();
777+
};
778+
779+
if (isMounted) {
780+
// Create a new model using the one synchronized with the collaboration room
781+
const model = new NotebookModel({
782+
collaborationEnabled: true,
783+
disableDocumentWideUndoRedo: true,
784+
sharedModel,
785+
});
786+
model.readOnly = readonly;
787+
setModel(model);
788+
789+
disposable.add(Object.freeze({ dispose, isDisposed: false }));
790+
} else {
791+
dispose();
792+
}
763793
}
764794
};
765795

766-
Promise.all([connect(), ready.promise])
767-
.then(() => {
768-
if (provider) {
769-
const dispose = () => {
770-
(provider!.synced ? Promise.resolve() : ready.promise).finally(
771-
() => {
772-
provider!.off('sync', onSync);
773-
provider!.off('connection-close', onConnectionClose);
774-
provider!.disconnect();
775-
provider!.destroy();
776-
}
777-
);
778-
};
779-
if (isMounted) {
780-
disposable.add(Object.freeze({ dispose, isDisposed: false }));
781-
} else {
782-
dispose();
783-
}
784-
}
785-
})
786-
.catch(error => {
787-
console.error('Failed to setup collaboration connection.', error);
788-
});
796+
connect().catch(error => {
797+
console.error('Failed to setup collaboration connection.', error);
798+
});
789799
} else {
790800
const createModel = (nbformat: INotebookContent | undefined) => {
791801
const model = new NotebookModel();
@@ -837,7 +847,12 @@ class CommonFeatures {
837847
protected _editorServices: IEditorServices;
838848
protected _rendermime: RenderMimeRegistry;
839849

840-
constructor(options: { commands?: CommandRegistry } = {}) {
850+
constructor(
851+
options: {
852+
commands?: CommandRegistry;
853+
renderers?: IRenderMime.IRendererFactory[];
854+
} = {}
855+
) {
841856
this._commands = options.commands ?? new CommandRegistry();
842857

843858
const languages = new EditorLanguageRegistry();
@@ -858,11 +873,10 @@ class CommonFeatures {
858873
},
859874
});
860875

861-
const initialFactories = standardRendererFactories.filter(
862-
factory => factory.mimeTypes[0] !== 'text/javascript'
863-
);
864-
initialFactories.push(jsonRendererFactory);
865-
initialFactories.push(javascriptRendererFactory);
876+
const initialFactories = standardRendererFactories
877+
.filter(factory => factory.mimeTypes[0] !== 'text/javascript')
878+
.concat([jsonRendererFactory, javascriptRendererFactory])
879+
.concat(options.renderers ?? []);
866880

867881
this._rendermime = new RenderMimeRegistry({
868882
initialFactories,
@@ -1082,17 +1096,6 @@ function initializeContext(
10821096
);
10831097
context.sessionContext.ready.then(() => {
10841098
onSessionConnection?.(context?.sessionContext.session ?? undefined);
1085-
// FIXME
1086-
// const kernelConnection = context?.sessionContext.session?.kernel;
1087-
// if (this._kernelTransfer) {
1088-
// if (kernelConnection) {
1089-
// kernelConnection.connectionStatusChanged.connect((_, status) => {
1090-
// if (status === 'connected') {
1091-
// this._kernelTransfer!.transfer(kernelConnection);
1092-
// }
1093-
// });
1094-
// }
1095-
// }
10961099
});
10971100

10981101
// Initialize the context

0 commit comments

Comments
 (0)