diff --git a/packages/react/package.json b/packages/react/package.json index 4226cacd..e0297060 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@datalayer/jupyter-react", - "version": "0.18.2", + "version": "0.18.3", "description": "Jupyter React - React.js components 100% compatible with Jupyter.", "license": "MIT", "main": "lib/index.js", diff --git a/packages/react/src/components/filebrowser/FileBrowser.tsx b/packages/react/src/components/filebrowser/FileBrowser.tsx index 5567587e..728ee703 100644 --- a/packages/react/src/components/filebrowser/FileBrowser.tsx +++ b/packages/react/src/components/filebrowser/FileBrowser.tsx @@ -8,7 +8,7 @@ import { useState, useEffect, useReducer } from 'react'; import { TreeView } from '@primer/react'; import { FileIcon } from '@primer/octicons-react'; import { useJupyter } from './../../jupyter/JupyterContext'; -import Services from './../../jupyter/services/Services'; +import JupyterServices from './../../jupyter/services/JupyterServices'; interface RenderTree { id: string; @@ -26,12 +26,12 @@ export const FileBrowser = () => { const [, forceUpdate] = useReducer(x => x + 1, 0); const { serviceManager } = useJupyter(); const loadPath = ( - services: Services, + services: JupyterServices, subTree: RenderTree, path: string[] ) => { const loadFolderItems = ( - services: Services, + services: JupyterServices, path: string[] ): Promise => { const folderItems = services @@ -79,7 +79,7 @@ export const FileBrowser = () => { }; useEffect(() => { if (serviceManager) { - const services = new Services(serviceManager); + const services = new JupyterServices(serviceManager); loadPath(services, initialTree, []); } }, [serviceManager]); diff --git a/packages/react/src/components/jupyterlab/JupyterLabApp.tsx b/packages/react/src/components/jupyterlab/JupyterLabApp.tsx index ad4197d7..0cbaf824 100644 --- a/packages/react/src/components/jupyterlab/JupyterLabApp.tsx +++ b/packages/react/src/components/jupyterlab/JupyterLabApp.tsx @@ -126,7 +126,7 @@ JupyterLabAppComponent.defaultProps = { onJupyterLab: (_: JupyterLabAppAdapter) => {}, plugins: [], position: 'relative', - nosplash: true, + nosplash: false, serverless: false, startDefaultKernel: false, theme: 'light', diff --git a/packages/react/src/components/jupyterlab/JupyterLabAppAdapter.ts b/packages/react/src/components/jupyterlab/JupyterLabAppAdapter.ts index c736aaa0..56dc8b6a 100644 --- a/packages/react/src/components/jupyterlab/JupyterLabAppAdapter.ts +++ b/packages/react/src/components/jupyterlab/JupyterLabAppAdapter.ts @@ -84,7 +84,6 @@ export class JupyterLabAppAdapter { const extensionResolved = await Promise.all(extensionPromises!); disabledPlugins.push( "@jupyterlab/notebook-extension:language-server", -// "@jupyterlab/notebook-extension:toc", "@jupyterlab/notebook-extension:update-raw-mimetype", "@jupyterlab/fileeditor-extension:language-server", "@jupyterlab/apputils-extension:sessionDialogs", @@ -118,8 +117,13 @@ export class JupyterLabAppAdapter { */ this._jupyterLab.start({ hostID: hostId, - startPlugins: [], // How is this used in JupyterLab core? - ignorePlugins: [], // How is this used in JupyterLab core? + startPlugins: [ + ], // How is this used in JupyterLab core? + ignorePlugins: [ + ], // How is this used in JupyterLab core? + }).then(() => { +// this._plugins = (this._jupyterLab as any)['_plugins']; +// this._readyResolve(); }); this._jupyterLab.restored.then(() => { this._plugins = (this._jupyterLab as any)['_plugins']; diff --git a/packages/react/src/components/notebook/Notebook.tsx b/packages/react/src/components/notebook/Notebook.tsx index 466d924a..ea99cec2 100644 --- a/packages/react/src/components/notebook/Notebook.tsx +++ b/packages/react/src/components/notebook/Notebook.tsx @@ -77,8 +77,7 @@ export type INotebookProps = { * @returns A Notebook React.js component. */ export const Notebook = (props: INotebookProps) => { - const { serviceManager, defaultKernel, kernelManager, lite } = - useJupyter(); + const { serviceManager, defaultKernel, kernelManager, lite } = useJupyter(); const { path, kernel: propsKernel, @@ -97,6 +96,7 @@ export const Notebook = (props: INotebookProps) => { const portals = notebookStore.selectNotebookPortals(id); const newAdapterState = () => { if (id && serviceManager && kernelManager && kernel) { + console.log('---', serviceManager, kernelManager, kernel) kernel.ready.then(() => { const adapter = new NotebookAdapter( { diff --git a/packages/react/src/examples/CellExecuteControl.tsx b/packages/react/src/examples/CellExecuteControl.tsx deleted file mode 100644 index 7c8dfbc5..00000000 --- a/packages/react/src/examples/CellExecuteControl.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2021-2023 Datalayer, Inc. - * - * MIT License - */ -import React, {useEffect} from 'react'; -import { createRoot } from 'react-dom/client'; - -import {Button, Box} from '@primer/react'; - -import Jupyter from '../jupyter/Jupyter'; -import Cell from '../components/cell/Cell'; -import { cellsStore, ICellsState } from '../components/cell/CellState'; - - -const div = document.createElement('div'); -document.body.appendChild(div); -const root = createRoot(div); - -const btnStyle = { - marginLeft: '50px', - marginTop: '20px' -} - -const CELL_CODE = `import time\ntime.sleep(3)` - -const CellExecuteControl = () => { - const [executionDisable, setExecutionDisable] = React.useState(false); - - useEffect(() => { - const handleChange = (newState: ICellsState) => { - setExecutionDisable(newState.isAnyCellExecuting); - }; - - const unsubscribe = cellsStore.subscribe(handleChange); - return () => { - unsubscribe(); - }; - }, []); - - const onExecuteClick = () => { - cellsStore.getState().execute(); - } - - return ( - - - - - - - - ) -}; - -root.render(); diff --git a/packages/react/src/examples/CellLite.tsx b/packages/react/src/examples/CellLite.tsx index 3c4ef240..bafbd736 100644 --- a/packages/react/src/examples/CellLite.tsx +++ b/packages/react/src/examples/CellLite.tsx @@ -17,8 +17,8 @@ root.render( A Jupyter Cell with a Lite Kernel ); diff --git a/packages/react/src/examples/CellsExecute.tsx b/packages/react/src/examples/CellsExecute.tsx new file mode 100644 index 00000000..77d0bf7b --- /dev/null +++ b/packages/react/src/examples/CellsExecute.tsx @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021-2023 Datalayer, Inc. + * + * MIT License + */ +import { useState, useEffect } from 'react'; +import { createRoot } from 'react-dom/client'; +import { Button, Box } from '@primer/react'; +import { cellsStore, ICellsState } from '../components/cell/CellState'; +import JupyterLabTheme from '../jupyter/lab/JupyterLabTheme'; +import Cell from '../components/cell/Cell'; + +const CODE_CELL_1 = `import time +time.sleep(3) +print("Cell 1 done.")` + +const CODE_CELL_2 = `import time +time.sleep(3) +print("Cell 2 done.")` + +const CellsExecute = () => { + const [executionDisable, setExecutionDisable] = useState(false); + useEffect(() => { + const handleChange = (newState: ICellsState) => { + setExecutionDisable(newState.isAnyCellExecuting); + }; + const unsubscribe = cellsStore.subscribe(handleChange); + return () => { + unsubscribe(); + }; + }, []); + const onExecuteClick = () => { + cellsStore.getState().execute(); + } + return ( + + + + + + + + ) +}; + +const div = document.createElement('div'); +document.body.appendChild(div); +const root = createRoot(div); + +root.render(); diff --git a/packages/react/src/examples/ConsoleLite.tsx b/packages/react/src/examples/ConsoleLite.tsx index 7101003a..22bca2e2 100644 --- a/packages/react/src/examples/ConsoleLite.tsx +++ b/packages/react/src/examples/ConsoleLite.tsx @@ -19,7 +19,8 @@ root.render( ); diff --git a/packages/react/src/examples/JupyterLabApp.tsx b/packages/react/src/examples/JupyterLabApp.tsx index 6628f4a2..f5721622 100644 --- a/packages/react/src/examples/JupyterLabApp.tsx +++ b/packages/react/src/examples/JupyterLabApp.tsx @@ -35,7 +35,9 @@ const JupyterLabAppExample = () => { plotlyPlugins, reactPlugins, ]} - mimeRenderers={[plotlyMimeRenderers]} + mimeRenderers={[ + plotlyMimeRenderers + ]} height="calc(100vh - 74px)" onJupyterLab={onJupyterLab} /> diff --git a/packages/react/src/examples/JupyterLabHeadlessApp.tsx b/packages/react/src/examples/JupyterLabAppHeadless.tsx similarity index 88% rename from packages/react/src/examples/JupyterLabHeadlessApp.tsx rename to packages/react/src/examples/JupyterLabAppHeadless.tsx index 9c26db85..51bfb8c9 100644 --- a/packages/react/src/examples/JupyterLabHeadlessApp.tsx +++ b/packages/react/src/examples/JupyterLabAppHeadless.tsx @@ -6,13 +6,7 @@ import { useState } from 'react'; import { createRoot } from 'react-dom/client'; -import { - Box, - Text, - ToggleSwitch, - ThemeProvider, - useTheme, -} from '@primer/react'; +import { Box, Text, ToggleSwitch, ThemeProvider, useTheme } from '@primer/react'; import { BoxPanel } from '@lumino/widgets'; import { ThemeManager } from '@jupyterlab/apputils'; // import { NotebookTracker } from '@jupyterlab/notebook'; @@ -31,15 +25,17 @@ import * as plotlyMimeRenderers from 'jupyterlab-plotly/lib/plotly-renderer'; const height = '900px'; -const PATHS = ['ipywidgets.ipynb', 'plotly.ipynb']; +const PATHS = [ + 'ipywidgets.ipynb', + 'plotly.ipynb', +]; const PATH_INDEX = 1; -const JupyterLabHeadlessAppExample = () => { +const JupyterLabAppHeadless = () => { const [notebookBoxPanel, setNotebookBoxPanel] = useState(); const [theme, setTheme] = useState('light'); - const [jupyterLabAdapter, setJupyterlabAdapter] = - useState(); + const [jupyterLabAdapter, setJupyterlabAdapter] = useState(); const { setColorMode } = useTheme(); const [isDark, setDark] = useState(false); const onSwitchClick = async () => { @@ -55,9 +51,10 @@ const JupyterLabHeadlessAppExample = () => { const handleSwitchChange = (dark: boolean) => { setDark(dark); }; - const onJupyterLab = async (jupyterLabAdapter: JupyterLabAppAdapter) => { - setJupyterlabAdapter(jupyterLabAdapter); - const boxPanel = await jupyterLabAdapter.notebook(PATHS[PATH_INDEX]); + const onJupyterLab = async (jupyterLab: JupyterLabAppAdapter) => { + setJupyterlabAdapter(jupyterLab); + console.log('JupyterLab is ready', jupyterLab); + const boxPanel = await jupyterLab.notebook(PATHS[PATH_INDEX]); setNotebookBoxPanel(boxPanel); }; const onPlugin = (themeManager: ThemeManager) => { @@ -123,6 +120,7 @@ const JupyterLabHeadlessAppExample = () => { )} { mimeRenderers={[ plotlyMimeRenderers ]} - headless - nosplash={false} onJupyterLab={onJupyterLab} pluginId="@jupyterlab/apputils-extension:themes" PluginType={ThemeManager} @@ -148,4 +144,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/JupyterLabAppHeadlessServerless.tsx b/packages/react/src/examples/JupyterLabAppHeadlessServerless.tsx new file mode 100644 index 00000000..a54539f5 --- /dev/null +++ b/packages/react/src/examples/JupyterLabAppHeadlessServerless.tsx @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021-2023 Datalayer, Inc. + * + * MIT License + */ + +import { useState } from 'react'; +import { createRoot } from 'react-dom/client'; +import { Box, Text, ToggleSwitch, ThemeProvider, useTheme } from '@primer/react'; +import { BoxPanel } from '@lumino/widgets'; +import { ThemeManager } from '@jupyterlab/apputils'; +// import { NotebookTracker } from '@jupyterlab/notebook'; +import JupyterLabTheme from '../jupyter/lab/JupyterLabTheme'; +import Lumino from '../components/lumino/Lumino'; +import { ColorMode } from '../jupyter/lab/JupyterLabColorMode'; +import JupyterLabApp from '../components/jupyterlab/JupyterLabApp'; +import JupyterLabAppAdapter from '../components/jupyterlab/JupyterLabAppAdapter'; + +import * as darkThemePlugins from '@jupyterlab/theme-dark-extension'; +import * as lightThemePlugins from '@jupyterlab/theme-light-extension'; +import * as ipywidgetsPlugins from '@jupyter-widgets/jupyterlab-manager'; +import * as plotlyPlugins from 'jupyterlab-plotly/lib/jupyterlab-plugin'; + +import * as plotlyMimeRenderers from 'jupyterlab-plotly/lib/plotly-renderer'; + +const height = '900px'; + +const PATHS = [ + 'ipywidgets.ipynb', + 'plotly.ipynb', +]; + +const PATH_INDEX = 1; + +const JupyterLabAppHeadless = () => { + const [notebookBoxPanel, setNotebookBoxPanel] = useState(); + const [theme, setTheme] = useState('light'); + const [jupyterLabAdapter, setJupyterlabAdapter] = useState(); + const { setColorMode } = useTheme(); + const [isDark, setDark] = useState(false); + const onSwitchClick = async () => { + if (jupyterLabAdapter) { + await jupyterLabAdapter.commands.execute('apputils:change-theme', { + theme: isDark ? 'JupyterLab Light' : 'JupyterLab Dark', + }); + setTheme(isDark ? 'light' : 'dark'); + setColorMode(isDark ? 'night' : 'day'); + } + setDark(!isDark); + }; + const handleSwitchChange = (dark: boolean) => { + setDark(dark); + }; + const onJupyterLab = async (jupyterLab: JupyterLabAppAdapter) => { + setJupyterlabAdapter(jupyterLab); + console.log('JupyterLab is ready', jupyterLab); + const boxPanel = await jupyterLab.notebook(PATHS[PATH_INDEX]); + setNotebookBoxPanel(boxPanel); + }; + const onPlugin = (themeManager: ThemeManager) => { + // const notebookTracker = jupyterlabAdapter.service("@jupyterlab/notebook-extension:tracker") as NotebookTracker; + console.log('Current theme', themeManager.theme); + }; + return ( + <> + + + + + JupyterLab Headless Serverless Application + + + + + Dark theme + + + + + + + + + {notebookBoxPanel && ( +
+ + {notebookBoxPanel} + +
+ )} + +
+ + ); +}; + +const div = document.createElement('div'); +document.body.appendChild(div); +const root = createRoot(div); + +root.render(); diff --git a/packages/react/src/examples/JupyterLabServerlessApp.tsx b/packages/react/src/examples/JupyterLabAppServerless.tsx similarity index 68% rename from packages/react/src/examples/JupyterLabServerlessApp.tsx rename to packages/react/src/examples/JupyterLabAppServerless.tsx index 3ea1f2c1..597fb0d0 100644 --- a/packages/react/src/examples/JupyterLabServerlessApp.tsx +++ b/packages/react/src/examples/JupyterLabAppServerless.tsx @@ -12,25 +12,35 @@ import JupyterLabAppAdapter from '../components/jupyterlab/JupyterLabAppAdapter' import * as lightThemePlugins from '@jupyterlab/theme-light-extension'; import * as ipywidgetsPlugins from '@jupyter-widgets/jupyterlab-manager'; import * as plotlyPlugins from 'jupyterlab-plotly/lib/jupyterlab-plugin'; -import * as reactPlugins from './../jupyter/lab/index'; +// import * as reactPlugins from './../jupyter/lab/index'; import * as plotlyMimeRenderers from 'jupyterlab-plotly/lib/plotly-renderer'; -const JupyterLabAppExample = () => { +const JupyterLabAppServerless = () => { const onJupyterLab = async (jupyterLabAdapter: JupyterLabAppAdapter) => { const jupyterLab = jupyterLabAdapter.jupyterLab; console.log('JupyterLab is ready', jupyterLab); + jupyterLab.commands.execute('apputils:activate-command-palette'); + jupyterLab.commands.execute('apputils:display-notifications'); + jupyterLab.commands.execute('toc:show-panel'); }; return ( @@ -44,6 +54,6 @@ const root = createRoot(div); root.render(

