CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jupyter-widgets--base

Foundational classes and utilities for building interactive widgets in Jupyter environments

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

communication.mddocs/

Communication

The communication layer provides bidirectional messaging between Jupyter widgets in the frontend and their corresponding models in the kernel through comm channels and message handling.

Capabilities

IClassicComm Interface

Core interface for communication channels between frontend widgets and kernel models.

/**
 * Interface for Jupyter comm communication channels
 */
interface IClassicComm {
  /**
   * Unique identifier for the communication channel
   */
  comm_id: string;

  /**
   * Target name identifying the comm type
   */
  target_name: string;

  /**
   * Open a communication channel to the kernel
   * @param data - Initial data to send
   * @param callbacks - Message callback handlers
   * @param metadata - Additional message metadata
   * @param buffers - Binary data buffers
   * @returns Message ID of the open message
   */
  open(
    data: JSONValue,
    callbacks?: ICallbacks,
    metadata?: JSONObject,
    buffers?: ArrayBuffer[] | ArrayBufferView[]
  ): string;

  /**
   * Send a message through the communication channel
   * @param data - Message data payload
   * @param callbacks - Message callback handlers
   * @param metadata - Additional message metadata
   * @param buffers - Binary data buffers
   * @returns Message ID of the sent message
   */
  send(
    data: JSONValue,
    callbacks?: ICallbacks,
    metadata?: JSONObject,
    buffers?: ArrayBuffer[] | ArrayBufferView[]
  ): string;

  /**
   * Close the communication channel
   * @param data - Optional closing data
   * @param callbacks - Message callback handlers
   * @param metadata - Additional message metadata
   * @param buffers - Binary data buffers
   * @returns Message ID of the close message
   */
  close(
    data?: JSONValue,
    callbacks?: ICallbacks,
    metadata?: JSONObject,
    buffers?: ArrayBuffer[] | ArrayBufferView[]
  ): string;

  /**
   * Register a handler for incoming messages
   * @param callback - Function to handle received messages
   */
  on_msg(callback: (x: any) => void): void;

  /**
   * Register a handler for comm close events
   * @param callback - Function to handle comm closure
   */
  on_close(callback: (x: any) => void): void;
}

Usage Examples:

// Setup message handling
comm.on_msg((msg) => {
  console.log('Received message:', msg);
  // Handle different message types
  switch (msg.content.method) {
    case 'update':
      handleStateUpdate(msg.content.state);
      break;
    case 'custom':
      handleCustomMessage(msg.content.data);
      break;
  }
});

// Setup close handling
comm.on_close((msg) => {
  console.log('Comm closed:', msg);
  cleanup();
});

// Send messages with callbacks
const msgId = comm.send(
  { method: 'request_data', params: { type: 'summary' } },
  {
    shell: {
      reply: (msg) => console.log('Got reply:', msg)
    },
    iopub: {
      status: (msg) => console.log('Status:', msg.content.execution_state)
    }
  }
);

Message Callbacks

Configuration for handling different types of kernel messages during comm operations.

/**
 * Callback handlers for different message channels
 */
interface ICallbacks {
  /**
   * Handlers for shell messages (replies, errors)
   */
  shell?: { 
    [key: string]: (msg: KernelMessage.IShellMessage) => void 
  };

  /**
   * Handlers for IOPub messages (status, output, errors)
   */
  iopub?: { 
    [key: string]: (msg: KernelMessage.IIOPubMessage) => void 
  };

  /**
   * Handler for stdin messages (input requests)
   */
  input?: (msg: KernelMessage.IStdinMessage) => void;
}

Usage Examples:

// Comprehensive callback setup
const callbacks: ICallbacks = {
  shell: {
    reply: (msg) => {
      // Handle successful replies
      console.log('Operation completed:', msg.content);
    },
    error: (msg) => {
      // Handle errors
      console.error('Operation failed:', msg.content);
    }
  },
  iopub: {
    status: (msg) => {
      // Track kernel execution state
      const state = msg.content.execution_state;
      if (state === 'busy') {
        showSpinner();
      } else if (state === 'idle') {
        hideSpinner();
      }
    },
    execute_result: (msg) => {
      // Handle execution results
      displayResult(msg.content.data);
    },
    stream: (msg) => {
      // Handle stdout/stderr output
      appendOutput(msg.content.text);
    },
    error: (msg) => {
      // Handle execution errors
      displayError(msg.content.ename, msg.content.evalue, msg.content.traceback);
    }
  },
  input: (msg) => {
    // Handle input requests from kernel
    const prompt = msg.content.prompt;
    const response = await getUserInput(prompt);
    // Send response back to kernel
  }
};

