Skip to content

Commit 28080e5

Browse files
committed
passive thebe manager (#756)
* wip * latest * controls are rendering * revert notebookId change * remove lumino * use the rendermime * 🎉 outputs * fix markdown cell * alert on comms change * add factory method * added on message handler * 🧹 tidy up, upgrade demo * 📗 changeset
1 parent 80dfbb9 commit 28080e5

File tree

19 files changed

+659
-1104
lines changed

19 files changed

+659
-1104
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'demo-react': patch
3+
'thebe-react': patch
4+
'thebe-core': patch
5+
---
6+
7+
Added a passive widget manager and integrated into `thebe-react` and `demo-react`

apps/demo-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"@types/node": "^16.18.14",
1111
"@types/react": "^18.0.28",
1212
"@types/react-dom": "^18.0.11",
13+
"classnames": "^2.5.1",
1314
"localforage": "^1.10.0",
1415
"match-sorter": "^6.3.1",
1516
"react": "^18.2.0",

apps/demo-react/public/widget-test.ipynb

Lines changed: 363 additions & 1053 deletions
Large diffs are not rendered by default.

apps/demo-react/src/NotebookPage.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
1-
import { ThebeSessionProvider, ThebeRenderMimeRegistryProvider, useThebeServer } from 'thebe-react';
1+
import {
2+
ThebeSessionProvider,
3+
ThebeRenderMimeRegistryProvider,
4+
useThebeServer,
5+
useThebeLoader,
6+
} from 'thebe-react';
27
import { ConnectionStatusTray } from './ConnectionStatusTray';
38
import { ConnectionErrorTray } from './ConnectionErrorTray';
49
import { NotebookStatusTray } from './NotebookStatusTray';
510
import { NotebookErrorTray } from './NotebookErrorTray';
611
import { AdminPanel } from './AdminPanel';
712

813
export function NotebookPage({ children }: React.PropsWithChildren) {
9-
const { connecting, ready, config, error } = useThebeServer();
14+
const { core } = useThebeLoader();
15+
const { connecting, config, ready, error } = useThebeServer();
16+
17+
if (!core) return null;
1018

11-
if (!connecting && !ready && !error) return null;
1219
return (
1320
<ThebeRenderMimeRegistryProvider>
1421
<ThebeSessionProvider start path={config?.kernels.path}>
1522
<>
16-
<ConnectionStatusTray />
17-
<ConnectionErrorTray />
18-
<NotebookStatusTray />
19-
<NotebookErrorTray />
20-
<AdminPanel />
23+
{(connecting || ready || error) && (
24+
<>
25+
<ConnectionStatusTray />
26+
<ConnectionErrorTray />
27+
<NotebookStatusTray />
28+
<NotebookErrorTray />
29+
<AdminPanel />
30+
</>
31+
)}
2132
{children}
2233
</>
2334
</ThebeSessionProvider>

apps/demo-react/src/WidgetsPage.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { useNotebook } from 'thebe-react';
22
import JupyterOutputDecoration from './JupyterOutputDecoration';
33
import { useParams } from 'react-router-dom';
4+
import classNames from 'classnames';
45

56
export function WidgetsPage() {
67
const { notebookName } = useParams<{ notebookName: string }>();
7-
const { ready, executing, executeAll, cellRefs, cellIds } = useNotebook(
8+
const { ready, executing, executeAll, cellRefs, cellIds, errors } = useNotebook(
89
notebookName ?? 'widget-test',
910
async (n) => {
1011
const url = `/${n}.ipynb`;
@@ -27,12 +28,22 @@ export function WidgetsPage() {
2728
<h4 className="text-sm">
2829
notebook: <code>{notebookName}.ipynb</code>
2930
</h4>
30-
<div className="inline-block px-4 py-2 mt-3 text-sm font-bold text-white bg-green-500 rounded-full">
31+
<div
32+
className={classNames(
33+
'inline-block px-4 py-2 mt-3 text-sm font-bold text-white rounded-full',
34+
{ 'bg-gray-500': !ready, 'bg-green-500': ready },
35+
)}
36+
>
3137
{ready ? 'ready' : 'not ready'}
3238
</div>
3339
<div className="mt-4">
3440
{!executing && (
35-
<button className="button" onClick={clickExecute}>
41+
<button
42+
className={classNames('button', {
43+
'text-gray-400 bg-gray-100 border-gray-300 cursor-not-allowed': !ready || errors,
44+
})}
45+
onClick={clickExecute}
46+
>
3647
run all
3748
</button>
3849
)}

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/src/cell.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,31 @@ import { ensureString, shortId } from './utils';
1010

1111
class ThebeCodeCell extends PassiveCellRenderer implements IThebeCell {
1212
kind: CellKind;
13+
notebookId: string;
1314
source: string;
1415
metadata: JsonObject;
1516
session?: ThebeSession;
1617
executionCount: number | null;
17-
protected initialOutputs: IOutput[];
18-
readonly notebookId: string;
1918
protected busy: boolean;
2019
protected events: EventEmitter;
2120

2221
constructor(
2322
id: string,
2423
notebookId: string,
2524
source: string,
25+
outputs: IOutput[],
2626
config: Config,
2727
metadata: JsonObject,
2828
rendermime: IRenderMimeRegistry,
2929
) {
30-
super(id, rendermime);
30+
super(id, outputs, rendermime);
3131
this.kind = 'code';
3232
this.events = new EventEmitter(id, config, EventSubject.cell, this);
3333
this.notebookId = notebookId;
3434
this.source = source;
3535
this.metadata = metadata;
3636
this.busy = false;
3737
this.executionCount = null;
38-
this.initialOutputs = [];
3938
console.debug('thebe:cell constructor', this);
4039
}
4140

@@ -49,6 +48,7 @@ class ThebeCodeCell extends PassiveCellRenderer implements IThebeCell {
4948
icc.id ?? shortId(),
5049
notebookId,
5150
ensureString(icc.source),
51+
icc.outputs ?? [],
5252
config,
5353
icc.metadata,
5454
rendermime,

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export * from './thebe/api';
1111
export * from './thebe/entrypoint';
1212
export * from './utils';
1313
export * from './manager';
14+
export * from './passiveManager';
1415
export * from './rendermime';
1516
export * from './types';
1617
export * from './config';

packages/core/src/manager.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
import type { IRenderMimeRegistry } from '@jupyterlab/rendermime';
22
import type { Widget } from '@lumino/widgets';
3-
43
import * as LuminoWidget from '@lumino/widgets';
54
import { MessageLoop } from '@lumino/messaging';
6-
75
import { KernelWidgetManager, WidgetRenderer, output } from '@jupyter-widgets/jupyterlab-manager';
8-
9-
export const WIDGET_MIMETYPE = 'application/vnd.jupyter.widget-view+json';
10-
116
import * as base from '@jupyter-widgets/base';
127
import * as controls from '@jupyter-widgets/controls';
138
import { shortId } from './utils';
149
import { RequireJsLoader } from './requireJsLoader';
1510
import { requireLoader } from './loader';
1611
import type { Kernel } from '@jupyterlab/services';
1712

13+
export const WIDGET_STATE_MIMETYPE = 'application/vnd.jupyter.widget-state+json';
14+
export const WIDGET_VIEW_MIMETYPE = 'application/vnd.jupyter.widget-view+json';
15+
16+
/**
17+
* @deprecated use WIDGET_VIEW_MIMETYPE
18+
*/
19+
export const WIDGET_MIMETYPE = WIDGET_VIEW_MIMETYPE;
20+
1821
/**
1922
* A Widget Manager class for Thebe using the context-free KernelWidgetManager from
2023
* the JupyterLab Manager and inspierd by the implementation in Voila here:
@@ -32,25 +35,17 @@ export class ThebeManager extends KernelWidgetManager {
3235
/** ensure this registry always gets the widget renderer.
3336
* This is essential for cases where widgets are rendered heirarchically
3437
*/
35-
this.addWidgetFactories();
36-
37-
this._registerWidgets();
38-
this._loader = new RequireJsLoader();
39-
}
40-
41-
addWidgetFactories() {
4238
this.rendermime.addFactory(
4339
{
4440
safe: false,
45-
mimeTypes: [WIDGET_MIMETYPE],
41+
mimeTypes: [WIDGET_VIEW_MIMETYPE],
4642
createRenderer: (options) => new WidgetRenderer(options, this as any),
4743
},
4844
1,
4945
);
50-
}
5146

52-
removeWidgetFactories() {
53-
this.rendermime.removeMimeType(WIDGET_MIMETYPE);
47+
this._registerWidgets();
48+
this._loader = new RequireJsLoader();
5449
}
5550

5651
/**

packages/core/src/markdown.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import PassiveCellRenderer from './passive';
55
import type ThebeSession from './session';
66
import type { CellKind, IThebeCell, IThebeCellExecuteReturn, JsonObject } from './types';
77
import { ensureString, shortId } from './utils';
8+
import type ThebeNotebook from './notebook';
89

910
/**
1011
* A Thebe cell that is exepected to contain markdown (or raw) source.
@@ -28,7 +29,7 @@ export default class ThebeMarkdownCell extends PassiveCellRenderer implements IT
2829
metadata: JsonObject,
2930
rendermime: IRenderMimeRegistry,
3031
) {
31-
super(id, rendermime);
32+
super(id, [], rendermime);
3233
this.kind = 'markdown';
3334
this.id = id;
3435
this.notebookId = notebookId;

0 commit comments

Comments
 (0)