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

Support server-side execution #10

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ export * from './notebookrenderer';
export * from './notebookrenderer/types';

import { notebookRenderer, yWidgetManager } from './notebookrenderer';
import { yOutputHandler } from './youtputhandler';
import { yInputWidget } from './yinputwidget';
import { yStdoutOutputWidget } from './youtputwidget';

export default [notebookRenderer, yWidgetManager];
export default [notebookRenderer, yWidgetManager, yOutputHandler, yStdoutOutputWidget, yInputWidget];
27 changes: 14 additions & 13 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import * as Y from 'yjs';
import { IJupyterYDoc, IJupyterYModel } from './types';

export class JupyterYModel implements IJupyterYModel {
constructor(commMetadata: {[key: string]: any}) {
this._yModelName = commMetadata.ymodel_name;
const ydoc = this.ydocFactory(commMetadata);
this._sharedModel = new JupyterYDoc(commMetadata, ydoc);
constructor(options: {[key: string]: any}) {
this._yModelName = options.ymodel_name;
const ydoc = this.ydocFactory(options);
this._sharedModel = new JupyterYDoc(options, ydoc);
this.roomId = options.room_id;
}

get yModelName(): string {
Expand All @@ -32,7 +33,7 @@ export class JupyterYModel implements IJupyterYModel {
return this._isDisposed;
}

ydocFactory(commMetadata: {[key: string]: any}): Y.Doc {
ydocFactory(options: {[key: string]: any}): Y.Doc {
return new Y.Doc();
}

Expand All @@ -56,24 +57,24 @@ export class JupyterYModel implements IJupyterYModel {

private _yModelName: string;
private _sharedModel: IJupyterYDoc;

private _isDisposed = false;

private _disposed = new Signal<this, void>(this);

roomId?: string;
}

export class JupyterYDoc implements IJupyterYDoc {
constructor(commMetadata: {[key: string]: any}, ydoc: Y.Doc) {
this._commMetadata = commMetadata;
constructor(options: {[key: string]: any}, ydoc: Y.Doc) {
this._options = options;
this._ydoc = ydoc;
if (commMetadata.create_ydoc) {
if (options.create_ydoc) {
this._attrs = this._ydoc.getMap<string>('_attrs');
this._attrs.observe(this._attrsObserver);
}
}

get commMetadata(): {[key: string]: any} {
return this._commMetadata;
get options(): {[key: string]: any} {
return this._options;
}

get ydoc(): Y.Doc {
Expand Down Expand Up @@ -130,5 +131,5 @@ export class JupyterYDoc implements IJupyterYDoc {

private _disposed = new Signal<this, void>(this);
private _ydoc: Y.Doc;
private _commMetadata: {[key: string]: any};
private _options: {[key: string]: any};
}
2 changes: 1 addition & 1 deletion src/notebookrenderer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const notebookRenderer: JupyterFrontEndPlugin<void> = {
nbTracker.currentWidget?.sessionContext.session?.kernel?.id;
const mimeType = options.mimeType;
const modelFactory = new NotebookRendererModel({
kernelId,
kernelOrNotebookId: kernelId,
widgetManager: wmManager
});
return new JupyterYWidget({ mimeType, modelFactory });
Expand Down
18 changes: 9 additions & 9 deletions src/notebookrenderer/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IJupyterYWidgetManager } from './types';
export class NotebookRendererModel implements IDisposable {
constructor(options: NotebookRendererModel.IOptions) {
this._widgetManager = options.widgetManager;
this._kernelId = options.kernelId;
this._kernelOrNotebookId = options.kernelOrNotebookId;
}

get isDisposed(): boolean {
Expand All @@ -20,15 +20,15 @@ export class NotebookRendererModel implements IDisposable {
this._isDisposed = true;
}

getYModel(commId: string): IJupyterYModel | undefined {
if (this._kernelId) {
return this._widgetManager.getWidgetModel(this._kernelId, commId);
getYModel(commOrRoomId: string): IJupyterYModel | undefined {
if (this._kernelOrNotebookId) {
return this._widgetManager.getWidgetModel(this._kernelOrNotebookId, commOrRoomId);
}
}

createYWidget(commId: string, node: HTMLElement): void {
if (this._kernelId) {
const yModel = this._widgetManager.getWidgetModel(this._kernelId, commId);
createYWidget(commOrRoomId: string, node: HTMLElement): void {
if (this._kernelOrNotebookId) {
const yModel = this._widgetManager.getWidgetModel(this._kernelOrNotebookId, commOrRoomId);
if (yModel) {
const widgetFactory = this._widgetManager.getWidgetFactory(
yModel.yModelName
Expand All @@ -39,13 +39,13 @@ export class NotebookRendererModel implements IDisposable {
}

private _isDisposed = false;
private _kernelId?: string;
private _kernelOrNotebookId?: string;
private _widgetManager: IJupyterYWidgetManager;
}

export namespace NotebookRendererModel {
export interface IOptions {
kernelId?: string;
kernelOrNotebookId?: string;
widgetManager: IJupyterYWidgetManager;
}
}
3 changes: 3 additions & 0 deletions src/notebookrenderer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IJupyterYModel } from '../types';

export interface IJupyterYWidgetModelRegistry {
getModel(id: string): IJupyterYModel | undefined;
setModel(id: string, model: IJupyterYModel): void;
}

export interface IJupyterYModelFactory {
Expand All @@ -15,6 +16,7 @@ export interface IJupyterYWidgetFactory {
}

export interface IJupyterYWidgetManager {
registerNotebook(notebookId: string): IJupyterYWidgetModelRegistry;
registerKernel(kernel: Kernel.IKernelConnection): void;
registerWidget(
name: string,
Expand All @@ -23,6 +25,7 @@ export interface IJupyterYWidgetManager {
): void;
getWidgetModel(kernelId: string, commId: string): IJupyterYModel | undefined;
getWidgetFactory(modelName: string): any | undefined;
yModelFactories: Map<string, IJupyterYModelFactory>;
}

export const IJupyterYWidgetManager = new Token<IJupyterYWidgetManager>(
Expand Down
9 changes: 9 additions & 0 deletions src/notebookrenderer/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class JupyterYWidget extends Widget implements IRenderMime.IRenderer {
this._yModel?.dispose();
super.dispose();
}

async renderModel(mimeModel: IRenderMime.IMimeModel): Promise<void> {
const modelId = mimeModel.data[this._mimeType]!['model_id'];

Expand All @@ -38,6 +39,14 @@ export class JupyterYWidget extends Widget implements IRenderMime.IRenderer {
this._modelFactory.createYWidget(modelId, this.node);
}

render(roomId: string): void {
this._yModel = this._modelFactory.getYModel(roomId);
if (!this._yModel) {
return;
}
this._modelFactory.createYWidget(roomId, this.node);
}

private _modelFactory: NotebookRendererModel;
private _mimeType: string;
private _yModel?: IJupyterYModel;
Expand Down
28 changes: 21 additions & 7 deletions src/notebookrenderer/widgetManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ import { YCommProvider } from './yCommProvider';
import { IJupyterYModel } from '../types';

export class JupyterYWidgetManager implements IJupyterYWidgetManager {

registerNotebook(notebookId: string): IJupyterYWidgetModelRegistry {
const yModelFactories = this.yModelFactories;
const wm = new WidgetModelRegistry({ yModelFactories });
this._registry.set(notebookId, wm);
return wm;
}

registerKernel(kernel: Kernel.IKernelConnection): void {
const yModelFactories = this._yModelFactories;
const yModelFactories = this.yModelFactories;
const wm = new WidgetModelRegistry({ kernel, yModelFactories });
this._registry.set(kernel.id, wm);
}
Expand All @@ -26,37 +34,43 @@ export class JupyterYWidgetManager implements IJupyterYWidgetManager {
yModelFactory: IJupyterYModelFactory,
yWidgetFactory: IJupyterYWidgetFactory
): void {
this._yModelFactories.set(name, yModelFactory);
this.yModelFactories.set(name, yModelFactory);
this._yWidgetFactories.set(name, yWidgetFactory);
}

getWidgetModel(kernelId: string, commId: string): IJupyterYModel | undefined {
return this._registry.get(kernelId)?.getModel(commId);
getWidgetModel(kernelOrNotebookId: string, commOrRoomId: string): IJupyterYModel | undefined {
return this._registry.get(kernelOrNotebookId)?.getModel(commOrRoomId);
}

getWidgetFactory(modelName: string) {
return this._yWidgetFactories.get(modelName);
}

yModelFactories = new Map<string, IJupyterYModelFactory>();
private _registry = new Map<string, IJupyterYWidgetModelRegistry>();
private _yModelFactories = new Map<string, IJupyterYModelFactory>();
private _yWidgetFactories = new Map<string, IJupyterYWidgetFactory>();
}

export class WidgetModelRegistry implements IJupyterYWidgetModelRegistry {
constructor(options: {
kernel: Kernel.IKernelConnection;
kernel?: Kernel.IKernelConnection;
yModelFactories: any;
}) {
const { kernel, yModelFactories } = options;
this._yModelFactories = yModelFactories;
kernel.registerCommTarget('ywidget', this._handle_comm_open);
if (kernel !== undefined) {
kernel.registerCommTarget('ywidget', this._handle_comm_open);
}
}

getModel(id: string): IJupyterYModel | undefined {
return this._yModels.get(id);
}

setModel(id: string, model: IJupyterYModel): void {
this._yModels.set(id, model);
}

/**
* Handle when a comm is opened.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ export interface IJupyterYDoc extends IDisposable {

attrsChanged: ISignal<IJupyterYDoc, MapChange>;
ydoc: Y.Doc;
commMetadata: {[key: string]: any};
options: {[key: string]: any};
disposed: ISignal<any, void>;
}

export interface IJupyterYModel extends IDisposable {
yModelName: string;
isDisposed: boolean;
sharedModel: IJupyterYDoc;
roomId?: string;

sharedAttrsChanged: ISignal<IJupyterYDoc, MapChange>;

Expand Down
67 changes: 67 additions & 0 deletions src/yinputwidget/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { IJupyterYModel } from '../types';
import { JupyterYModel } from '../model';
import { IJupyterYWidgetManager } from '../notebookrenderer/types';
import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';
import { ybinding } from '@jupyterlab/codemirror';
import { StateCommand } from '@codemirror/state';
import { EditorView, KeyBinding, keymap } from '@codemirror/view';
import { WebsocketProvider } from 'y-websocket';

class InputWidget {
constructor(yModel: IJupyterYModel, node: HTMLElement) {
this.yModel = yModel;
this.node = node;

const wsProvider = new WebsocketProvider(
'ws://127.0.0.1:8000', `api/collaboration/room/${yModel.roomId}`,
yModel.sharedModel.ydoc
);

wsProvider.on('sync', (isSynced) => {
const prompt: string = this.yModel.sharedModel.getAttr('prompt');
const password: boolean = this.yModel.sharedModel.getAttr('password');
const promptNode = document.createElement('pre');
promptNode.textContent = prompt;
const input1 = document.createElement('div');
input1.style.border = 'thin solid';
const input2 = document.createElement('div');
if (password === true) {
(input2.style as any).webkitTextSecurity = 'disc';
}
input1.appendChild(input2);
this.node.appendChild(promptNode);
promptNode.appendChild(input1);

const stdin = this.yModel.sharedModel.getAttr('value');
const ybind = ybinding({ ytext: stdin });
const submit: StateCommand = ({ state, dispatch }) => {
this.yModel.sharedModel.setAttr('submitted', true);
return true;
};
const submitWithEnter: KeyBinding = {
key: 'Enter',
run: submit
};
new EditorView({
doc: stdin.toString(),
extensions: [keymap.of([submitWithEnter]), ybind],
parent: input2
});
});
}

yModel: IJupyterYModel;
node: HTMLElement;
}

export const yInputWidget: JupyterFrontEndPlugin<void> = {
id: 'jupyterywidget:yInputWidget',
autoStart: true,
requires: [IJupyterYWidgetManager],
activate: (app: JupyterFrontEnd, wm: IJupyterYWidgetManager): void => {
wm.registerWidget('Input', JupyterYModel, InputWidget);
}
};
47 changes: 47 additions & 0 deletions src/youtputhandler/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
JupyterFrontEnd,
JupyterFrontEndPlugin,
} from '@jupyterlab/application';
import {
INotebookTracker,
INotebookModel,
NotebookPanel,
} from '@jupyterlab/notebook';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { DocumentRegistry } from '@jupyterlab/docregistry';
import YWidget, { PLUGIN_NAME } from './yWidget';
import { IJupyterYWidgetManager } from '../notebookrenderer/types';

class yWidgetExtension implements DocumentRegistry.WidgetExtension {
constructor(tracker: INotebookTracker, wmManager: IJupyterYWidgetManager) {
this._tracker = tracker;
this._wmManager = wmManager;
}

createNew(
panel: NotebookPanel,
context: DocumentRegistry.IContext<INotebookModel>
) {
return new YWidget(panel, this._tracker, this._wmManager);
}

private _tracker: INotebookTracker;
private _wmManager: IJupyterYWidgetManager;
}

export const yOutputHandler: JupyterFrontEndPlugin<void> = {
id: PLUGIN_NAME,
autoStart: true,
requires: [INotebookTracker, ISettingRegistry, IJupyterYWidgetManager],
activate: async (
app: JupyterFrontEnd,
tracker: INotebookTracker,
settingRegistry: ISettingRegistry,
wmManager: IJupyterYWidgetManager
) => {
app.docRegistry.addWidgetExtension(
'Notebook',
new yWidgetExtension(tracker, wmManager)
);
},
};
Loading