// Use callbacks with comm operations
comm.send(data, callbacks);

Services Shim Layer

Compatibility layer that provides the classic comm API while using modern @jupyterlab/services underneath.

CommManager

Manager class for creating and registering communication channels.

/**
 * Manager for communication channels, provides compatibility with @jupyterlab/services
 */
class CommManager {
  /**
   * Initialize comm manager with kernel connection
   * @param jsServicesKernel - @jupyterlab/services kernel connection
   */
  constructor(jsServicesKernel: Kernel.IKernelConnection);

  /**
   * Setup or update kernel connection
   * @param jsServicesKernel - @jupyterlab/services kernel connection
   */
  init_kernel(jsServicesKernel: Kernel.IKernelConnection): void;

  /**
   * Create a new communication channel
   * @param target_name - Comm target identifier
   * @param data - Initial data to send
   * @param callbacks - Message callbacks
   * @param metadata - Message metadata
   * @param comm_id - Specific comm ID to use
   * @param buffers - Binary data buffers
   * @returns Promise resolving to the created comm
   */
  async new_comm(
    target_name: string,
    data: any,
    callbacks: any,
    metadata: any,
    comm_id: string,
    buffers?: ArrayBuffer[] | ArrayBufferView[]
  ): Promise<Comm>;

  /**
   * Register a handler for incoming comm creation requests
   * @param target_name - Target name to handle
   * @param f - Handler function receiving comm and message
   */
  register_target(
    target_name: string,
    f: (comm: Comm, object: KernelMessage.IMessage) => void
  ): void;

  /**
   * Unregister a comm target handler
   * @param target_name - Target name to unregister
   * @param f - Handler function to remove
   */
  unregister_target(
    target_name: string,
    f: (comm: Comm, object: KernelMessage.IMessage) => void
  ): void;

  /**
   * Register a comm instance with the manager
   * @param comm - Comm instance to register
   * @returns Comm ID
   */
  register_comm(comm: Comm): string;
}

Comm Implementation

Wrapper class that implements IClassicComm using @jupyterlab/services IComm.

/**
 * Comm implementation wrapping @jupyterlab/services IComm
 */
class Comm implements IClassicComm {
  /**
   * Create comm wrapper
   * @param jsServicesComm - @jupyterlab/services IComm instance
   */
  constructor(jsServicesComm: Kernel.IComm);

  /**
   * Get the comm identifier
   */
  get comm_id(): string;

  /**
   * Get the target name
   */
  get target_name(): string;

  /**
   * Open the comm channel
   * @param data - Opening data
   * @param callbacks - Message callbacks
   * @param metadata - Message metadata
   * @param buffers - Binary buffers
   * @returns Message ID
   */
  open(
    data: JSONValue,
    callbacks?: ICallbacks,
    metadata?: JSONObject,
    buffers?: ArrayBuffer[] | ArrayBufferView[]
  ): string;

  /**
   * Send message through comm
   * @param data - Message data
   * @param callbacks - Message callbacks
   * @param metadata - Message metadata  
   * @param buffers - Binary buffers
   * @returns Message ID
   */
  send(
    data: JSONValue,
    callbacks?: ICallbacks,
    metadata?: JSONObject,
    buffers?: ArrayBuffer[] | ArrayBufferView[]
  ): string;

  /**
   * Close the comm channel
   * @param data - Closing data
   * @param callbacks - Message callbacks
   * @param metadata - Message metadata
   * @param buffers - Binary buffers
   * @returns Message ID
   */
  close(
    data?: JSONValue,
    callbacks?: ICallbacks,
    metadata?: JSONObject,
    buffers?: ArrayBuffer[] | ArrayBufferView[]
  ): string;

  /**
   * Register message handler
   * @param callback - Message handler function
   */
  on_msg(callback: (x: any) => void): void;

  /**
   * Register close handler
   * @param callback - Close handler function
   */
  on_close(callback: (x: any) => void): void;
}

Usage Examples:

// Initialize comm manager
const kernel = await kernelManager.startNew();
const commManager = new CommManager(kernel);