JupyterLab Serverless Application

- +
); diff --git a/packages/react/src/examples/JupyterLabAppServiceManager.tsx b/packages/react/src/examples/JupyterLabAppServiceManager.tsx new file mode 100644 index 00000000..4f09b2ad --- /dev/null +++ b/packages/react/src/examples/JupyterLabAppServiceManager.tsx @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021-2023 Datalayer, Inc. + * + * MIT License + */ + +import { useState } from 'react'; +import { createRoot } from 'react-dom/client'; +import { NotebookPanel } from '@jupyterlab/notebook'; +import JupyterLabTheme from '../jupyter/lab/JupyterLabTheme'; +import JupyterLabApp from '../components/jupyterlab/JupyterLabApp'; +import JupyterLabAppAdapter from '../components/jupyterlab/JupyterLabAppAdapter'; +import JupyterServiceManagerLess from '../jupyter/services/JupyterServiceManagerLess'; + +import * as lightThemePlugins from '@jupyterlab/theme-light-extension'; +import * as ipywidgetsPlugins from '@jupyter-widgets/jupyterlab-manager'; +import * as plotlyPlugins from 'jupyterlab-plotly/lib/jupyterlab-plugin'; +// import * as reactPlugins from './../jupyter/lab/index'; + +import * as plotlyMimeRenderers from 'jupyterlab-plotly/lib/plotly-renderer'; + +const JupyterLabAppServiceManager = () => { + const [serviceManager, _] = useState(new JupyterServiceManagerLess()); + const onJupyterLab = async (jupyterLabAdapter: JupyterLabAppAdapter) => { + const jupyterLab = jupyterLabAdapter.jupyterLab; + console.log('JupyterLab is ready', jupyterLab); + jupyterLab.commands.execute('apputils:activate-command-palette'); + jupyterLab.commands.execute('apputils:display-notifications'); + jupyterLab.commands.execute('toc:show-panel'); + jupyterLab.commands + .execute('notebook:create-new', { kernelName: 'python3' }) + .then((notebookPanel: NotebookPanel) => { + console.log('Jupyter Notebook Panel', notebookPanel); + }); + }; + return ( + + ); +}; + +const div = document.createElement('div'); +document.body.appendChild(div); +const root = createRoot(div); + +root.render( + +

