From 25ae1ca282ced693a4e6b0d73049ee9ea1bfd25f Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 24 Apr 2025 10:18:06 +0200 Subject: [PATCH 1/3] Provide `createProxy` --- packages/host/src/index.ts | 13 ++++++++++++- packages/host/tsconfig.json | 7 ++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/host/src/index.ts b/packages/host/src/index.ts index 9265c29..008a1bd 100644 --- a/packages/host/src/index.ts +++ b/packages/host/src/index.ts @@ -1,6 +1,7 @@ // Copyright (c) TileDB, Inc. // Distributed under the terms of the Modified BSD License. -import { windowEndpoint, wrap } from 'comlink'; +import { windowEndpoint, wrap, proxy, ProxyOrClone } from 'comlink'; + import { ICommandBridgeRemote } from 'jupyter-iframe-commands'; /** * A bridge to expose actions on JupyterLab commands. @@ -22,3 +23,13 @@ export function createBridge({ iframeId }: { iframeId: string }) { return wrap(windowEndpoint(iframe.contentWindow)); } + +/** + * Creates a proxy for the given object. + * + * @param args - The object to create a proxy for. + * @returns A proxy for the given object. + */ +export function createProxy(args: any): ProxyOrClone { + return proxy(args); +} diff --git a/packages/host/tsconfig.json b/packages/host/tsconfig.json index 92d6e92..29b7565 100644 --- a/packages/host/tsconfig.json +++ b/packages/host/tsconfig.json @@ -4,5 +4,10 @@ "outDir": "lib", "rootDir": "src" }, - "include": ["src/**/*"] + "include": ["src/**/*"], + "references": [ + { + "path": "../extension" + } + ] } From e25c0597dcb12669fd7bd20d46cc98ea7da66fe6 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 24 Apr 2025 10:36:17 +0200 Subject: [PATCH 2/3] Document use of callbacks --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/README.md b/README.md index dbff852..7cbbbd1 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,64 @@ For further information please consult the Jupyter documentation: - Creating an extension: https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html - Adding commands to the command registry: https://jupyterlab.readthedocs.io/en/stable/extension/extension_points.html#commands + +### Registering Callbacks + +If you would like to register a callback to be executed when a particular event occurs in the JupyterLab, you can make use of the `createProxy` function from the `jupyter-iframe-commands-host` package. This function allows you to create a proxy to be passed to the IFrame as a command `args`, so it can be invoked in a JupyterLab command. + +On the host, define the callback function: + +```js +import { createBridge, createProxy } from 'jupyter-iframe-commands-host'; + +const commandBridge = createBridge({ iframeId: 'jupyterlab' }); + +const kernelStatus = async ({ displayName, isBusy }) => { + console.log('Received kernel status update from the iframe'); + console.log('Display Name:', displayName); + console.log('Is Busy:', isBusy); +}; + +await commandBridge.execute('kernel-status', createProxy({ kernelStatus })); +``` + +In your custom JupyterLab extension, you can define a command: + +```ts +const demoPlugin: JupyterFrontEndPlugin = { + id: 'jupyter-iframe-commands:demo-callback', + autoStart: true, + description: 'Using host callbacks', + requires: [ILabStatus, INotebookTracker], + activate: async ( + app: JupyterFrontEnd, + labStatus: ILabStatus, + tracker: INotebookTracker + ) => { + const { commands } = app; + + commands.addCommand('kernel-status', { + label: 'Kernel Status', + execute: args => { + const kernelStatus = args['kernelStatus'] as unknown as ( + args?: any + ) => void; + + labStatus.busySignal.connect(async () => { + const kernelSpec = + await tracker.currentWidget?.sessionContext?.session?.kernel?.spec; + const displayName = kernelSpec + ? kernelSpec.display_name + : 'Loading...'; + kernelStatus({ displayName, isBusy: labStatus.isBusy }); + }); + } + }); + } +}; +``` + + ## Demos ### Local Demo From a74709cf70b9fb9324f2ed735869905e95f52361 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 24 Apr 2025 10:40:51 +0200 Subject: [PATCH 3/3] add note about proxy --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cbbbd1..e1a079d 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,6 @@ For further information please consult the Jupyter documentation: - Creating an extension: https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html - Adding commands to the command registry: https://jupyterlab.readthedocs.io/en/stable/extension/extension_points.html#commands - ### Registering Callbacks If you would like to register a callback to be executed when a particular event occurs in the JupyterLab, you can make use of the `createProxy` function from the `jupyter-iframe-commands-host` package. This function allows you to create a proxy to be passed to the IFrame as a command `args`, so it can be invoked in a JupyterLab command. @@ -185,6 +184,17 @@ const demoPlugin: JupyterFrontEndPlugin = { }; ``` +> [!WARNING] +> The returned value of `createProxy` must be passed as the `args` of the command so the underlying proxy can properly be transferred by Comlink. `createProxy` can proxy an entire object, so you can pass multiple callbacks and other data as well at once. For example: +> +> ```js +> const proxy = createProxy({ +> kernelStatus, +> anotherCallback, +> someData: { foo: 'bar' } +> }); +> await commandBridge.execute('kernel-status', proxy); +> ``` ## Demos