// Register comm target
commManager.register_target('my_widget', (comm, msg) => {
  console.log('New widget comm created:', comm.comm_id);
  
  // Setup handlers for this comm
  comm.on_msg((msg) => {
    handleWidgetMessage(comm, msg);
  });
  
  comm.on_close((msg) => {
    cleanupWidget(comm.comm_id);
  });
});

// Create new comm
const comm = await commManager.new_comm(
  'my_widget',
  { widget_type: 'slider', initial_value: 50 },
  callbacks,
  {},
  'slider-widget-1'
);

Message Patterns

Widget State Synchronization

// Handle state updates from kernel
comm.on_msg((msg) => {
  if (msg.content.method === 'update') {
    const state = msg.content.state;
    const bufferPaths = msg.content.buffer_paths || [];
    const buffers = msg.buffers || [];
    
    // Reconstruct state with binary data
    put_buffers(state, bufferPaths, buffers);
    
    // Apply state to model
    model.set_state(state);
  }
});

// Send state changes to kernel
const sendStateUpdate = (changes: any) => {
  // Remove binary buffers for transmission
  const { state, buffer_paths, buffers } = remove_buffers(changes);
  
  comm.send({
    method: 'update',
    state: state,
    buffer_paths: buffer_paths
  }, callbacks, {}, buffers);
};

Custom Message Handling

// Send custom messages
const sendCustomMessage = (action: string, data: any) => {
  comm.send({
    method: 'custom',
    content: {
      action: action,
      data: data,
      timestamp: Date.now()
    }
  });
};

// Handle custom messages
comm.on_msg((msg) => {
  if (msg.content.method === 'custom') {
    const { action, data } = msg.content.content;
    
    switch (action) {
      case 'highlight':
        highlightWidget(data.color);
        break;
      case 'reset':
        resetWidget();
        break;
      case 'export':
        exportData(data.format);
        break;
    }
  }
});

Error Handling and Recovery

// Robust message sending with error handling
const sendMessageWithRetry = async (data: any, maxRetries = 3) => {
  let attempt = 0;
  
  while (attempt < maxRetries) {
    try {
      const msgId = comm.send(data, {
        shell: {
          reply: (msg) => {
            console.log('Message sent successfully:', msgId);
          },
          error: (msg) => {
            console.error('Message failed:', msg);
            throw new Error(`Message failed: ${msg.content.ename}`);
          }
        }
      });
      return msgId;
    } catch (error) {
      attempt++;
      if (attempt >= maxRetries) {
        throw error;
      }
      await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
    }
  }
};

// Connection state management
let connectionState = 'connected';

comm.on_close(() => {
  connectionState = 'disconnected';
  // Attempt reconnection
  setTimeout(attemptReconnection, 5000);
});

const attemptReconnection = async () => {
  try {
    // Create new comm with same ID
    const newComm = await commManager.new_comm(
      comm.target_name,
      { reconnect: true },
      callbacks,
      {},
      comm.comm_id
    );
    connectionState = 'connected';
    console.log('Reconnected successfully');
  } catch (error) {
    console.error('Reconnection failed:', error);
    setTimeout(attemptReconnection, 10000);
  }
};

Buffer Handling

Working with binary data in widget communication:

// Send data with binary buffers
const sendBinaryData = (imageData: Uint8Array, metadata: any) => {
  const data = {
    metadata: metadata,
    image: imageData  // This will be extracted as a buffer
  };
  
  // remove_buffers extracts binary data
  const { state, buffer_paths, buffers } = remove_buffers(data);
  
  comm.send({
    method: 'update',
    state: state,
    buffer_paths: buffer_paths
  }, callbacks, {}, buffers);
};

// Receive data with binary buffers
comm.on_msg((msg) => {
  if (msg.content.buffer_paths && msg.buffers) {
    const state = msg.content.state;
    const bufferPaths = msg.content.buffer_paths;
    const buffers = msg.buffers;
    
    // Reconstruct object with binary data
    put_buffers(state, bufferPaths, buffers);
    
    // Now state.image is a DataView containing the binary data
    const imageData = new Uint8Array(state.image.buffer);
    displayImage(imageData);
  }
});

docs

communication.md

error-handling.md

index.md

layout-style.md

nativeview.md

registry.md

utilities.md

widget-classes.md

widget-manager.md

tile.json