JupyterLab Application with Service Manager property

+ +
+); diff --git a/packages/react/src/examples/KernelExecute.tsx b/packages/react/src/examples/KernelExecute.tsx index 6d527815..de7233b1 100644 --- a/packages/react/src/examples/KernelExecute.tsx +++ b/packages/react/src/examples/KernelExecute.tsx @@ -7,43 +7,52 @@ import { useState } from 'react'; import { createRoot } from 'react-dom/client'; import { Box, Heading, Textarea, Button } from '@primer/react'; -import Jupyter from '../jupyter/Jupyter'; import { useJupyter } from '../jupyter/JupyterContext'; +import Jupyter from '../jupyter/Jupyter'; import KernelProgressBar from './../components/kernel/KernelProgressBar'; export const KernelExecuteView = () => { - const { defaultKernel } = useJupyter(); + const { defaultKernel: kernel } = useJupyter(); const [running, setRunning] = useState(false); const [code, setCode] = useState(''); const [result, setResult] = useState(); const handleChange = (event: React.ChangeEvent) => { setCode(event.target.value); }; - const exec = async () => { + const exec = () => { setRunning(true); setResult(''); - const result = await defaultKernel?.execute(code)?.result; - setResult(result); - setRunning(false); + kernel?.execute(code)?.result + .then((result: string) => { + setResult(result); + }) + .catch((error: string) => { + console.log('Error', error); + setResult(error); + }) + .finally(() => { + setRunning(false); + } + ); }; const interrupt = () => { - defaultKernel?.interrupt(); + kernel?.interrupt(); }; return ( - Wait on Code execution with a Promise + Wait on code execution