diff --git a/attic/prosemirror/package.json b/attic/prosemirror/package.json
index f953bfbc..c6a3126e 100644
--- a/attic/prosemirror/package.json
+++ b/attic/prosemirror/package.json
@@ -23,7 +23,7 @@
},
"dependencies": {
"@codemirror/lang-python": "6.0.1",
- "@datalayer/jupyter-react": "^0.12.0",
+ "@datalayer/jupyter-react": "^0.15.0",
"@prosemirror-adapter/react": "0.2.4",
"@types/orderedmap": "1.0.0",
"codemirror": "6.0.1",
diff --git a/examples/cra/package.json b/examples/cra/package.json
index e58e39e6..f12b8b4d 100644
--- a/examples/cra/package.json
+++ b/examples/cra/package.json
@@ -13,7 +13,7 @@
},
"dependencies": {
"@datalayer/icons-react": "^0.3.0",
- "@datalayer/jupyter-react": "^0.12.0",
+ "@datalayer/jupyter-react": "^0.15.0",
"@datalayer/primer-addons": "0.3.0",
"jupyterlab-plotly": "^5.17.0",
"plotly.js": "^2.26.2",
diff --git a/examples/cra/src/examples/cell/CellComponents.tsx b/examples/cra/src/examples/cell/CellComponents.tsx
index 408d0b24..b2eda44d 100755
--- a/examples/cra/src/examples/cell/CellComponents.tsx
+++ b/examples/cra/src/examples/cell/CellComponents.tsx
@@ -4,7 +4,7 @@
* MIT License
*/
-import { useCellStore, Cell } from "@datalayer/jupyter-react";
+import { useCellsStore, Cell } from "@datalayer/jupyter-react";
import CellToolbar from './CellToolbar';
const SOURCE_EXAMPLE = `"""
@@ -36,7 +36,7 @@ ax2.set_ylabel('Undamped')
plt.show()`;
const CellPreview = () => {
- const cellStore = useCellStore();
+ const cellStore = useCellsStore();
return (
<>
source: {cellStore.source}
diff --git a/examples/cra/src/examples/cell/CellToolbar.tsx b/examples/cra/src/examples/cell/CellToolbar.tsx
index 7d5588c2..01cdebbf 100755
--- a/examples/cra/src/examples/cell/CellToolbar.tsx
+++ b/examples/cra/src/examples/cell/CellToolbar.tsx
@@ -7,10 +7,10 @@
import React from 'react';
import { Box, IconButton, Text, Tooltip } from '@primer/react';
import { PlayIcon, ReplyIcon, ThreeBarsIcon } from '@primer/octicons-react';
-import { useCellStore } from '@datalayer/jupyter-react';
+import { useCellsStore } from '@datalayer/jupyter-react';
const CellToolbar: React.FC = () => {
- const cellStore = useCellStore();
+ const cellStore = useCellsStore();
const outputsCount = cellStore.outputsCount;
return (
<>
diff --git a/examples/next-js/package.json b/examples/next-js/package.json
index 5a3a3859..3ae2d7db 100644
--- a/examples/next-js/package.json
+++ b/examples/next-js/package.json
@@ -11,7 +11,7 @@
},
"dependencies": {
"@datalayer/icons-react": "^0.3.0",
- "@datalayer/jupyter-react": "^0.12.0",
+ "@datalayer/jupyter-react": "^0.15.0",
"@datalayer/primer-addons": "0.3.0",
"autoprefixer": "^10.4.14",
"eslint": "^8.40.0",
diff --git a/examples/slate/package.json b/examples/slate/package.json
index f35ef4ba..cd982ba7 100644
--- a/examples/slate/package.json
+++ b/examples/slate/package.json
@@ -24,7 +24,7 @@
},
"dependencies": {
"@datalayer/icons-react": "^0.3.0",
- "@datalayer/jupyter-react": "^0.12.0",
+ "@datalayer/jupyter-react": "^0.15.0",
"@datalayer/primer-addons": "0.3.0",
"@emotion/css": "^11.1.3",
"@emotion/react": "^11.10.6",
diff --git a/packages/docusaurus-plugin/package.json b/packages/docusaurus-plugin/package.json
index ec592de3..e5cc53d5 100644
--- a/packages/docusaurus-plugin/package.json
+++ b/packages/docusaurus-plugin/package.json
@@ -23,7 +23,7 @@
},
"dependencies": {
"@datalayer/icons-react": "^0.3.0",
- "@datalayer/jupyter-react": "^0.12.0",
+ "@datalayer/jupyter-react": "^0.15.0",
"@datalayer/primer-addons": "0.3.0",
"@docusaurus/core": "^2.4.0",
"@docusaurus/types": "^2.1.0"
diff --git a/packages/lexical/package.json b/packages/lexical/package.json
index 0a7e498d..d0445be4 100644
--- a/packages/lexical/package.json
+++ b/packages/lexical/package.json
@@ -59,7 +59,7 @@
},
"dependencies": {
"@datalayer/icons-react": "^0.3.0",
- "@datalayer/jupyter-react": "^0.12.0",
+ "@datalayer/jupyter-react": "^0.15.0",
"@datalayer/primer-addons": "0.3.0",
"@jupyterlab/application": "^4.0.0",
"@jupyterlab/coreutils": "^6.0.0",
diff --git a/packages/lexical/src/components/JupyterOutputComponent.tsx b/packages/lexical/src/components/JupyterOutputComponent.tsx
index 7eda41d5..a37ca3b9 100644
--- a/packages/lexical/src/components/JupyterOutputComponent.tsx
+++ b/packages/lexical/src/components/JupyterOutputComponent.tsx
@@ -27,7 +27,7 @@ export const JupyterOutputComponent = (props: Props) => {
adapter={outputAdapter}
showEditor={false}
autoRun={autoRun}
- sourceId={outputNodeUuid}
+ id={outputNodeUuid}
executeTrigger={executeTrigger}
lumino={false}
/>
diff --git a/packages/lexical/src/examples/App2.tsx b/packages/lexical/src/examples/App2.tsx
index 950e2e40..818c17e1 100644
--- a/packages/lexical/src/examples/App2.tsx
+++ b/packages/lexical/src/examples/App2.tsx
@@ -90,7 +90,7 @@ const Tabs = () => {
diff --git a/packages/lexical/src/nodes/JupyterOutputNode.tsx b/packages/lexical/src/nodes/JupyterOutputNode.tsx
index 0e68f3b8..f10263aa 100644
--- a/packages/lexical/src/nodes/JupyterOutputNode.tsx
+++ b/packages/lexical/src/nodes/JupyterOutputNode.tsx
@@ -8,7 +8,7 @@ import { LexicalEditor, EditorConfig, DecoratorNode, LexicalNode, NodeKey, Sprea
import { UUID } from '@lumino/coreutils';
import { IOutput } from '@jupyterlab/nbformat';
import { OUTPUT_UUID_TO_CODE_UUID, CODE_UUID_TO_OUTPUT_KEY, CODE_UUID_TO_OUTPUT_UUID, OUTPUT_UUID_TO_OUTPUT_KEY } from "../plugins/JupyterPlugin";
-import { OutputAdapter } from "@datalayer/jupyter-react";
+import { OutputAdapter, newUuid } from "@datalayer/jupyter-react";
import JupyterOutputComponent from "../components/JupyterOutputComponent";
export type SerializedJupyterOutputNode = Spread<
@@ -52,7 +52,7 @@ export class JupyterOutputNode extends DecoratorNode {
/** @override */
static importJSON(serializedNode: SerializedJupyterOutputNode): JupyterOutputNode {
- return $createJupyterOutputNode(serializedNode.source, new OutputAdapter(undefined, []), serializedNode.outputs, false, serializedNode.codeNodeUuid, serializedNode.outputNodeUuid);
+ return $createJupyterOutputNode(serializedNode.source, new OutputAdapter(newUuid(), undefined, []), serializedNode.outputs, false, serializedNode.codeNodeUuid, serializedNode.outputNodeUuid);
}
/** @override */
diff --git a/packages/lexical/src/plugins/JupyterPlugin.tsx b/packages/lexical/src/plugins/JupyterPlugin.tsx
index 4a6d8adc..69bd4b71 100644
--- a/packages/lexical/src/plugins/JupyterPlugin.tsx
+++ b/packages/lexical/src/plugins/JupyterPlugin.tsx
@@ -10,7 +10,7 @@ import { $getNodeByKey, $createNodeSelection, $setSelection } from "lexical";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { $setBlocksType } from "@lexical/selection";
import { $insertNodeToNearestRoot } from '@lexical/utils';
-import { OutputAdapter } from '@datalayer/jupyter-react';
+import { OutputAdapter, newUuid } from '@datalayer/jupyter-react';
import { UUID } from '@lumino/coreutils';
import { IOutput } from '@jupyterlab/nbformat';
import { $createJupyterCodeNode, JupyterCodeNode, $isJupyterCodeNode } from "../nodes/JupyterCodeNode";
@@ -77,7 +77,7 @@ export const JupyterPlugin = () => {
return true;
}
}
- const jupyterOutputNode = $createJupyterOutputNode(code, new OutputAdapter(undefined, []), [], true, codeNodeUuid, UUID.uuid4());
+ const jupyterOutputNode = $createJupyterOutputNode(code, new OutputAdapter(newUuid(), undefined, []), [], true, codeNodeUuid, UUID.uuid4());
$insertNodeToNearestRoot(jupyterOutputNode);
const nodeSelection = $createNodeSelection();
nodeSelection.add(parentNode.__key);
@@ -163,7 +163,7 @@ export const JupyterPlugin = () => {
} else {
selection.insertNodes([jupyterCodeNode]);
}
- const outputAdapter = new OutputAdapter(undefined, outputs);
+ const outputAdapter = new OutputAdapter(newUuid(), undefined, outputs);
const jupyterOutputNode = $createJupyterOutputNode(code, outputAdapter, outputs || [], false, jupyterCodeNode.getCodeNodeUuid(), UUID.uuid4()) ;
outputAdapter.outputArea.model.changed.connect((outputModel, args) => {
editor.update(() => {
diff --git a/packages/react/jupyter_react/static/README.md b/packages/react/jupyter_react/static/README.md
deleted file mode 100644
index 5cde08ff..00000000
--- a/packages/react/jupyter_react/static/README.md
+++ /dev/null
@@ -1 +0,0 @@
-[![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io)
diff --git a/packages/react/package.json b/packages/react/package.json
index 709bcadc..d846a5f3 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,6 +1,6 @@
{
"name": "@datalayer/jupyter-react",
- "version": "0.14.0",
+ "version": "0.17.0",
"description": "Jupyter React - React.js components 100% compatible with Jupyter.",
"license": "MIT",
"main": "lib/index.js",
@@ -73,7 +73,7 @@
"@jupyter-widgets/output": "^6.0.0",
"@jupyter/collaboration-extension": "^1.0.0",
"@jupyter/web-components": "^0.15.3",
- "@jupyter/ydoc": "^1.0.0",
+ "@jupyter/ydoc": "^2.0.1",
"@jupyterlab/application": "^4.0.0",
"@jupyterlab/application-extension": "^4.0.0",
"@jupyterlab/apputils": "^4.0.0",
@@ -146,6 +146,7 @@
"react-sparklines": "^1.7.0",
"rxjs": "^6.6.0",
"styled-components": "^5.3.10",
+ "ulid": "^2.3.0",
"usehooks-ts": "^2.9.1",
"utf-8-validate": "^6.0.3",
"wildcard-match": "^5.1.2",
diff --git a/packages/react/src/app/tabs/components/NotebookComponent.tsx b/packages/react/src/app/tabs/components/NotebookComponent.tsx
index 85f1e281..ae7daffc 100644
--- a/packages/react/src/app/tabs/components/NotebookComponent.tsx
+++ b/packages/react/src/app/tabs/components/NotebookComponent.tsx
@@ -15,7 +15,7 @@ const NotebookComponent = () => {
{/*
{
- const { type='code', source = '', autoStart, showToolbar=true } = props;
- const { serverSettings, defaultKernel } = useJupyter();
+ const {
+ autoStart,
+ showToolbar=true,
+ source = '',
+ type='code',
+ } = props;
+ const { defaultKernel, serverSettings } = useJupyter();
+
const [id] = useState(props.id || newUuid());
const [adapter, setAdapter] = useState();
- const cellStore = useCellStore();
+
+ const cellsStore = useCellsStore();
const handleCellInitEvents = (adapter: CellAdapter) => {
adapter.cell.model.contentChanged.connect(
(cellModel, changedArgs) => {
- cellStore.setSource(id, cellModel.sharedModel.getSource());
+ cellsStore.setSource(id, cellModel.sharedModel.getSource());
}
);
if (adapter.cell instanceof CodeCell) {
adapter.cell.outputArea.outputLengthChanged?.connect(
(outputArea, outputsCount) => {
- cellStore.setOutputsCount(id, outputsCount);
+ cellsStore.setOutputsCount(id, outputsCount);
}
);
}
adapter.sessionContext.initialize().then(() => {
- if (!autoStart || !adapter.cell.model) {
- return;
- }
-
- // Perform auto-start for code or markdown cells
- if (adapter.cell instanceof CodeCell) {
- CodeCell.execute(
- adapter.cell,
- adapter.sessionContext
- );
- }
-
- if (adapter.cell instanceof MarkdownCell) {
- adapter.cell.rendered = true;
+ if (autoStart && adapter.cell.model) {
+ // Perform auto-start for code or markdown cells.
+ adapter.execute();
}
});
adapter.sessionContext.kernelChanged.connect(() => {
void adapter.sessionContext.session?.kernel?.info.then(info => {
// Set that session/kernel is ready for this cell when the kernel is guaranteed to be connected
- cellStore.setIsKernelSessionAvailable(id, true);
+ cellsStore.setKernelSessionAvailable(id, true);
})
});
}
@@ -88,14 +84,15 @@ export const Cell = (props: ICellProps) => {
if (id && defaultKernel && serverSettings) {
defaultKernel.ready.then(() => {
const adapter = new CellAdapter({
+ id,
type,
source,
serverSettings,
kernel: defaultKernel,
boxOptions: {showToolbar}
});
- cellStore.setAdapter(id, adapter);
- cellStore.setSource(id, source);
+ cellsStore.setAdapter(id, adapter);
+ cellsStore.setSource(id, source);
handleCellInitEvents(adapter);
setAdapter(adapter);
diff --git a/packages/react/src/components/cell/CellAdapter.ts b/packages/react/src/components/cell/CellAdapter.ts
index 4688d675..81c4a12b 100755
--- a/packages/react/src/components/cell/CellAdapter.ts
+++ b/packages/react/src/components/cell/CellAdapter.ts
@@ -7,6 +7,7 @@
import { BoxPanel, Widget } from '@lumino/widgets';
import { find } from '@lumino/algorithm';
import { CommandRegistry } from '@lumino/commands';
+import { JSONObject } from '@lumino/coreutils';
import {
SessionContext,
ISessionContext,
@@ -14,6 +15,7 @@ import {
ToolbarButton,
} from '@jupyterlab/apputils';
import { CodeCellModel, CodeCell, Cell, MarkdownCell, RawCell, MarkdownCellModel } from '@jupyterlab/cells';
+import { Kernel as JupyterKernel, KernelMessage } from '@jupyterlab/services';
import {
ybinding,
CodeMirrorMimeTypeService,
@@ -34,6 +36,7 @@ import {
RenderMimeRegistry,
standardRendererFactories as initialFactories,
} from '@jupyterlab/rendermime';
+import { execute as executeOutput } from './../output/OutputExecutor';
import {
Session,
ServerConnection,
@@ -56,15 +59,18 @@ import CellCommands from './CellCommands';
interface BoxOptions {
showToolbar?: boolean;
}
+
export class CellAdapter {
+ private _id: string;
private _cell: CodeCell | MarkdownCell | RawCell;
private _kernel: Kernel;
private _panel: BoxPanel;
private _sessionContext: SessionContext;
- private _type: 'code' | 'markdown' | 'raw'
+ private _type: 'code' | 'markdown' | 'raw';
- constructor(options: CellAdapter.ICellAdapterOptions) {
- const { type, source, serverSettings, kernel, boxOptions } = options;
+ public constructor(options: CellAdapter.ICellAdapterOptions) {
+ const { id, type, source, serverSettings, kernel, boxOptions } = options;
+ this._id = id;
this._kernel = kernel;
this._type = type;
this.setupCell(type, source, serverSettings, kernel, boxOptions);
@@ -303,7 +309,7 @@ export class CellAdapter {
});
handler.editor = editor;
- CellCommands(commands, this._cell!, this._sessionContext, handler);
+ CellCommands(commands, this._cell!, handler, this);
completer.hide();
completer.addClass('jp-Completer-Cell');
Widget.attach(completer, document.body);
@@ -313,7 +319,7 @@ export class CellAdapter {
icon: runIcon,
onClick: () => {
if (this._type === 'code') {
- CodeCell.execute(this._cell as CodeCell, this._sessionContext);
+ this.execute();
} else if (this._type === 'markdown') {
(this._cell as MarkdownCell).rendered = true;
}
@@ -377,15 +383,126 @@ export class CellAdapter {
execute = () => {
if (this._type === 'code') {
- CodeCell.execute((this._cell as CodeCell), this._sessionContext);
+ this._execute(this._cell as CodeCell);
} else if (this._type === 'markdown') {
(this._cell as MarkdownCell).rendered = true;
}
- };
+ }
+
+ private async _execute(
+ cell: CodeCell,
+ metadata?: JSONObject
+ ): Promise {
+ const model = cell.model;
+ const code = model.sharedModel.getSource();
+ if (!code.trim() || !this.kernel) {
+ model.sharedModel.transact(() => {
+ model.clearExecution();
+ }, false);
+ return new Promise(() => {});
+ }
+ const cellId = { cellId: model.sharedModel.getId() };
+ metadata = {
+ ...model.metadata,
+ ...metadata,
+ ...cellId
+ };
+ const { recordTiming } = metadata;
+ model.sharedModel.transact(() => {
+ model.clearExecution();
+ cell.outputHidden = false;
+ }, false);
+ cell.setPrompt('*');
+ model.trusted = true;
+ let future:
+ | JupyterKernel.IFuture<
+ KernelMessage.IExecuteRequestMsg,
+ KernelMessage.IExecuteReplyMsg
+ >
+ | undefined;
+ try {
+ const kernelMessagePromise = executeOutput(
+ this._id,
+ code,
+ cell.outputArea,
+ this._kernel,
+ metadata
+ );
+ // cell.outputArea.future assigned synchronously in `execute`.
+ if (recordTiming) {
+ const recordTimingHook = (msg: KernelMessage.IIOPubMessage) => {
+ let label: string;
+ switch (msg.header.msg_type) {
+ case 'status':
+ label = `status.${
+ (msg as KernelMessage.IStatusMsg).content.execution_state
+ }`;
+ break;
+ case 'execute_input':
+ label = 'execute_input';
+ break;
+ default:
+ return true;
+ }
+ // If the data is missing, estimate it to now
+ // Date was added in 5.1: https://jupyter-client.readthedocs.io/en/stable/messaging.html#message-header
+ const value = msg.header.date || new Date().toISOString();
+ const timingInfo: any = Object.assign(
+ {},
+ model.getMetadata('execution')
+ );
+ timingInfo[`iopub.${label}`] = value;
+ model.setMetadata('execution', timingInfo);
+ return true;
+ };
+ cell.outputArea.future.registerMessageHook(recordTimingHook);
+ } else {
+ model.deleteMetadata('execution');
+ }
+ // Save this execution's future so we can compare in the catch below.
+ future = cell.outputArea.future;
+ const executeReplyMessage = (await kernelMessagePromise)!;
+ model.executionCount = executeReplyMessage.content.execution_count;
+ if (recordTiming) {
+ const timingInfo = Object.assign(
+ {},
+ model.getMetadata('execution') as any
+ );
+ const started = executeReplyMessage.metadata.started as string;
+ // Started is not in the API, but metadata IPyKernel sends
+ if (started) {
+ timingInfo['shell.execute_reply.started'] = started;
+ }
+ // Per above, the 5.0 spec does not assume date, so we estimate is required
+ const finished = executeReplyMessage.header.date as string;
+ timingInfo['shell.execute_reply'] =
+ finished || new Date().toISOString();
+ model.setMetadata('execution', timingInfo);
+ }
+ return executeReplyMessage;
+ } catch (e) {
+ // If we started executing, and the cell is still indicating this execution, clear the prompt.
+ if (future && !cell.isDisposed && cell.outputArea.future === future) {
+ cell.setPrompt('');
+ if (recordTiming && future.isDisposed) {
+ // Record the time when the cell execution was aborted
+ const timingInfo: any = Object.assign(
+ {},
+ model.getMetadata('execution')
+ );
+ timingInfo['execution_failed'] = new Date().toISOString();
+ model.setMetadata('execution', timingInfo);
+ }
+ }
+ throw e;
+ }
+ }
+
}
export namespace CellAdapter {
export type ICellAdapterOptions = {
+ id: string;
type: 'code' | 'markdown' | 'raw';
source: string;
serverSettings: ServerConnection.ISettings;
diff --git a/packages/react/src/components/cell/CellCommands.ts b/packages/react/src/components/cell/CellCommands.ts
index b08f8b2e..e48f36e0 100644
--- a/packages/react/src/components/cell/CellCommands.ts
+++ b/packages/react/src/components/cell/CellCommands.ts
@@ -7,7 +7,7 @@
import { CommandRegistry } from '@lumino/commands';
import { CompletionHandler } from '@jupyterlab/completer';
import { CodeCell, MarkdownCell, RawCell } from '@jupyterlab/cells';
-import { SessionContext } from '@jupyterlab/apputils';
+import CellAdapter from './CellAdapter';
const cmdIds = {
invoke: 'completer:invoke',
@@ -17,8 +17,8 @@ const cmdIds = {
export const CellCommands = (
commandRegistry: CommandRegistry,
cell: CodeCell | MarkdownCell | RawCell,
- sessionContext: SessionContext,
- completerHandler: CompletionHandler
+ completerHandler: CompletionHandler,
+ cellAdapter: CellAdapter,
): void => {
commandRegistry.addCommand(cmdIds.invoke, {
label: 'Completer: Invoke',
@@ -31,7 +31,7 @@ export const CellCommands = (
commandRegistry.addCommand('run:cell', {
execute: () => {
if (cell instanceof CodeCell) {
- CodeCell.execute(cell, sessionContext)
+ cellAdapter.execute();
} else if (cell instanceof MarkdownCell) {
(cell as MarkdownCell).rendered = true;
}
diff --git a/packages/react/src/components/cell/CellState.ts b/packages/react/src/components/cell/CellState.ts
index 0a7d1cdb..459cebcd 100644
--- a/packages/react/src/components/cell/CellState.ts
+++ b/packages/react/src/components/cell/CellState.ts
@@ -6,7 +6,7 @@
import { createStore } from 'zustand/vanilla';
import { useStore } from 'zustand';
-import CellAdapter from './CellAdapter';
+import { CellAdapter } from './CellAdapter';
export interface ICellState {
source?: string;
@@ -20,16 +20,16 @@ export interface ICellsState {
areAllKernelSessionsReady: boolean; // Control the state for all cells
}
-export type CellState = ICellsState & {
+export type CellsState = ICellsState & {
setCells: (cells: Map) => void;
setSource: (id: string, source: string) => void;
setOutputsCount: (id: string, outputsCount: number) => void;
- setIsKernelSessionAvailable: (id: string, kernelAvailable: boolean) => void;
+ setKernelSessionAvailable: (id: string, kernelAvailable: boolean) => void;
setAdapter: (id: string, adapter?: CellAdapter) => void;
getAdapter: (id: string) => CellAdapter | undefined;
getSource: (id: string) => string | undefined;
getOutputsCount: (id: string) => number | undefined;
- getIsKernelSessionAvailable: (id: string) => boolean | undefined;
+ isKernelSessionAvailable: (id: string) => boolean | undefined;
execute: (id?: string) => void;
};
@@ -45,24 +45,22 @@ const areAllKernelSessionsAvailable = (cells: Map): boolean
return true;
};
-export const cellStore = createStore((set, get) => ({
+export const cellsStore = createStore((set, get) => ({
cells: new Map(),
source: '',
outputsCount: 0,
- isKernelSessionAvailable: false,
areAllKernelSessionsReady: false,
adapter: undefined,
- setCells: (cells: Map) => set((cell: CellState) => ({ cells })),
-
+ setCells: (cells: Map) => set((cell: CellsState) => ({ cells })),
setSource: (id: string, source: string) => {
const cells = get().cells;
const cell = cells.get(id);
if (cell) {
cell.source = source;
} else {
- cells.set(id, {source});
+ cells.set(id, { source });
}
- set((cell: CellState) => ({ cells }))
+ set((cell: CellsState) => ({ cells }))
},
setOutputsCount: (id: string, outputsCount: number) => {
const cells = get().cells;
@@ -70,11 +68,11 @@ export const cellStore = createStore((set, get) => ({
if (cell) {
cell.outputsCount = outputsCount;
} else {
- cells.set(id, {outputsCount});
+ cells.set(id, { outputsCount });
}
- set((state: CellState) => ({ cells }))
+ set((state: CellsState) => ({ cells }));
},
- setIsKernelSessionAvailable: (id: string, isKernelSessionAvailable: boolean) => {
+ setKernelSessionAvailable: (id: string, isKernelSessionAvailable: boolean) => {
const cells = get().cells;
const cell = cells.get(id);
if (cell) {
@@ -83,7 +81,7 @@ export const cellStore = createStore((set, get) => ({
cells.set(id, {isKernelSessionAvailable});
}
const areAllKernelSessionsReady = areAllKernelSessionsAvailable(cells);
- set((cell: CellState) => ({ cells, areAllKernelSessionsReady }));
+ set((cell: CellsState) => ({ cells, areAllKernelSessionsReady }));
},
setAdapter: (id: string, adapter?: CellAdapter) => {
const cells = get().cells;
@@ -93,7 +91,7 @@ export const cellStore = createStore((set, get) => ({
} else {
cells.set(id, { adapter });
}
- set((cell: CellState) => ({ cells }))
+ set((cell: CellsState) => ({ cells }))
},
getAdapter: (id: string) => {
return get().cells.get(id)?.adapter;
@@ -104,24 +102,24 @@ export const cellStore = createStore((set, get) => ({
getOutputsCount: (id: string): number | undefined => {
return get().cells.get(id)?.outputsCount;
},
- getIsKernelSessionAvailable: (id: string): boolean | undefined => {
+ isKernelSessionAvailable: (id: string): boolean | undefined => {
return get().cells.get(id)?.isKernelSessionAvailable;
},
execute: (id: string) => {
const cells = get().cells;
const cell = cells.get(id);
if (cell) {
- cell.adapter?.execute()
+ cell.adapter?.execute();
} else {
- get().cells.forEach((cell) => cell.adapter?.execute())
+ get().cells.forEach((cell) => cell.adapter?.execute());
}
},
}));
-export function useCellStore(): CellState;
-export function useCellStore(selector: (state: CellState) => T): T;
-export function useCellStore(selector?: (state: CellState) => T) {
- return useStore(cellStore, selector!);
+export function useCellsStore(): CellsState;
+export function useCellsStore(selector: (state: CellsState) => T): T;
+export function useCellsStore(selector?: (state: CellsState) => T) {
+ return useStore(cellsStore, selector!);
}
-export default useCellStore;
+export default useCellsStore;
diff --git a/packages/react/src/components/codemirror/CodeMirrorEditor.tsx b/packages/react/src/components/codemirror/CodeMirrorEditor.tsx
index 0e6204cf..aeee1352 100644
--- a/packages/react/src/components/codemirror/CodeMirrorEditor.tsx
+++ b/packages/react/src/components/codemirror/CodeMirrorEditor.tsx
@@ -13,7 +13,7 @@ import Kernel from '../../jupyter/kernel/Kernel';
import codeMirrorTheme from './CodeMirrorTheme';
import OutputAdapter from '../output/OutputAdapter';
import CodeMirrorOutputToolbar from './CodeMirrorOutputToolbar';
-import useOutputStore from '../output/OutputState';
+import useOutputsStore from '../output/OutputState';
export const CodeMirrorEditor = (props: {
code: string;
@@ -37,10 +37,10 @@ export const CodeMirrorEditor = (props: {
insertText,
kernel,
} = props;
- const outputStore = useOutputStore();
+ const outputStore = useOutputsStore();
const [view, setView] = useState();
const dataset = outputStore.getDataset(sourceId);
- const source = outputStore.getSource(sourceId);
+ const source = outputStore.getInput(sourceId);
const editorDiv = useRef();
const setEditorSource = (source: string | undefined) => {
if (view && source) {
@@ -78,10 +78,7 @@ export const CodeMirrorEditor = (props: {
return true;
};
useEffect(() => {
- outputStore.setSource({
- sourceId,
- source: code,
- })
+ outputStore.setInput(sourceId, code);
const language = new Compartment();
const keyBinding = [
{
@@ -101,10 +98,7 @@ export const CodeMirrorEditor = (props: {
EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
if (viewUpdate.docChanged) {
const source = viewUpdate.state.doc.toString();
- outputStore.setSource({
- sourceId,
- source,
- })
+ outputStore.setInput(sourceId, source);
}
}),
],
@@ -122,10 +116,10 @@ export const CodeMirrorEditor = (props: {
};
}, [code]);
useEffect(() => {
- doInsertText(dataset?.dataset);
+ doInsertText(dataset);
}, [dataset]);
useEffect(() => {
- setEditorSource(source?.source);
+ setEditorSource(source);
}, [source]);
return (
<>
diff --git a/packages/react/src/components/kernel/Kernelndicator.tsx b/packages/react/src/components/kernel/Kernelndicator.tsx
index 0a2059de..f1428e77 100644
--- a/packages/react/src/components/kernel/Kernelndicator.tsx
+++ b/packages/react/src/components/kernel/Kernelndicator.tsx
@@ -23,7 +23,7 @@ import { KernelMessage } from '@jupyterlab/services';
import { ConnectionStatus, IKernelConnection } from '@jupyterlab/services/lib/kernel/kernel';
import { Environment } from '../environment/Environment';
-type KernelState =
+export type ExecutionState =
'connecting' |
'connected-unknown' |
'connected-starting' |
@@ -34,7 +34,8 @@ type KernelState =
'connected-autorestarting' |
'connected-dead' |
'disconnecting' |
- 'undefined';
+ 'undefined'
+ ;
/**
* The valid kernel connection states.
@@ -54,7 +55,7 @@ type KernelState =
*
* Status = 'unknown' | 'starting' | 'idle' | 'busy' | 'terminating' | 'restarting' | 'autorestarting' | 'dead';
*/
-export const KERNEL_STATES: Map = new Map([
+export const KERNEL_STATES: Map = new Map([
['connecting', ],
['connected-unknown', ],
['connected-starting', ],
@@ -68,27 +69,28 @@ export const KERNEL_STATES: Map = new Map([
['undefined', ],
]);
-type Props = {
+export const toKernelState = (
+ connectionStatus: ConnectionStatus,
+ status: KernelMessage.Status
+): ExecutionState => {
+ if (
+ connectionStatus === 'connecting' ||
+ connectionStatus === 'disconnected'
+ ) {
+ return connectionStatus as ExecutionState;
+ }
+ return connectionStatus + '-' + status as ExecutionState;
+};
+
+type KernelIndicatorProps = {
kernel?: IKernelConnection | null;
env?: Environment;
};
-export const KernelIndicator = (props: Props) => {
+export const KernelIndicator = (props: KernelIndicatorProps) => {
const { kernel, env } = props;
const [connectionStatus, setConnectionStatus] = useState();
const [status, setStatus] = useState();
- const toState = (
- connectionStatus: ConnectionStatus,
- status: KernelMessage.Status
- ): KernelState => {
- if (
- connectionStatus === 'connecting' ||
- connectionStatus === 'disconnected'
- ) {
- return connectionStatus as KernelState;
- }
- return connectionStatus + '-' + status as KernelState;
- };
useEffect(() => {
if (kernel) {
setConnectionStatus(kernel?.connectionStatus);
@@ -105,7 +107,7 @@ export const KernelIndicator = (props: Props) => {
}, [kernel]);
return connectionStatus && status ? (
- {KERNEL_STATES.get(toState(connectionStatus, status))}
+ {KERNEL_STATES.get(toKernelState(connectionStatus, status))}
) : (
diff --git a/packages/react/src/components/kernel/inspector/widget.tsx b/packages/react/src/components/kernel/inspector/widget.tsx
index b1ae5870..99ce4970 100644
--- a/packages/react/src/components/kernel/inspector/widget.tsx
+++ b/packages/react/src/components/kernel/inspector/widget.tsx
@@ -12,7 +12,6 @@ import {
closeIcon,
jsonIcon,
} from '@jupyterlab/ui-components';
-import { UUID } from '@lumino/coreutils';
import { Message as luminoMessage } from '@lumino/messaging';
import { Widget, BoxLayout } from '@lumino/widgets';
import {
@@ -20,6 +19,7 @@ import {
ObjectLabel,
InspectorNodeParams,
} from 'react-inspector';
+import { newUuid } from '../../../utils';
import { KernelSpyModel, ThreadIterator } from './model';
import './kernelinspector.css';
@@ -127,7 +127,7 @@ namespace Message {
export class MessageLogView extends VDomRenderer {
constructor(model: KernelSpyModel) {
super(model);
- this.id = `kernelspy-messagelog-${UUID.uuid4()}`;
+ this.id = `kernelspy-messagelog-${newUuid()}`;
this.addClass('dla-KernelInspector-messagelog');
}
@@ -216,7 +216,7 @@ export class KernelSpyView extends Widget {
super();
this._model = new KernelSpyModel(kernel);
this.addClass('dla-KernelInspector-view');
- this.id = `kernelspy-${UUID.uuid4()}`;
+ this.id = `kernelspy-${newUuid()}`;
this.title.label = 'Kernel spy';
this.title.closable = true;
this.title.icon = jsonIcon;
diff --git a/packages/react/src/components/notebook/Notebook.tsx b/packages/react/src/components/notebook/Notebook.tsx
index 076987e1..466d924a 100644
--- a/packages/react/src/components/notebook/Notebook.tsx
+++ b/packages/react/src/components/notebook/Notebook.tsx
@@ -63,7 +63,7 @@ export type INotebookProps = {
path?: string;
readOnly: boolean;
renderers: IRenderMime.IRendererFactory[];
- uid: string;
+ id: string;
url?: string;
CellSidebar?: (props: CellSidebarProps) => JSX.Element;
Toolbar?: (props: any) => JSX.Element;
@@ -91,30 +91,30 @@ export const Notebook = (props: INotebookProps) => {
} = props;
const notebookStore = useNotebookStore();
- const [uid] = useState(props.uid || newUuid());
+ const [id] = useState(props.id || newUuid());
const [adapter, setAdapter] = useState();
const kernel = propsKernel || defaultKernel;
- const portals = notebookStore.selectNotebookPortals(uid);
+ const portals = notebookStore.selectNotebookPortals(id);
const newAdapterState = () => {
- if (uid && serviceManager && kernelManager && kernel) {
+ if (id && serviceManager && kernelManager && kernel) {
kernel.ready.then(() => {
const adapter = new NotebookAdapter(
{
...props,
kernel,
- uid,
+ id,
},
serviceManager,
lite
);
setAdapter(adapter);
- notebookStore.update({ uid, partialState: { adapter: adapter } })
+ notebookStore.update({ id, partialState: { adapter: adapter } })
adapter.serviceManager.ready.then(() => {
if (!readOnly) {
const activeCell = adapter.notebookPanel!.content.activeCell;
if (activeCell) {
notebookStore.activeCellChange({
- uid,
+ id,
cellModel: activeCell,
});
}
@@ -122,7 +122,7 @@ export const Notebook = (props: INotebookProps) => {
adapter.notebookPanel!.content.activeCellChanged
);
activeCellChanged$.subscribe((cellModel: Cell) => {
- notebookStore.activeCellChange({ uid, cellModel });
+ notebookStore.activeCellChange({ id, cellModel });
const panelDiv = document.getElementById(
'right-panel-id'
) as HTMLDivElement;
@@ -130,7 +130,7 @@ export const Notebook = (props: INotebookProps) => {
const cellMetadataOptions = (
@@ -138,7 +138,7 @@ export const Notebook = (props: INotebookProps) => {
);
const portal = createPortal(cellMetadataOptions, panelDiv);
notebookStore.setPortalDisplay({
- uid,
+ id,
portalDisplay: { portal, pinned: false },
});
}
@@ -146,32 +146,32 @@ export const Notebook = (props: INotebookProps) => {
}
adapter.notebookPanel?.model?.contentChanged.connect(
(notebookModel, _) => {
- notebookStore.modelChange({ uid, notebookModel });
+ notebookStore.modelChange({ id, notebookModel });
}
);
/*
adapter.notebookPanel?.model!.sharedModel.changed.connect((_, notebookChange) => {
- notebookStore.notebookChange({ uid, notebookChange });
+ notebookStore.notebookChange({ id, notebookChange });
});
adapter.notebookPanel?.content.modelChanged.connect((notebook, _) => {
- dispatÅch(notebookStore.notebookChange({ uid, notebook }));
+ dispatÅch(notebookStore.notebookChange({ id, notebook }));
});
*/
adapter.notebookPanel?.content.activeCellChanged.connect(
(_, cellModel) => {
if (cellModel === null) {
notebookStore.activeCellChange({
- uid,
+ id,
cellModel: undefined,
});
} else {
- notebookStore.activeCellChange({ uid, cellModel });
+ notebookStore.activeCellChange({ id, cellModel });
}
}
);
adapter.notebookPanel?.sessionContext.statusChanged.connect(
(_, kernelStatus) => {
- notebookStore.kernelStatusChanged({ uid, kernelStatus });
+ notebookStore.kernelStatusChanged({ id, kernelStatus });
}
);
});
@@ -184,10 +184,10 @@ export const Notebook = (props: INotebookProps) => {
}
newAdapterState();
return () => {
- notebookStore.setPortalDisplay({ uid, portalDisplay: undefined });
- notebookStore.dispose(uid);
+ notebookStore.setPortalDisplay({ id, portalDisplay: undefined });
+ notebookStore.dispose(id);
};
- }, [uid, serviceManager, kernelManager, kernel, path]);
+ }, [id, serviceManager, kernelManager, kernel, path]);
useEffect(() => {
if (adapter && nbformat) {
adapter.setNbformat(nbformat);
@@ -198,7 +198,7 @@ export const Notebook = (props: INotebookProps) => {
style={{ height, width: '100%', position: 'relative' }}
id="dla-Jupyter-Notebook"
>
- {Toolbar && }
+ {Toolbar && }
JSX.Element;
constructor(
@@ -100,7 +100,7 @@ export class NotebookAdapter {
this._path = props.path;
this._readOnly = props.readOnly;
this._renderers = props.renderers;
- this._uid = props.uid;
+ this._id = props.id;
this._CellSidebar = props.CellSidebar;
@@ -223,14 +223,14 @@ export class NotebookAdapter {
const contentFactory = this._CellSidebar
? new JupyterReactContentFactory(
this._CellSidebar,
- this._uid,
+ this._id,
this._nbgrader,
this._commandRegistry,
{ editorFactory },
)
: new NotebookPanel.ContentFactory({ editorFactory });
- this._tracker = new NotebookTracker({ namespace: this._uid });
+ this._tracker = new NotebookTracker({ namespace: this._id });
const notebookWidgetFactory = new NotebookWidgetFactory({
name: 'Notebook',
@@ -418,8 +418,8 @@ export class NotebookAdapter {
(this._context as Context).model.dirty = false;
const now = new Date().toISOString();
const model: Contents.IModel = {
- path: this._uid,
- name: this._uid,
+ path: this._id,
+ name: this._id,
type: 'notebook',
content: undefined,
writable: true,
@@ -492,8 +492,8 @@ export class NotebookAdapter {
});
}
- get uid(): string {
- return this._uid;
+ get id(): string {
+ return this._id;
}
get notebookPanel(): NotebookPanel | undefined {
diff --git a/packages/react/src/components/notebook/NotebookState.ts b/packages/react/src/components/notebook/NotebookState.ts
index bd50c826..ed36cfb7 100644
--- a/packages/react/src/components/notebook/NotebookState.ts
+++ b/packages/react/src/components/notebook/NotebookState.ts
@@ -36,217 +36,217 @@ export interface INotebooksState {
notebooks: Map;
}
-type UpdateUid = {
- uid: string;
+type UpdateId = {
+ id: string;
partialState: Partial;
};
-type NotebookChangeUid = {
- uid: string;
+type NotebookChangeId = {
+ id: string;
notebookChange: NotebookChange;
};
-type NotebookModelUid = {
- uid: string;
+type NotebookModelId = {
+ id: string;
notebookModel: INotebookModel;
};
-type CellModelUid = {
- uid: string;
+type CellModelId = {
+ id: string;
cellModel?: Cell;
};
type KernelStatusMutation = {
- uid: string;
+ id: string;
kernelStatus: JupyterKernel.Status;
};
type KernelChangeMutation = {
- uid: string;
+ id: string;
kernel: Kernel;
};
type ReactPortalsMutation = {
- uid: string;
+ id: string;
portals: ReactPortal[];
};
type PortalDisplayMutation = {
- uid: string;
+ id: string;
portalDisplay: PortalDisplay | undefined;
};
type DateMutation = {
- uid: string;
+ id: string;
date: Date | undefined;
};
type CellMutation = {
- uid: string;
+ id: string;
cellType: nbformat.CellType;
source?: string;
};
export type NotebookState = INotebooksState & {
setNotebooks: (notebooks: Map) => void;
- selectNotebook: (uid: string) => INotebookState | undefined;
- selectNotebookModel: (uid: string) => { model: INotebookModel | undefined; changed: any } | undefined;
- selectKernelStatus: (uid: string) => string | undefined;
- selectActiveCell: (uid: string) => Cell | undefined;
- selectNotebookPortals: (uid: string) => React.ReactPortal[] | undefined;
- selectSaveRequest: (uid: string) => Date | undefined;
- selectNotebookPortalDisplay: (uid: string) => PortalDisplay | undefined;
- run: (uid: string) => void;
- runAll: (uid: string) => void;
- interrupt: (uid: string) => void;
+ selectNotebook: (id: string) => INotebookState | undefined;
+ selectNotebookModel: (id: string) => { model: INotebookModel | undefined; changed: any } | undefined;
+ selectKernelStatus: (id: string) => string | undefined;
+ selectActiveCell: (id: string) => Cell | undefined;
+ selectNotebookPortals: (id: string) => React.ReactPortal[] | undefined;
+ selectSaveRequest: (id: string) => Date | undefined;
+ selectNotebookPortalDisplay: (id: string) => PortalDisplay | undefined;
+ run: (id: string) => void;
+ runAll: (id: string) => void;
+ interrupt: (id: string) => void;
insertAbove: (mutation: CellMutation) => void;
insertBelow: (mutation: CellMutation) => void;
- delete: (uid: string) => void;
+ delete: (id: string) => void;
changeCellType: (mutation: CellMutation) => void;
save: (mutation: DateMutation) => void;
reset: () => void;
- update: (update: UpdateUid) => void;
- activeCellChange: (cellModelUid: CellModelUid) => void;
- modelChange: (notebookModelUid: NotebookModelUid) => void;
- notebookChange: (notebookChangeUid: NotebookChangeUid) => void;
- kernelStatusChanged: (kernelStatusUid: KernelStatusMutation) => void;
+ update: (update: UpdateId) => void;
+ activeCellChange: (cellModelId: CellModelId) => void;
+ modelChange: (notebookModelId: NotebookModelId) => void;
+ notebookChange: (notebookChangeId: NotebookChangeId) => void;
+ kernelStatusChanged: (kernelStatusId: KernelStatusMutation) => void;
changeKernel: (kernelChange: KernelChangeMutation) => void;
- addPortals: (portalsUid: ReactPortalsMutation) => void;
- dispose: (uid: string) => void;
- setPortals: (portalsUid: ReactPortalsMutation) => void;
- setPortalDisplay: (portalDisplayUid: PortalDisplayMutation) => void;
+ addPortals: (portalsId: ReactPortalsMutation) => void;
+ dispose: (id: string) => void;
+ setPortals: (portalsId: ReactPortalsMutation) => void;
+ setPortalDisplay: (portalDisplayId: PortalDisplayMutation) => void;
};
export const notebookStore = createStore((set, get) => ({
notebooks: new Map(),
setNotebooks: (notebooks: Map) => set((state: NotebookState) => ({ notebooks })),
- selectNotebook: (uid: string): INotebookState | undefined => {
- return get().notebooks.get(uid);
+ selectNotebook: (id: string): INotebookState | undefined => {
+ return get().notebooks.get(id);
},
- selectNotebookModel: (uid: string): { model: INotebookModel | undefined; changed: any } | undefined => {
- if (get().notebooks.get(uid)) {
+ selectNotebookModel: (id: string): { model: INotebookModel | undefined; changed: any } | undefined => {
+ if (get().notebooks.get(id)) {
return {
- model: get().notebooks.get(uid)?.model,
- changed: get().notebooks.get(uid)?.model?.contentChanged,
+ model: get().notebooks.get(id)?.model,
+ changed: get().notebooks.get(id)?.model?.contentChanged,
};
}
return undefined;
},
- selectKernelStatus: (uid: string): string | undefined => {
- return get().notebooks.get(uid)?.kernelStatus;
+ selectKernelStatus: (id: string): string | undefined => {
+ return get().notebooks.get(id)?.kernelStatus;
},
- selectActiveCell: (uid: string): Cell | undefined => {
- return get().notebooks.get(uid)?.activeCell;
+ selectActiveCell: (id: string): Cell | undefined => {
+ return get().notebooks.get(id)?.activeCell;
},
- selectNotebookPortals: (uid: string): React.ReactPortal[] | undefined => {
- return get().notebooks.get(uid)?.portals;
+ selectNotebookPortals: (id: string): React.ReactPortal[] | undefined => {
+ return get().notebooks.get(id)?.portals;
},
- selectSaveRequest: (uid: string): Date | undefined => {
- return get().notebooks.get(uid)?.saveRequest;
+ selectSaveRequest: (id: string): Date | undefined => {
+ return get().notebooks.get(id)?.saveRequest;
},
- selectNotebookPortalDisplay: (uid: string): PortalDisplay | undefined => {
- return get().notebooks.get(uid)?.portalDisplay;
+ selectNotebookPortalDisplay: (id: string): PortalDisplay | undefined => {
+ return get().notebooks.get(id)?.portalDisplay;
},
- run: (uid: string): void => { get().notebooks.get(uid)?.adapter?.commands.execute(cmdIds.run); },
- runAll: (uid: string): void => { get().notebooks.get(uid)?.adapter?.commands.execute(cmdIds.runAll); },
- interrupt: (uid: string): void => { get().notebooks.get(uid)?.adapter?.commands.execute(cmdIds.interrupt); },
+ run: (id: string): void => { get().notebooks.get(id)?.adapter?.commands.execute(cmdIds.run); },
+ runAll: (id: string): void => { get().notebooks.get(id)?.adapter?.commands.execute(cmdIds.runAll); },
+ interrupt: (id: string): void => { get().notebooks.get(id)?.adapter?.commands.execute(cmdIds.interrupt); },
insertAbove: (mutation: CellMutation) => {
- get().notebooks.get(mutation.uid)?.adapter?.setDefaultCellType(mutation.cellType);
- get().notebooks.get(mutation.uid)?.adapter?.insertAbove(mutation.source);
+ get().notebooks.get(mutation.id)?.adapter?.setDefaultCellType(mutation.cellType);
+ get().notebooks.get(mutation.id)?.adapter?.insertAbove(mutation.source);
},
insertBelow: (mutation: CellMutation) => {
- get().notebooks.get(mutation.uid)?.adapter?.setDefaultCellType(mutation.cellType);
- get().notebooks.get(mutation.uid)?.adapter?.insertBelow(mutation.source);
+ get().notebooks.get(mutation.id)?.adapter?.setDefaultCellType(mutation.cellType);
+ get().notebooks.get(mutation.id)?.adapter?.insertBelow(mutation.source);
},
- delete: (uid: string): void => { get().notebooks.get(uid)?.adapter?.commands.execute(cmdIds.deleteCells); },
+ delete: (id: string): void => { get().notebooks.get(id)?.adapter?.commands.execute(cmdIds.deleteCells); },
changeCellType: (mutation: CellMutation) => {
- get().notebooks.get(mutation.uid)?.adapter?.changeCellType(mutation.cellType);
+ get().notebooks.get(mutation.id)?.adapter?.changeCellType(mutation.cellType);
},
save: (mutation: DateMutation) => {
- get().notebooks.get(mutation.uid)?.adapter?.commands.execute(cmdIds.save);
+ get().notebooks.get(mutation.id)?.adapter?.commands.execute(cmdIds.save);
const notebooks = get().notebooks;
- const notebook = notebooks.get(mutation.uid);
+ const notebook = notebooks.get(mutation.id);
if (notebook) {
notebook.saveRequest = mutation.date;
set((state: NotebookState) => ({ notebooks }));
}
},
reset: () => set((state: NotebookState) => ({ notebooks: new Map() })),
- update: (update: UpdateUid) => {
+ update: (update: UpdateId) => {
const notebooks = get().notebooks;
- let notebook = notebooks.get(update.uid);
+ let notebook = notebooks.get(update.id);
if (notebook) {
notebook = { ...notebook, ...update.partialState };
set((state: NotebookState) => ({ notebooks }));
} else {
- notebooks.set(update.uid, {
+ notebooks.set(update.id, {
adapter: update.partialState.adapter,
portals: [],
});
set((state: NotebookState) => ({ notebooks }));
}
},
- activeCellChange: (cellModelUid: CellModelUid) => {
+ activeCellChange: (cellModelId: CellModelId) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(cellModelUid.uid);
+ const notebook = notebooks.get(cellModelId.id);
if (notebook) {
- notebook.activeCell = cellModelUid.cellModel;
+ notebook.activeCell = cellModelId.cellModel;
set((state: NotebookState) => ({ notebooks }));
}
},
- modelChange: (notebookModelUid: NotebookModelUid) => {
+ modelChange: (notebookModelId: NotebookModelId) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(notebookModelUid.uid);
+ const notebook = notebooks.get(notebookModelId.id);
if (notebook) {
- notebook.model = notebookModelUid.notebookModel;
+ notebook.model = notebookModelId.notebookModel;
set((state: NotebookState) => ({ notebooks }));
}
},
- notebookChange: (notebookChangeUid: NotebookChangeUid) => {
+ notebookChange: (notebookChangeId: NotebookChangeId) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(notebookChangeUid.uid);
+ const notebook = notebooks.get(notebookChangeId.id);
if (notebook) {
- notebook.notebookChange = notebookChangeUid.notebookChange;
+ notebook.notebookChange = notebookChangeId.notebookChange;
set((state: NotebookState) => ({ notebooks }));
}
},
- kernelStatusChanged: (kernelStatusUid: KernelStatusMutation) => {
+ kernelStatusChanged: (kernelStatusId: KernelStatusMutation) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(kernelStatusUid.uid);
+ const notebook = notebooks.get(kernelStatusId.id);
if (notebook) {
- notebook.kernelStatus = kernelStatusUid.kernelStatus;
+ notebook.kernelStatus = kernelStatusId.kernelStatus;
set((state: NotebookState) => ({ notebooks }));
}
},
changeKernel: (kernelChange: KernelChangeMutation) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(kernelChange.uid);
+ const notebook = notebooks.get(kernelChange.id);
if (notebook) {
notebook.adapter?.assignKernel(kernelChange.kernel);
set((state: NotebookState) => ({ notebooks }));
}
},
- addPortals: (portalsUid: ReactPortalsMutation) => {
+ addPortals: (portalsId: ReactPortalsMutation) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(portalsUid.uid);
+ const notebook = notebooks.get(portalsId.id);
if (notebook) {
- notebook.portals = notebook.portals.concat(portalsUid.portals);
+ notebook.portals = notebook.portals.concat(portalsId.portals);
set((state: NotebookState) => ({ notebooks }));
}
},
- dispose: (uid: string): void => {
+ dispose: (id: string): void => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(uid);
+ const notebook = notebooks.get(id);
if(notebook){
notebook.adapter?.dispose();
- notebooks.delete(uid);
+ notebooks.delete(id);
}
set((state: NotebookState) => ({ notebooks }));
},
- setPortals: (portalsUid: ReactPortalsMutation) => {
+ setPortals: (portalsId: ReactPortalsMutation) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(portalsUid.uid);
+ const notebook = notebooks.get(portalsId.id);
if (notebook) {
- notebook.portals = portalsUid.portals;
+ notebook.portals = portalsId.portals;
set((state: NotebookState) => ({ notebooks }));
}
},
- setPortalDisplay: (portalDisplayUid: PortalDisplayMutation) => {
+ setPortalDisplay: (portalDisplayId: PortalDisplayMutation) => {
const notebooks = get().notebooks;
- const notebook = notebooks.get(portalDisplayUid.uid);
+ const notebook = notebooks.get(portalDisplayId.id);
if (notebook) {
- notebook.portalDisplay = portalDisplayUid.portalDisplay;
+ notebook.portalDisplay = portalDisplayId.portalDisplay;
set((state: NotebookState) => ({ notebooks }));
}
},
diff --git a/packages/react/src/components/notebook/cell/metadata/CellMetadataEditor.tsx b/packages/react/src/components/notebook/cell/metadata/CellMetadataEditor.tsx
index 829e56d4..ec75ba00 100644
--- a/packages/react/src/components/notebook/cell/metadata/CellMetadataEditor.tsx
+++ b/packages/react/src/components/notebook/cell/metadata/CellMetadataEditor.tsx
@@ -4,11 +4,12 @@
* MIT License
*/
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
import { ActionList, TextInput } from '@primer/react';
import { CheckIcon } from '@primer/octicons-react';
import { Cell, ICellModel } from '@jupyterlab/cells';
import NbGraderType, { getNbGraderType } from './NbGraderCells';
+import { newUlid } from '../../../../utils';
type Props = {
notebookId: string;
@@ -19,17 +20,23 @@ type Props = {
export const CellMetadataEditor = (props: Props) => {
const { cell } = props;
const [cellGradeType, setCellGradeType] = useState(getNbGraderType(cell));
- const [nbg, setNbg] = useState(
- cell.model.getMetadata('nbgrader') || { grade_id: '', points: 0 }
+ const [nbGrade, setNbGrade] = useState<{grade_id: string; points: number}>(
+ cell.model.getMetadata('nbgrader') ?? { grade_id: newUlid(), points: 1 }
);
+ useEffect(() => {
+ setNbGrade({
+ grade_id: nbGrade.grade_id ?? newUlid(),
+ points: nbGrade.points ?? 1,
+ });
+ }, [nbGrade]);
const handleGradeIdChange = (cell: Cell, gradeId: string) => {
const nbgrader = cell.model.getMetadata('nbgrader') as any;
cell.model.setMetadata('nbgrader', {
...nbgrader,
grade_id: gradeId,
});
- setNbg({
- ...nbg,
+ setNbGrade({
+ ...nbGrade,
grade_id: gradeId,
});
};
@@ -41,8 +48,8 @@ export const CellMetadataEditor = (props: Props) => {
...nbgrader,
points: points_number,
});
- setNbg({
- ...nbg,
+ setNbGrade({
+ ...nbGrade,
points: points_number,
});
}
@@ -65,6 +72,8 @@ export const CellMetadataEditor = (props: Props) => {
solution: true,
locked: false,
task: false,
+ grade_id: newUlid(),
+ points: 1,
});
setCellGradeType(NbGraderType.AutogradedAnswer);
break;
@@ -77,6 +86,8 @@ export const CellMetadataEditor = (props: Props) => {
solution: false,
locked: false,
task: false,
+ grade_id: newUlid(),
+ points: 1,
});
setCellGradeType(NbGraderType.AutogradedTest);
break;
@@ -89,6 +100,8 @@ export const CellMetadataEditor = (props: Props) => {
solution: true,
locked: false,
task: false,
+ grade_id: newUlid(),
+ points: 1,
});
setCellGradeType(NbGraderType.ManuallyGradedAnswer);
break;
@@ -102,6 +115,8 @@ export const CellMetadataEditor = (props: Props) => {
solution: false,
locked: true,
task: true,
+ grade_id: newUlid(),
+ points: 1,
});
setCellGradeType(NbGraderType.ManuallyGradedTask);
break;
@@ -217,7 +232,7 @@ export const CellMetadataEditor = (props: Props) => {
{
{
e.preventDefault();
handleGradeIdChange(cell, e.target.value);
@@ -230,7 +245,7 @@ export const CellMetadataEditor = (props: Props) => {
{
{
e.preventDefault();
handlePointsChange(cell, e.target.value);
diff --git a/packages/react/src/components/notebook/cell/metadata/NbGraderCells.tsx b/packages/react/src/components/notebook/cell/metadata/NbGraderCells.tsx
index c7f7ed14..a9fa8f7e 100644
--- a/packages/react/src/components/notebook/cell/metadata/NbGraderCells.tsx
+++ b/packages/react/src/components/notebook/cell/metadata/NbGraderCells.tsx
@@ -84,14 +84,14 @@ export const getNbGraderType = (cell: Cell) => {
if (!grade && solution && !locked) {
return NbGraderType.AutogradedAnswer;
}
- // Autograded test (only for code cells) { grade: true, solution: false, locked: false, points: ... }
- if (grade && !solution && !locked) {
- return NbGraderType.AutogradedTest;
- }
// Manually graded task { grade: false, solution: false, locked: true, task: true, points: ... }
if (!grade && !solution && locked && task) {
return NbGraderType.ManuallyGradedTask;
}
+ // Autograded test (only for code cells) { grade: true, solution: false, locked: true, points: ... }
+ if (grade && !solution && locked) {
+ return NbGraderType.AutogradedTest;
+ }
// Manually graded answer { grade: true, solution: true, locked: false, points: ... }
if (grade && solution && !locked) {
return NbGraderType.ManuallyGradedAnswer;
diff --git a/packages/react/src/components/notebook/cell/sidebar/CellSidebar.tsx b/packages/react/src/components/notebook/cell/sidebar/CellSidebar.tsx
index 035cf8b7..7deecb23 100644
--- a/packages/react/src/components/notebook/cell/sidebar/CellSidebar.tsx
+++ b/packages/react/src/components/notebook/cell/sidebar/CellSidebar.tsx
@@ -92,7 +92,7 @@ export const CellSidebar = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertAbove({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
}}
@@ -109,7 +109,7 @@ export const CellSidebar = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertAbove({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -127,7 +127,7 @@ export const CellSidebar = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.changeCellType({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -143,7 +143,7 @@ export const CellSidebar = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.changeCellType({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
}}
@@ -161,7 +161,7 @@ export const CellSidebar = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -178,7 +178,7 @@ export const CellSidebar = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
}}
diff --git a/packages/react/src/components/notebook/cell/sidebar/CellSidebarButton.tsx b/packages/react/src/components/notebook/cell/sidebar/CellSidebarButton.tsx
index c0ed6751..96f39119 100644
--- a/packages/react/src/components/notebook/cell/sidebar/CellSidebarButton.tsx
+++ b/packages/react/src/components/notebook/cell/sidebar/CellSidebarButton.tsx
@@ -73,7 +73,7 @@ export const CellSidebarNew = (props: CellSidebarProps) => {
onClick={e => {
e.preventDefault();
notebookStore.insertAbove({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
}}
@@ -90,7 +90,7 @@ export const CellSidebarNew = (props: CellSidebarProps) => {
onClick={e => {
e.preventDefault();
notebookStore.insertAbove({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -109,7 +109,7 @@ export const CellSidebarNew = (props: CellSidebarProps) => {
onClick={e => {
e.preventDefault();
notebookStore.changeCellType({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -124,7 +124,7 @@ export const CellSidebarNew = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.changeCellType({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
}}
@@ -140,7 +140,7 @@ export const CellSidebarNew = (props: CellSidebarProps) => {
onClick={e => {
e.preventDefault();
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -157,7 +157,7 @@ export const CellSidebarNew = (props: CellSidebarProps) => {
onClick={e => {
e.preventDefault();
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
}}
diff --git a/packages/react/src/components/notebook/cell/sidebar/CellSidebarWidget.tsx b/packages/react/src/components/notebook/cell/sidebar/CellSidebarWidget.tsx
index e5c715b6..26ab666c 100644
--- a/packages/react/src/components/notebook/cell/sidebar/CellSidebarWidget.tsx
+++ b/packages/react/src/components/notebook/cell/sidebar/CellSidebarWidget.tsx
@@ -48,7 +48,7 @@ export class CellSidebarWidget
);
const portal = createPortal(portalDiv, this.node);
notebookStore.getState().addPortals({
- uid: notebookId,
+ id: notebookId,
portals: [portal],
});
}
diff --git a/packages/react/src/components/output/Output.tsx b/packages/react/src/components/output/Output.tsx
index 8d43b274..3dc2c8d7 100644
--- a/packages/react/src/components/output/Output.tsx
+++ b/packages/react/src/components/output/Output.tsx
@@ -6,18 +6,18 @@
import { useState, useEffect } from 'react';
import { Box } from '@primer/react';
-import { UUID } from '@lumino/coreutils';
import { IOutput } from '@jupyterlab/nbformat';
import { IOutputAreaModel } from '@jupyterlab/outputarea';
import { KernelMessage } from '@jupyterlab/services';
+import { newUuid } from '../../utils';
import { useJupyter } from '../../jupyter/JupyterContext';
-import Kernel from '../../jupyter/kernel/Kernel';
+import { Kernel } from '../../jupyter/kernel/Kernel';
import { KernelActionMenu, KernelProgressBar } from './../kernel';
-import Lumino from '../lumino/Lumino';
-import CodeMirrorEditor from '../codemirror/CodeMirrorEditor';
-import OutputAdapter from './OutputAdapter';
-import OutputRenderer from './OutputRenderer';
-import { useOutputStore } from './OutputState';
+import { Lumino } from '../lumino/Lumino';
+import { CodeMirrorEditor } from '../codemirror/CodeMirrorEditor';
+import { OutputAdapter } from './OutputAdapter';
+import { OutputRenderer } from './OutputRenderer';
+import { useOutputsStore } from './OutputState';
import './Output.css';
@@ -29,8 +29,9 @@ export type IOutputProps = {
codePre?: string;
disableRun: boolean;
executeTrigger: number;
+ id: string;
insertText?: (payload?: any) => string;
- kernel: Kernel;
+ kernel?: Kernel;
lumino: boolean;
model?: IOutputAreaModel;
outputs?: IOutput[];
@@ -38,13 +39,12 @@ export type IOutputProps = {
showControl?: boolean;
showEditor: boolean;
showKernelProgressBar?: boolean;
- sourceId: string;
toolbarPosition: 'up' | 'middle' | 'none';
};
export const Output = (props: IOutputProps) => {
- const { defaultKernel: kernel } = useJupyter();
- const outputStore = useOutputStore();
+ const { defaultKernel } = useJupyter();
+ const outputStore = useOutputsStore();
const {
adapter: propsAdapter,
autoRun,
@@ -54,60 +54,72 @@ export const Output = (props: IOutputProps) => {
disableRun,
executeTrigger,
insertText,
+ kernel: propsKernel,
lumino,
model,
+ outputs: propsOutputs,
receipt,
showControl,
showEditor,
showKernelProgressBar = true,
- sourceId,
+ id: sourceId,
toolbarPosition,
} = props;
+ const kernel = propsKernel ?? defaultKernel;
const [id, setId] = useState(sourceId);
- const [kernelStatus, setKernelStatus] =
- useState('unknown');
- const [outputs, setOutputs] = useState(props.outputs);
+ const [kernelStatus, setKernelStatus] = useState('unknown');
+ const [outputs, setOutputs] = useState(propsOutputs);
const [adapter, setAdapter] = useState();
useEffect(() => {
if (!id) {
- setId(UUID.uuid4());
+ setId(newUuid());
}
}, []);
useEffect(() => {
- if (id && kernel) {
- const adapter =
- propsAdapter ?? new OutputAdapter(kernel, outputs ?? [], model);
- if (receipt) {
- adapter.outputArea.model.changed.connect((sender, change) => {
- if (change.type === 'add') {
- change.newValues.map(val => {
- if (val && val.data) {
- const out = val.data['text/html']; // val.data['application/vnd.jupyter.stdout'];
- if (out) {
- if ((out as string).indexOf(receipt) > -1) {
- outputStore.setGrade({
- sourceId,
- success: true,
- });
- }
- }
+ const outputsCallback = (model: IOutputAreaModel, change: IOutputAreaModel.ChangedArgs) => {
+ setOutputs(model.toJSON());
+ if (id) {
+ outputStore.setModel(id, model);
+ }
+ };
+ const receiptCallback = (model: IOutputAreaModel, change: IOutputAreaModel.ChangedArgs) => {
+ if (receipt && change.type === 'add') {
+ change.newValues.map(val => {
+ if (val && val.data) {
+ const out = val.data['text/html']; // val.data['application/vnd.jupyter.stdout'];
+ if (out) {
+ if ((out as string).indexOf(receipt) > -1) {
+ outputStore.setGradeSuccess(sourceId, true)
}
- });
+ }
}
});
}
+ };
+ if (id && kernel) {
+ const adapter = propsAdapter ?? new OutputAdapter(id, kernel, outputs ?? [], model);
setAdapter(adapter);
- outputStore.setAdapter(sourceId, adapter);
- adapter.outputArea.model.changed.connect((outputModel, args) => {
- setOutputs(outputModel.toJSON());
- });
+ outputStore.setAdapter(id, adapter);
+ if (model) {
+ outputStore.setModel(id, model);
+ }
+ if (code) {
+ outputStore.setInput(id,code);
+ }
+ adapter.outputArea.model.changed.connect(outputsCallback);
+ if (receipt) {
+ adapter.outputArea.model.changed.connect(receiptCallback)
+ }
+ }
+ return () => {
+ if (adapter) {
+ adapter.outputArea.model.changed.disconnect(outputsCallback);
+ adapter.outputArea.model.changed.disconnect(receiptCallback)
+ }
}
}, [id, kernel]);
useEffect(() => {
if (adapter) {
- if (!adapter.kernel) {
- adapter.kernel = kernel;
- }
if (autoRun) {
adapter.execute(code);
}
@@ -126,10 +138,10 @@ export const Output = (props: IOutputProps) => {
};
}
}, [kernel]);
- const executeRequest = outputStore.getExecute(sourceId);
+ const executeRequest = outputStore.getExecuteRequest(sourceId);
useEffect(() => {
- if (adapter && executeRequest && executeRequest.sourceId === id) {
- adapter.execute(executeRequest.source);
+ if (adapter && executeRequest && executeRequest === id) {
+ adapter.execute(code);
}
}, [executeRequest, adapter]);
useEffect(() => {
@@ -214,6 +226,7 @@ export const Output = (props: IOutputProps) => {
};
Output.defaultProps = {
+ autoRun: false,
clearTrigger: 0,
disableRun: false,
executeTrigger: 0,
diff --git a/packages/react/src/components/output/OutputAdapter.ts b/packages/react/src/components/output/OutputAdapter.ts
index 0304a48f..36c50214 100755
--- a/packages/react/src/components/output/OutputAdapter.ts
+++ b/packages/react/src/components/output/OutputAdapter.ts
@@ -5,27 +5,18 @@
*/
import { IOutput } from '@jupyterlab/nbformat';
-import {
- IOutputAreaModel,
- OutputArea,
- OutputAreaModel,
-} from '@jupyterlab/outputarea';
-import {
- IRenderMime,
- RenderMimeRegistry,
- standardRendererFactories,
-} from '@jupyterlab/rendermime';
+import { IOutputAreaModel, OutputArea, OutputAreaModel } from '@jupyterlab/outputarea';
+import { IRenderMime, RenderMimeRegistry, standardRendererFactories } from '@jupyterlab/rendermime';
import { rendererFactory as jsonRendererFactory } from '@jupyterlab/json-extension';
import { rendererFactory as javascriptRendererFactory } from '@jupyterlab/javascript-extension';
-import {
- WIDGET_MIMETYPE,
- WidgetRenderer,
-} from '@jupyter-widgets/html-manager/lib/output_renderers';
+import { WIDGET_MIMETYPE, WidgetRenderer } from '@jupyter-widgets/html-manager/lib/output_renderers';
import { requireLoader as loader } from '../../jupyter/ipywidgets/libembed-amd';
import { ClassicWidgetManager } from '../../jupyter/ipywidgets/classic/manager';
-import Kernel from '../../jupyter/kernel/Kernel';
+import { Kernel } from '../../jupyter/kernel/Kernel';
+import { execute } from './OutputExecutor';
export class OutputAdapter {
+ private _id: string;
private _kernel?: Kernel;
private _renderers: IRenderMime.IRendererFactory[];
private _outputArea: OutputArea;
@@ -33,10 +24,12 @@ export class OutputAdapter {
private _iPyWidgetsClassicManager: ClassicWidgetManager;
public constructor(
+ id: string,
kernel?: Kernel,
outputs?: IOutput[],
outputAreaModel?: IOutputAreaModel
) {
+ this._id = id;
this._kernel = kernel;
this._renderers = standardRendererFactories.filter(
factory => factory.mimeTypes[0] !== 'text/javascript'
@@ -89,8 +82,8 @@ export class OutputAdapter {
public async execute(code: string) {
if (this._kernel) {
this.clear();
- await this._kernel?.execute(code, { model: this._outputArea.model })
- ?.done;
+ const done = execute(this._id, code, this._outputArea, this._kernel);
+ await done;
}
}
diff --git a/packages/react/src/components/output/OutputExecutor.ts b/packages/react/src/components/output/OutputExecutor.ts
new file mode 100755
index 00000000..66d34531
--- /dev/null
+++ b/packages/react/src/components/output/OutputExecutor.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021-2023 Datalayer, Inc.
+ *
+ * MIT License
+ */
+
+import { JSONObject } from '@lumino/coreutils';
+import { KernelMessage } from '@jupyterlab/services';
+import { OutputArea } from '@jupyterlab/outputarea';
+import { Kernel } from './../../jupyter/kernel/Kernel';
+
+/**
+ * Execute code on an output area.
+ */
+export async function execute(
+ id: string,
+ code: string,
+ output: OutputArea,
+ kernel: Kernel,
+ metadata?: JSONObject
+): Promise {
+ // Override the default for `stop_on_error`.
+ let stopOnError = true;
+ if (
+ metadata &&
+ Array.isArray(metadata.tags) &&
+ metadata.tags.indexOf('raises-exception') !== -1
+ ) {
+ stopOnError = false;
+ }
+ const kernelExecutor = kernel.execute(
+ code,
+ {
+ model: output.model,
+ stopOnError,
+ }
+ );
+ const future = kernelExecutor!.future;
+ // TODO fix in upstream jupyterlab if possible...
+ (output as any)._onIOPub = future!.onIOPub;
+ (output as any)._onExecuteReply = future!.onReply;
+ output.future = future!;
+ return future?.done;
+}
diff --git a/packages/react/src/components/output/OutputState.ts b/packages/react/src/components/output/OutputState.ts
index 5232c6d5..c2024dcf 100644
--- a/packages/react/src/components/output/OutputState.ts
+++ b/packages/react/src/components/output/OutputState.ts
@@ -6,38 +6,16 @@
import { createStore } from 'zustand/vanilla';
import { useStore } from 'zustand';
-import OutputAdapter from './OutputAdapter';
-
-export namespace OutputState {
- export type ISource = {
- sourceId: string;
- source: string;
- increment?: number;
- };
- export type IDataset = {
- sourceId: string;
- dataset: any;
- increment?: number;
- };
- export type IExecute = {
- sourceId: string;
- source: string;
- increment?: number;
- };
- export type IGrade = {
- sourceId: string;
- success: boolean;
- increment?: number;
- };
-}
+import { IOutputAreaModel } from '@jupyterlab/outputarea';
+import { OutputAdapter } from './OutputAdapter';
export type IOutputState = {
adapter?: OutputAdapter;
- source?: OutputState.ISource;
- dataset?: OutputState.IDataset;
- setSource?: OutputState.ISource;
- execute?: OutputState.IExecute;
- grade?: OutputState.IGrade;
+ model?: IOutputAreaModel;
+ input?: string;
+ dataset?: any;
+ code?: string;
+ gradeSuccess?: boolean;
};
export interface IOutputsState {
@@ -45,22 +23,42 @@ export interface IOutputsState {
}
export type OutputState = IOutputsState & {
- setOutputs: (outputs: Map) => void;
- setAdapter: (id: string, adapter: OutputAdapter) => void;
- setDataset: (dataset: OutputState.IDataset) => void;
- setExecute: (execute: OutputState.IExecute) => void;
- setSource: (source: OutputState.ISource) => void;
- setGrade: (grade: OutputState.IGrade) => void;
getAdapter: (id: string) => OutputAdapter | undefined;
- getSource: (id: string) => OutputState.ISource | undefined;
- getDataset: (id: string) => OutputState.IDataset | undefined;
- getExecute: (id: string) => OutputState.IExecute | undefined;
- getGrade: (id: string) => OutputState.IGrade | undefined;
+ getDataset: (id: string) => any | undefined;
+ getExecuteRequest: (id: string) => string | undefined;
+ getGradeSuccess: (id: string) => boolean | undefined;
+ getInput: (id: string) => string | undefined;
+ getModel: (id: string) => IOutputAreaModel | undefined;
+ setAdapter: (id: string, adapter: OutputAdapter) => void;
+ setDataset: (id: string, dataset: any) => void;
+ setExecuteRequest: (id: string, code: string) => void;
+ setGradeSuccess: (id: string, gradeSuccess: boolean) => void;
+ setInput: (id: string, source: string) => void;
+ setModel: (id: string, output: IOutputAreaModel) => void;
+ setOutputs: (id: string, outputs: Map) => void;
};
-export const outputStore = createStore((set, get) => ({
+export const outputsStore = createStore((set, get) => ({
outputs: new Map(),
- setOutputs: (outputs: Map) => set((state: OutputState) => ({ outputs })),
+ getAdapter: (id: string) => {
+ return get().outputs.get(id)?.adapter;
+ },
+ getInput: (id: string): string | undefined => {
+ return get().outputs.get(id)?.input;
+ },
+ getModel: (id: string): IOutputAreaModel | undefined => {
+ return get().outputs.get(id)?.model;
+ },
+ getDataset: (id: string): any | undefined => {
+ return get().outputs.get(id)?.dataset;
+ },
+ getExecuteRequest: (id: string): string | undefined => {
+ return get().outputs.get(id)?.code;
+ },
+ getGradeSuccess: (id: string): boolean | undefined => {
+ return get().outputs.get(id)?.gradeSuccess;
+ },
+ setOutputs: (id: string, outputs: Map) => set((state: OutputState) => ({ outputs })),
setAdapter: (id: string, adapter: OutputAdapter) => {
const outputs = get().outputs;
const d = outputs.get(id);
@@ -71,71 +69,62 @@ export const outputStore = createStore((set, get) => ({
}
set((state: OutputState) => ({ outputs }))
},
- setDataset: (dataset: OutputState.IDataset) => {
- const sourceId = dataset.sourceId;
+ setDataset: (id: string, dataset: string) => {
const outputs = get().outputs;
- const d = outputs.get(sourceId);
+ const d = outputs.get(id);
if (d) {
d.dataset = dataset;
} else {
- outputs.set(sourceId, { dataset });
+ outputs.set(id, { dataset });
}
set((state: OutputState) => ({ outputs }))
},
- setExecute: (execute: OutputState.IExecute) => {
- const sourceId = execute.sourceId;
+ setExecuteRequest: (id: string, code: string) => {
const outputs = get().outputs;
- const e = outputs.get(sourceId);
+ const e = outputs.get(id);
if (e) {
- e.execute = execute;
+ e.code = code;
} else {
- outputs.set(sourceId, { execute });
+ outputs.set(id, { code });
}
set((state: OutputState) => ({ outputs }))
},
- setSource: (setSource: OutputState.ISource) => {
- const sourceId = setSource.sourceId;
+ setModel: (id: string, model: IOutputAreaModel) => {
const outputs = get().outputs;
- const s = outputs.get(sourceId);
- if (s) {
- s.setSource = setSource;
+ const e = outputs.get(id);
+ if (e) {
+ e.model = model;
} else {
- outputs.set(sourceId, { setSource });
+ outputs.set(id, { model });
}
set((state: OutputState) => ({ outputs }))
},
- setGrade: (grade: OutputState.IGrade) => {
- const sourceId = grade.sourceId;
+ setInput: (id: string, input: string) => {
const outputs = get().outputs;
- const g = outputs.get(sourceId);
- if (g) {
- g.grade = grade;
+ const e = outputs.get(id);
+ if (e) {
+ e.input = input;
} else {
- outputs.set(sourceId, { grade });
+ outputs.set(id, { input });
}
set((state: OutputState) => ({ outputs }))
},
- getAdapter: (id: string) => {
- return get().outputs.get(id)?.adapter;
- },
- getSource: (id: string): OutputState.ISource | undefined => {
- return get().outputs.get(id)?.source;
- },
- getDataset: (id: string): OutputState.IDataset | undefined => {
- return get().outputs.get(id)?.dataset;
- },
- getExecute: (id: string): OutputState.IExecute | undefined => {
- return get().outputs.get(id)?.execute;
- },
- getGrade: (id: string): OutputState.IGrade | undefined => {
- return get().outputs.get(id)?.grade;
+ setGradeSuccess: (id: string, gradeSuccess: boolean) => {
+ const outputs = get().outputs;
+ const e = outputs.get(id);
+ if (e) {
+ e.gradeSuccess = gradeSuccess;
+ } else {
+ outputs.set(id, { gradeSuccess });
+ }
+ set((state: OutputState) => ({ outputs }))
},
}));
-export function useOutputStore(): OutputState;
-export function useOutputStore(selector: (state: OutputState) => T): T;
-export function useOutputStore(selector?: (state: OutputState) => T) {
- return useStore(outputStore, selector!);
+export function useOutputsStore(): OutputState;
+export function useOutputsStore(selector: (state: OutputState) => T): T;
+export function useOutputsStore(selector?: (state: OutputState) => T) {
+ return useStore(outputsStore, selector!);
}
-export default useOutputStore;
+export default useOutputsStore;
diff --git a/packages/react/src/examples/All.tsx b/packages/react/src/examples/All.tsx
index 663b7332..2f8de0af 100644
--- a/packages/react/src/examples/All.tsx
+++ b/packages/react/src/examples/All.tsx
@@ -19,16 +19,16 @@ import Terminal from '../components/terminal/Terminal';
import CellSidebarNew from '../components/notebook/cell/sidebar/CellSidebarButton';
import CellSidebar from '../components/notebook/cell/sidebar/CellSidebar';
import Console from '../components/console/Console';
-import { useCellStore } from '../components/cell/CellState';
+import { useCellsStore } from '../components/cell/CellState';
import useNotebookStore from '../components/notebook/NotebookState';
import notebook from './notebooks/NotebookExample1.ipynb.json';
const SOURCE_1 = '1+1';
-const NOTEBOOK_UID_1 = 'notebook-1-uid';
-const NOTEBOOK_UID_2 = 'notebook-2-uid';
-const NOTEBOOK_UID_3 = 'notebook-3-uid';
+const NOTEBOOK_ID_1 = 'notebook-1-id';
+const NOTEBOOK_ID_2 = 'notebook-2-id';
+const NOTEBOOK_ID_3 = 'notebook-3-id';
const SOURCE_1_OUTPUTS: IOutput[] = [
{
@@ -54,18 +54,18 @@ interface ICellToolProps {
}
const CellPreview = (props: ICellToolProps) => {
- const cellStore = useCellStore();
+ const cellsStore = useCellsStore();
return (
<>
- <>source: {cellStore.getSource(props.id)}>
- <>kernel available: {String(cellStore.getIsKernelSessionAvailable(props.id))}>
+ <>source: {cellsStore.getSource(props.id)}>
+ <>kernel available: {String(cellsStore.isKernelSessionAvailable(props.id))}>
>
);
};
const CellToolbar = (props: ICellToolProps) => {
const {id} = props;
- const cellStore = useCellStore();
+ const cellsStore = useCellsStore();
return (
<>
@@ -73,20 +73,20 @@ const CellToolbar = (props: ICellToolProps) => {
- Outputs count: {cellStore.getOutputsCount(id)}
+ Outputs count: {cellsStore.getOutputsCount(id)}
>
);
};
@@ -101,7 +101,7 @@ const NotebookToolbar = () => {
size="small"
onClick={() =>
notebookStore.save({
- uid: NOTEBOOK_UID_1,
+ id: NOTEBOOK_ID_1,
date: new Date(),
})
}
@@ -112,7 +112,7 @@ const NotebookToolbar = () => {
variant="default"
size="small"
onClick={() =>
- notebookStore.runAll(NOTEBOOK_UID_1)
+ notebookStore.runAll(NOTEBOOK_ID_1)
}
>
Run all
@@ -131,12 +131,11 @@ const NotebookKernelChange = () => {
kernelManager,
kernelName: 'defaultKernel',
kernelSpecName: 'python',
- kernelType: 'notebook',
kernelspecsManager: serviceManager.kernelspecs,
sessionManager: serviceManager.sessions,
});
kernel.ready.then(() => {
- notebookStore.changeKernel({ uid: NOTEBOOK_UID_2, kernel });
+ notebookStore.changeKernel({ id: NOTEBOOK_ID_2, kernel });
alert('Kernel is changed.');
});
}
@@ -153,7 +152,7 @@ const NotebookKernelChange = () => {
>
);
@@ -195,7 +194,7 @@ root.render(
diff --git a/packages/react/src/examples/Bokeh.tsx b/packages/react/src/examples/Bokeh.tsx
index 7d92dcb6..f2aa2df7 100644
--- a/packages/react/src/examples/Bokeh.tsx
+++ b/packages/react/src/examples/Bokeh.tsx
@@ -14,7 +14,7 @@ const Bokeh = () => (
(
{
- const cellStore = useJupyterStore().cellStore();
- const cellId = 'cell-1'
-
- console.log('Cell Outputs', (cellStore.getAdapter(cellId)?.cell as CodeCell).outputArea.model.toJSON());
+ const { defaultKernel } = useJupyter();
+ const cellsStore = useCellsStore();
+ const kernelsStore = useKernelsStore();
+ console.log('Cell Outputs', (cellsStore.getAdapter(CELL_ID)?.cell as CodeCell)?.outputArea.model.toJSON());
return (
A Jupyter Cell
- Outputs Count: {cellStore.getOutputsCount(cellId)}
+ Source: {cellsStore.getSource(CELL_ID)}
+
+
+ Outputs Count: {cellsStore.getOutputsCount(CELL_ID)}
+
+ defaultKernel
+ Kernel State: {defaultKernel && kernelsStore.getExecutionState(defaultKernel.id)}
- Source: {cellStore.getSource(cellId)}
+ Kernel Phase: {defaultKernel && kernelsStore.getExecutionPhase(defaultKernel.id)}
-
+
-
+
+
+
+
| |
)
}
+const div = document.createElement('div');
+document.body.appendChild(div);
+const root = createRoot(div);
+
root.render();
diff --git a/packages/react/src/examples/Cells.tsx b/packages/react/src/examples/Cells.tsx
index 036a48e2..08f18795 100644
--- a/packages/react/src/examples/Cells.tsx
+++ b/packages/react/src/examples/Cells.tsx
@@ -16,10 +16,9 @@ const root = createRoot(div);
root.render(
Jupyter Cells wrapped in a single Jupyter Context
- |
- |
- |
+ |
+ |
diff --git a/packages/react/src/examples/Dashboard.tsx b/packages/react/src/examples/Dashboard.tsx
index 17972ad3..4ad6870d 100644
--- a/packages/react/src/examples/Dashboard.tsx
+++ b/packages/react/src/examples/Dashboard.tsx
@@ -17,7 +17,7 @@ const Dashboard = () => (
(
(
(
(
(
{
+export const KernelExecuteView = () => {
const { defaultKernel } = useJupyter();
const [running, setRunning] = useState(false);
const [code, setCode] = useState('');
@@ -72,10 +72,10 @@ export const KernelExecResultView = () => {
);
};
-const KernelExecResult = () => {
+const KernelExecute = () => {
return (
-
+
);
};
@@ -84,4 +84,4 @@ const div = document.createElement('div');
document.body.appendChild(div);
const root = createRoot(div);
-root.render();
+root.render();
diff --git a/packages/react/src/examples/KernelExecutor.tsx b/packages/react/src/examples/KernelExecutor.tsx
index ef583895..62e9c43d 100644
--- a/packages/react/src/examples/KernelExecutor.tsx
+++ b/packages/react/src/examples/KernelExecutor.tsx
@@ -36,14 +36,14 @@ const KernelExecutorView = () => {
msg: KernelMessage.IIOPubMessage
) => {
// Do something with the IOPub message.
- console.debug('---iopubMessage', msg);
+ console.log('---iopubMessage', msg);
return true;
};
const shellMessageHook: ShellMessageHook = (
msg: KernelMessage.IShellControlMessage
) => {
- // Do something with the IOPub message.
- console.debug('---shellMessage', msg);
+ // Do something with the Shell message.
+ console.log('---shellMessage', msg);
return true;
};
const kernelExecutor = defaultKernel.execute(CODE, {
diff --git a/packages/react/src/examples/Matplotlib.tsx b/packages/react/src/examples/Matplotlib.tsx
index 3f2cb09e..9815f373 100644
--- a/packages/react/src/examples/Matplotlib.tsx
+++ b/packages/react/src/examples/Matplotlib.tsx
@@ -19,7 +19,7 @@ const Matplotlib = () => (
(
{
kernelManager,
kernelName: JUPYTER_KERNEL_NAME,
kernelSpecName: JUPYTER_KERNEL_NAME,
- kernelType: 'notebook',
kernelspecsManager: serviceManager.kernelspecs,
sessionManager: serviceManager.sessions,
});
@@ -67,7 +66,7 @@ const NotebookInit: React.FC = () => {
// console.log("You can use one of these commands:", notebook.adapter?.commands.listCommands());
// notebook.adapter?.commands.execute("notebook:run-all");
notebookStore.insertAbove({
- uid: NOTEBOOK_ID,
+ id: NOTEBOOK_ID,
cellType: 'code',
source: 'print("Hello 🪐 ⚛️ Jupyter React")',
});
@@ -78,7 +77,7 @@ const NotebookInit: React.FC = () => {
return kernel ? (
{
kernelManager,
kernelName: NEW_KERNEL_NAME,
kernelSpecName: NEW_KERNEL_NAME,
- kernelType: 'notebook',
kernelspecsManager: serviceManager.kernelspecs,
sessionManager: serviceManager.sessions,
});
kernel.ready.then(() => {
- notebookStore.changeKernel({ uid: NOTEBOOK_UID, kernel });
+ notebookStore.changeKernel({ id: NOTEBOOK_ID, kernel });
alert(
`The kernel is changed (was python3, now ${NEW_KERNEL_NAME}). Bummer, all your variables are lost!`
);
@@ -48,7 +47,7 @@ const NotebookKernelChange = () => {
>
diff --git a/packages/react/src/examples/NotebookLite.tsx b/packages/react/src/examples/NotebookLite.tsx
index 9541ae1e..eab0cb8f 100644
--- a/packages/react/src/examples/NotebookLite.tsx
+++ b/packages/react/src/examples/NotebookLite.tsx
@@ -21,7 +21,7 @@ const NotebookLite = () => (
A Jupyter Notebook with a Lite Kernel
(
{
const notebookStore = useNotebookStore();
@@ -24,7 +24,7 @@ const NotebookNbFormatChange = () => {
const changeModel = () => {
console.log(
'Notebook NbFormat from store',
- notebookStore.notebooks.get(NOTEBOOK_UID)?.model?.toJSON() as INotebookContent
+ notebookStore.notebooks.get(NOTEBOOK_ID)?.model?.toJSON() as INotebookContent
);
nbformat === nbformat1 ? setNbformat(nbformat2) : setNbformat(nbformat1);
};
@@ -38,7 +38,7 @@ const NotebookNbFormatChange = () => {
(
{
>
diff --git a/packages/react/src/examples/NotebookSkeleton.tsx b/packages/react/src/examples/NotebookSkeleton.tsx
index 81ac39f6..d9671b0f 100644
--- a/packages/react/src/examples/NotebookSkeleton.tsx
+++ b/packages/react/src/examples/NotebookSkeleton.tsx
@@ -11,7 +11,7 @@ import Notebook from '../components/notebook/Notebook';
import NotebookToolbar from './toolbars/NotebookToolbar';
import CellSidebar from '../components/notebook/cell/sidebar/CellSidebarButton';
-const NOTEBOOK_UID = 'notebook-uid';
+const NOTEBOOK_ID = 'notebook-id';
const div = document.createElement('div');
document.body.appendChild(div);
@@ -21,7 +21,7 @@ root.render(
}>
{
/>
{
{
kernelManager,
kernelName: 'defaultKernel',
kernelSpecName: 'python',
- kernelType: 'notebook',
kernelspecsManager: serviceManager.kernelspecs,
sessionManager: serviceManager.sessions,
});
@@ -53,7 +52,7 @@ const NotebookUnmount = () => {
{
- const outputStore = useJupyterStore().outputStore();
- console.log('Outputs 1', outputStore.getAdapter(SOURCE_ID_1)?.outputArea.model.toJSON());
+ const outputStore = useOutputsStore();
+ console.log(
+ 'Outputs 1',
+ outputStore.getModel(SOURCE_ID_1)?.toJSON(),
+ outputStore.getInput(SOURCE_ID_1),
+ );
return (
<>
Output without Code Editor
>
);
@@ -55,23 +77,80 @@ const OutputWithoutEditor = () => {
const OutputWithEditor = () => {
const { defaultKernel } = useJupyter();
- const outputStore = useJupyterStore().outputStore();
- console.log('Outputs 2', outputStore.getAdapter(SOURCE_ID_2)?.outputArea.model.toJSON());
+ const outputStore = useOutputsStore();
+ console.log(
+ 'Outputs 2',
+ outputStore.getModel(SOURCE_ID_2)?.toJSON(),
+ outputStore.getInput(SOURCE_ID_2),
+ );
return (
<>
Output with Code Editor
>
);
};
+const OutputWithEmptyOutput = () => {
+ const { kernelManager, serviceManager } = useJupyter();
+ const outputStore = useOutputsStore();
+ const kernelsStore = useKernelsStore();
+ const [kernel, setKernel] = useState();
+ useEffect( () => {
+ if (serviceManager && kernelManager) {
+ const kernel = new Kernel({
+ kernelManager,
+ kernelName: 'kernel-example',
+ kernelSpecName: 'python',
+ kernelspecsManager: serviceManager.kernelspecs,
+ path: newUuid(),
+ sessionManager: serviceManager.sessions,
+ });
+ setKernel(kernel);
+ }
+ }, [serviceManager, kernelManager]);
+ console.log(
+ 'Outputs 3',
+ outputStore.getModel(SOURCE_ID_3)?.toJSON(),
+ outputStore.getInput(SOURCE_ID_3),
+ );
+ return (
+ <>
+ Output with empty Output
+ { kernel &&
+ <>
+
+ Kernel State: {kernelsStore.getExecutionState(kernel.id)}
+
+
+ Kernel Phase: {kernelsStore.getExecutionPhase(kernel.id)}
+
+
+
+
+
+
+
+ >
+ }
+ >
+ );
+};
+
const div = document.createElement('div');
document.body.appendChild(div);
const root = createRoot(div);
@@ -80,5 +159,6 @@ root.render(
+
);
diff --git a/packages/react/src/examples/Panel.tsx b/packages/react/src/examples/Panel.tsx
index 7f9e9fdf..48cde2a8 100644
--- a/packages/react/src/examples/Panel.tsx
+++ b/packages/react/src/examples/Panel.tsx
@@ -15,7 +15,7 @@ const Panel = () => {
(
(
{
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertAbove({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
source:
"print('Hello 🪐 ⚛️ Jupyter React, I have been inserted up ⬆️.')",
@@ -90,7 +90,7 @@ export const CellSidebarSource = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertAbove({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -107,7 +107,7 @@ export const CellSidebarSource = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.changeCellType({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -122,7 +122,7 @@ export const CellSidebarSource = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.changeCellType({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
}}
@@ -139,7 +139,7 @@ export const CellSidebarSource = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}}
@@ -155,7 +155,7 @@ export const CellSidebarSource = (props: CellSidebarProps) => {
onClick={(e: any) => {
e.preventDefault();
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
source:
"print('Hello 🪐 ⚛️ Jupyter React, I have been inserted down ⬇️.')",
diff --git a/packages/react/src/examples/toolbars/NotebookToolbar.tsx b/packages/react/src/examples/toolbars/NotebookToolbar.tsx
index 0d685631..5a1c295c 100644
--- a/packages/react/src/examples/toolbars/NotebookToolbar.tsx
+++ b/packages/react/src/examples/toolbars/NotebookToolbar.tsx
@@ -53,7 +53,7 @@ export const NotebookToolbar = (props: { notebookId: string }) => {
onClick={e => {
e.preventDefault();
notebookStore.save({
- uid: notebookId,
+ id: notebookId,
date: new Date(),
});
}}
@@ -132,17 +132,17 @@ export const NotebookToolbar = (props: { notebookId: string }) => {
e.preventDefault();
if (type === 'raw') {
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'raw',
});
} else if (type === 'code') {
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'code',
});
} else if (type === 'markdown') {
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: 'markdown',
});
}
diff --git a/packages/react/src/examples/toolbars/NotebookToolbarAutoSave.tsx b/packages/react/src/examples/toolbars/NotebookToolbarAutoSave.tsx
index 2762c12b..c711c24c 100644
--- a/packages/react/src/examples/toolbars/NotebookToolbarAutoSave.tsx
+++ b/packages/react/src/examples/toolbars/NotebookToolbarAutoSave.tsx
@@ -57,7 +57,7 @@ export const NotebookToolbarAutoSave = (props: { notebookId: string }) => {
onClick={e => {
e.preventDefault();
notebookStore.save({
- uid: notebookId,
+ id: notebookId,
date: new Date(),
});
}}
@@ -145,7 +145,7 @@ export const NotebookToolbarAutoSave = (props: { notebookId: string }) => {
onClick={e => {
e.preventDefault();
notebookStore.insertBelow({
- uid: notebookId,
+ id: notebookId,
cellType: addType,
});
}}
diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts
index 400464e1..5b5b61cd 100755
--- a/packages/react/src/index.ts
+++ b/packages/react/src/index.ts
@@ -101,7 +101,7 @@ export * from './components/notebook/cell/sidebar/CellSidebarRun';
// Outputs.
// @todo CodeMirrorEditor imported by Output breaks the JupyterLab extension loading.
// @see https://github.com/datalayer/jupyter-ui/issues/170
-// export * from './components/output/Output';
+export * from './components/output/Output';
export * from './components/output/OutputAdapter';
export * from './components/output/OutputState';
export * from './components/output/OutputIPyWidgets';
diff --git a/packages/react/src/jupyter/kernel/Kernel.ts b/packages/react/src/jupyter/kernel/Kernel.ts
index 8859b8f0..62bcff12 100755
--- a/packages/react/src/jupyter/kernel/Kernel.ts
+++ b/packages/react/src/jupyter/kernel/Kernel.ts
@@ -5,7 +5,7 @@
*/
import { find } from '@lumino/algorithm';
-import { PromiseDelegate, UUID } from '@lumino/coreutils';
+import { PromiseDelegate } from '@lumino/coreutils';
import {
Kernel as JupyterKernel,
KernelMessage,
@@ -14,7 +14,7 @@ import {
} from '@jupyterlab/services';
import { ISessionConnection } from '@jupyterlab/services/lib/session/session';
import { ConnectionStatus } from '@jupyterlab/services/lib/kernel/kernel';
-import { getCookie } from '../../utils/Utils';
+import { getCookie, newUuid } from '../../utils/Utils';
import KernelExecutor, {
IOPubMessageHook,
ShellMessageHook,
@@ -51,20 +51,22 @@ export class Kernel {
kernelspecsManager,
kernelSpecName,
kernelModel,
+ path,
sessionManager,
} = props;
this._kernelSpecManager = kernelspecsManager;
this._kernelManager = kernelManager;
this._kernelName = kernelName;
- this._kernelType = kernelType;
+ this._kernelType = kernelType ?? 'notebook';
this._kernelSpecName = kernelSpecName;
this._sessionManager = sessionManager;
this._ready = new PromiseDelegate();
- this.requestKernel(kernelModel);
+ this.requestKernel(kernelModel, path);
}
private async requestKernel(
- kernelModel?: JupyterKernel.IModel
+ kernelModel?: JupyterKernel.IModel,
+ propsPath?: string,
): Promise {
await this._kernelManager.ready;
await this._sessionManager.ready;
@@ -78,9 +80,9 @@ export class Kernel {
this._session = this._sessionManager.connectTo({ model });
}
} else {
- let path = getCookie(this.cookieName);
+ let path = propsPath ?? getCookie(this.cookieName);
if (!path) {
- path = 'kernel-' + UUID.uuid4();
+ path = 'kernel-' + newUuid();
document.cookie = this.cookieName + '=' + path;
}
this._path = path;
@@ -185,19 +187,21 @@ export class Kernel {
execute(
code: string,
{
+ model,
iopubMessageHooks = [],
shellMessageHooks = [],
- model,
silent,
stopOnError,
storeHistory,
+ allowStdin,
}: {
+ model?: IOutputAreaModel;
iopubMessageHooks?: IOPubMessageHook[];
shellMessageHooks?: ShellMessageHook[];
- model?: IOutputAreaModel;
silent?: boolean;
stopOnError?: boolean;
storeHistory?: boolean;
+ allowStdin?: boolean;
} = {}
): KernelExecutor | undefined {
if (this._kernelConnection) {
@@ -211,6 +215,7 @@ export class Kernel {
silent,
stopOnError,
storeHistory,
+ allowStdin,
});
return kernelExecutor;
}
@@ -264,6 +269,10 @@ export namespace Kernel {
* Kernel options
*/
export type IKernelProps = {
+ /**
+ * A path
+ */
+ path?: string;
/**
* Kernel manager
*/
@@ -283,7 +292,7 @@ export namespace Kernel {
/**
* Kernel type
*/
- kernelType: 'notebook' | 'file';
+ kernelType?: 'notebook' | 'file' | undefined;
/**
* Session manager
*/
diff --git a/packages/react/src/jupyter/kernel/KernelExecutor.ts b/packages/react/src/jupyter/kernel/KernelExecutor.ts
index 6f98267c..28023a97 100644
--- a/packages/react/src/jupyter/kernel/KernelExecutor.ts
+++ b/packages/react/src/jupyter/kernel/KernelExecutor.ts
@@ -15,9 +15,11 @@ import {
IMimeBundle,
} from '@jupyterlab/nbformat';
import { IOutputAreaModel, OutputAreaModel } from '@jupyterlab/outputarea';
-import { Kernel, KernelMessage } from '@jupyterlab/services';
+import { Kernel as JupyterKernel, KernelMessage } from '@jupyterlab/services';
import { IClearOutputMsg } from '@jupyterlab/services/lib/kernel/messages';
import { outputsAsString } from '../../utils/Utils';
+import { ExecutionPhase, KernelsState, kernelsStore } from './KernelState';
+import { toKernelState } from '../../components/kernel';
export type IOPubMessageHook = (
msg: KernelMessage.IIOPubMessage
@@ -30,11 +32,11 @@ export type ShellMessageHook = (
/**
* KernelExecutor options
*/
-export interface IKernelExecutorOptions {
+export type IKernelExecutorOptions = {
/**
* Kernel connection
*/
- connection: Kernel.IKernelConnection;
+ connection: JupyterKernel.IKernelConnection;
/**
* Outputs model to populate with the execution results.
*/
@@ -42,23 +44,23 @@ export interface IKernelExecutorOptions {
}
export class KernelExecutor {
- private _kernelConnection: Kernel.IKernelConnection;
- private _outputs: IOutput[];
- private _outputsChanged = new Signal(this);
+ private _executed: PromiseDelegate;
+ private _kernelConnection: JupyterKernel.IKernelConnection;
+ private _kernelState: KernelsState;
private _model: IOutputAreaModel;
private _modelChanged = new Signal(this);
- private _future?: Kernel.IFuture<
- KernelMessage.IExecuteRequestMsg,
- KernelMessage.IExecuteReplyMsg
- >;
+ private _outputs: IOutput[];
+ private _stopOnError: boolean;
+ private _outputsChanged = new Signal(this);
+ private _future?: JupyterKernel.IFuture;
private _shellMessageHooks = new Array();
- private _executed: PromiseDelegate;
- constructor({ connection, model }: IKernelExecutorOptions) {
+ public constructor({ connection, model }: IKernelExecutorOptions) {
+ this._executed = new PromiseDelegate();
this._kernelConnection = connection;
- this._outputs = [];
this._model = model ?? new OutputAreaModel();
- this._executed = new PromiseDelegate();
+ this._outputs = [];
+ this._kernelState = kernelsStore.getState();
}
/**
@@ -84,35 +86,38 @@ export class KernelExecutor {
silent = false,
stopOnError = false,
storeHistory = true,
+ allowStdin = false,
}: {
iopubMessageHooks?: IOPubMessageHook[];
shellMessageHooks?: ShellMessageHook[];
silent?: boolean;
stopOnError?: boolean;
storeHistory?: boolean;
+ allowStdin?: boolean;
} = {}
): Promise {
+ this._stopOnError = stopOnError;
this._shellMessageHooks = shellMessageHooks;
+ kernelsStore.getState().setExecutionPhase(this._kernelConnection.id, ExecutionPhase.running);
this._future = this._kernelConnection.requestExecute({
code,
- allow_stdin: false,
+ allow_stdin: allowStdin,
silent,
stop_on_error: stopOnError,
store_history: storeHistory,
});
- iopubMessageHooks.forEach(hook => this._future!.registerMessageHook(hook));
this._future.onIOPub = this._onIOPub;
this._future.onReply = this._onReply;
- /*
- FIXME Handle stdin. It will require updating the `allow_stdin` param aboove .
- future.onStdin = msg => {
+ iopubMessageHooks.forEach(hook => this._future!.registerMessageHook(hook));
+ this._future.onStdin = msg => {
if (KernelMessage.isInputRequestMsg(msg)) {
- this.onInputRequest(msg, value);
+ // FIXME Implement this.
+ // this.onInputRequest(msg, value);
}
};
- */
- // Wait for future to be done before resolving.
+ // Wait for future to be done before resolving the exectud promise.
this._future.done.then(() => {
+ kernelsStore.getState().setExecutionPhase(this._kernelConnection.id, ExecutionPhase.completed);
this._executed.resolve(this._model);
});
return this._executed.promise;
@@ -127,10 +132,71 @@ export class KernelExecutor {
this._model.clear();
}
+ registerIOPubMessageHook = (msg: IOPubMessageHook) => {
+ this._future?.registerMessageHook(msg);
+ };
+
+ /**
+ *
+ */
+ get future(): JupyterKernel.IFuture<
+ KernelMessage.IExecuteRequestMsg,
+ KernelMessage.IExecuteReplyMsg
+ > | undefined {
+ return this._future;
+ }
+
+ /**
+ * Promise that resolves when the execution is done.
+ */
+ get done(): Promise {
+ return this._executed.promise.then(() => {
+ return;
+ });
+ }
+
+ /**
+ * Code execution result as serialized JSON
+ */
+ get result(): Promise {
+ return this._executed.promise.then(model => {
+ return outputsAsString(model.toJSON());
+ });
+ }
+
+ /**
+ * Kernel outputs emitted.
+ */
+ get outputs(): IOutput[] {
+ return this._outputs;
+ }
+
+ /**
+ * Kernel outputs wrapped in a model.
+ */
+ get model(): IOutputAreaModel {
+ return this._model;
+ }
+
+ /**
+ * Signal emitted when the outputs list changes.
+ */
+ get outputsChanged(): ISignal {0
+ return this._outputsChanged;
+ }
+
+ /**
+ * Signal emitted when the outputs model changes.
+ */
+ get modelChanged(): ISignal {
+ return this._modelChanged;
+ }
+
private _onIOPub = (message: KernelMessage.IIOPubMessage): void => {
if (this._future?.msg.header.msg_id !== message.parent_header.msg_id) {
return;
}
+ console.debug('Kernel IOPub message', message);
const messageType: KernelMessage.IOPubMessageType = message.header.msg_type;
const output = { ...message.content, output_type: messageType };
switch (messageType) {
@@ -152,6 +218,9 @@ export class KernelExecutor {
this._outputsChanged.emit(this._outputs);
this._model.add(output);
this._modelChanged.emit(this._model);
+ if (this._stopOnError) {
+ kernelsStore.getState().setExecutionPhase(this._kernelConnection.id, ExecutionPhase.completed_with_error);
+ }
break;
case 'clear_output':
const wait = (message as IClearOutputMsg).content.wait;
@@ -165,9 +234,10 @@ export class KernelExecutor {
this._modelChanged.emit(this._model);
break;
case 'status':
- // execution_state: 'busy' 'starting' 'terminating' 'restarting' 'initializing' 'connecting' 'disconnected' 'dead' 'unknown' 'idle'
- const executionState = (message.content as any).execution_state;
- executionState;
+ const executionState = (message.content as any).execution_state as KernelMessage.Status;
+ const connectionStatus = this._kernelConnection.connectionStatus;
+ const kernelState = toKernelState(connectionStatus!, executionState);
+ this._kernelState.setExecutionState(this._kernelConnection.id, kernelState);
break;
default:
break;
@@ -178,6 +248,7 @@ export class KernelExecutor {
if (this._future?.msg.header.msg_id !== message.parent_header.msg_id) {
return;
}
+ console.debug('Kernel Reply message', message);
this._shellMessageHooks.forEach(hook => hook(message));
const content = message.content;
if (content.status !== 'ok') {
@@ -219,56 +290,6 @@ export class KernelExecutor {
}
};
- registerIOPubMessageHook = (msg: IOPubMessageHook) => {
- this._future?.registerMessageHook(msg);
- };
-
- /**
- * Promise that resolves when the execution is done.
- */
- get done(): Promise {
- return this._executed.promise.then(() => {
- return;
- });
- }
-
- /**
- * Code execution result as serialized JSON
- */
- get result(): Promise {
- return this._executed.promise.then(model => {
- return outputsAsString(model.toJSON());
- });
- }
-
- /**
- * Kernel outputs emitted.
- */
- get outputs(): IOutput[] {
- return this._outputs;
- }
-
- /**
- * Kernel outputs wrapped in a model.
- */
- get model(): IOutputAreaModel {
- return this._model;
- }
-
- /**
- * Signal emitted when the outputs list changes.
- */
- get outputsChanged(): ISignal {0
- return this._outputsChanged;
- }
-
- /**
- * Signal emitted when the outputs model changes.
- */
- get modelChanged(): ISignal {
- return this._modelChanged;
- }
-
}
export default KernelExecutor;
diff --git a/packages/react/src/jupyter/kernel/KernelState.ts b/packages/react/src/jupyter/kernel/KernelState.ts
new file mode 100644
index 00000000..b071c4a2
--- /dev/null
+++ b/packages/react/src/jupyter/kernel/KernelState.ts
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2021-2023 Datalayer, Inc.
+ *
+ * MIT License
+ */
+
+import { createStore } from 'zustand/vanilla';
+import { useStore } from 'zustand';
+import {ExecutionState } from './../../components/kernel/Kernelndicator';
+
+export enum ExecutionPhase {
+ ready_to_run = 'READY_TO_RUN',
+ running = 'RUNNING',
+ completed = 'COMPLETED',
+ completed_with_error = 'COMPLETED_WITH_ERROR',
+}
+
+export type IKernelState = {
+ id: string;
+ executionState?: ExecutionState;
+ executionPhase?: ExecutionPhase;
+};
+
+export interface IKernelsState {
+ kernels: Map;
+}
+
+export type KernelsState = IKernelsState & {
+ getExecutionState: (id: string) => ExecutionState | undefined;
+ setExecutionState: (id: string, executionState: ExecutionState) => void;
+ getExecutionPhase: (id: string) => ExecutionPhase | undefined;
+ setExecutionPhase: (id: string, executionState: ExecutionPhase) => void;
+};
+
+export const kernelsStore = createStore((set, get) => ({
+ kernels: new Map(),
+ getExecutionState: (id: string) => {
+ return get().kernels.get(id)?.executionState;
+ },
+ setExecutionState: (id: string, executionState: ExecutionState) => {
+ const kernels = get().kernels;
+ const k = kernels.get(id);
+ if (k) {
+ k.executionState = executionState;
+ } else {
+ kernels.set(id, {
+ id,
+ executionState: executionState
+ });
+ }
+ set((state: KernelsState) => ({ kernels }))
+ },
+ getExecutionPhase: (id: string) => {
+ return get().kernels.get(id)?.executionPhase;
+ },
+ setExecutionPhase: (id: string, executionPhase: ExecutionPhase) => {
+ const kernels = get().kernels;
+ const k = kernels.get(id);
+ if (k) {
+ k.executionPhase = executionPhase;
+ } else {
+ kernels.set(id, {
+ id,
+ executionPhase,
+ });
+ }
+ set((state: KernelsState) => ({ kernels }))
+ },
+}));
+
+export function useKernelsStore(): KernelsState;
+export function useKernelsStore(selector: (state: KernelsState) => T): T;
+export function useKernelsStore(selector?: (state: KernelsState) => T) {
+ return useStore(kernelsStore, selector!);
+}
+
+export default useKernelsStore;
diff --git a/packages/react/src/state/State.ts b/packages/react/src/state/State.ts
index 6f0b9fcf..0748ecda 100644
--- a/packages/react/src/state/State.ts
+++ b/packages/react/src/state/State.ts
@@ -10,29 +10,29 @@ import { useStore } from 'zustand';
import { ServiceManager } from '@jupyterlab/services';
import type { IDatalayerConfig } from './IState';
import { IJupyterConfig, loadJupyterConfig } from '../jupyter/JupyterConfig';
-import useCellStore from '../components/cell/CellState';
-import useConsoleStore from '../components/console/ConsoleState';
-import useNotebookStore from '../components/notebook/NotebookState';
-import useOutputStore from '../components/output/OutputState';
-import useTerminalStore from '../components/terminal/TerminalState';
+import { cellsStore, CellsState } from '../components/cell/CellState';
+import { consoleStore, ConsoleState } from '../components/console/ConsoleState';
+import { notebookStore, NotebookState } from '../components/notebook/NotebookState';
+import { outputsStore, OutputState } from '../components/output/OutputState';
+import { terminalStore, TerminalState } from '../components/terminal/TerminalState';
import { createLiteServer } from '../jupyter/lite/LiteServer';
import { getJupyterServerUrl } from '../jupyter/JupyterConfig';
import { ensureJupyterAuth, createServerSettings, JupyterContextPropsType } from '../jupyter/JupyterContext';
import Kernel from '../jupyter/kernel/Kernel';
export type JupyterState = {
- cellStore: typeof useCellStore;
- consoleStore: typeof useConsoleStore;
+ cellsStore: CellsState;
+ consoleStore: ConsoleState;
datalayerConfig?: IDatalayerConfig;
jupyterConfig?: IJupyterConfig;
kernel?: Kernel;
kernelIsLoading: boolean;
- notebookStore: typeof useNotebookStore;
- outputStore: typeof useOutputStore;
+ notebookStore: NotebookState;
+ outputStore: OutputState;
serviceManager?: ServiceManager;
setDatalayerConfig: (configuration?: IDatalayerConfig) => void;
setVersion: (version: string) => void;
- terminalStore: typeof useTerminalStore;
+ terminalStore: TerminalState;
version: string;
};
@@ -63,11 +63,11 @@ export const jupyterStore = createStore((set, get) => ({
kernel: undefined,
serviceManager: undefined,
serverSettings: undefined,
- cellStore: useCellStore,
- consoleStore: useConsoleStore,
- notebookStore: useNotebookStore,
- outputStore: useOutputStore,
- terminalStore: useTerminalStore,
+ cellsStore: cellsStore.getState(),
+ consoleStore: consoleStore.getState(),
+ notebookStore: notebookStore.getState(),
+ outputStore: outputsStore.getState(),
+ terminalStore: terminalStore.getState(),
}));
// TODO Reuse code portions from JupyterContext
@@ -187,7 +187,6 @@ export function useJupyterStoreFromContext(props: JupyterContextPropsType): Jupy
kernelName: defaultKernelName,
kernelSpecName: defaultKernelName,
kernelModel: kernel.value,
- kernelType: 'notebook',
kernelspecsManager: serviceManager.kernelspecs,
sessionManager: serviceManager.sessions,
});
@@ -215,7 +214,6 @@ export function useJupyterStoreFromContext(props: JupyterContextPropsType): Jupy
kernelManager,
kernelName: defaultKernelName,
kernelSpecName: defaultKernelName,
- kernelType: 'notebook',
kernelspecsManager: serviceManager.kernelspecs,
sessionManager: serviceManager.sessions,
});
diff --git a/packages/react/src/utils/Utils.ts b/packages/react/src/utils/Utils.ts
index be66eb17..7d65af32 100644
--- a/packages/react/src/utils/Utils.ts
+++ b/packages/react/src/utils/Utils.ts
@@ -5,19 +5,16 @@
*/
import { ICell, IOutput } from '@jupyterlab/nbformat';
+import { ulid } from 'ulid';
import { UUID } from '@lumino/coreutils';
-// const MAX = Number.MAX_SAFE_INTEGER;
-// const MAX = 999999;
-
-export const newSourceId = (base: string) => {
- // return base + Math.floor(Math.random() * MAX).toString();
- return base;
-};
+export const newUlid = () => {
+ return ulid()
+}
export const newUuid = () => {
return UUID.uuid4();
-};
+}
export const cellSourceAsString = (cell: ICell) => {
let source = cell.source;
@@ -25,7 +22,7 @@ export const cellSourceAsString = (cell: ICell) => {
source = (source as []).join('\n');
}
return source;
-};
+}
export const outputsAsString = (outputs: IOutput[]) => {
let result = '';
@@ -89,4 +86,4 @@ export const getCookie = (name: string): string | null => {
return decodeURIComponent(cookie.substring(nameLenPlus));
})[0] || null
);
-};
+}
diff --git a/packages/react/webpack.config.js b/packages/react/webpack.config.js
index 0c0345d1..65becb77 100644
--- a/packages/react/webpack.config.js
+++ b/packages/react/webpack.config.js
@@ -19,6 +19,7 @@ function shim(regExp) {
const ENTRY =
// './src/app/App';
// './src/examples/Cell';
+ // './src/examples/Cells';
// './src/examples/CellLite';
// './src/examples/Console';
// './src/examples/ConsoleLite';
@@ -30,8 +31,8 @@ const ENTRY =
// './src/examples/JupyterLabApp';
// './src/examples/JupyterLabHeadlessApp';
// './src/examples/Kernels';
+ // './src/examples/KernelExecute';
// './src/examples/KernelExecutor';
- // './src/examples/KernelExecResult';
// './src/examples/Lumino';
// './src/examples/Matplotlib';
'./src/examples/Notebook';
diff --git a/storybook/package.json b/storybook/package.json
index be46b93d..12d1f6fa 100644
--- a/storybook/package.json
+++ b/storybook/package.json
@@ -27,7 +27,7 @@
},
"dependencies": {
"@datalayer/jupyter-lexical": "^0.1.0",
- "@datalayer/jupyter-react": "^0.12.0",
+ "@datalayer/jupyter-react": "^0.15.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},