CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jupyterlab--application

JupyterLab application framework providing the core application class, shell management, plugin system, layout restoration, and routing capabilities.

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

mime-rendering.mddocs/

MIME Rendering

Plugin creation utilities for MIME type rendering extensions, enabling support for custom content types and document formats.

Capabilities

IMimeDocumentTracker Interface

Widget tracker interface for managing MIME document widgets across the application.

/**
 * MIME document widget tracker interface extending standard widget tracker
 */
interface IMimeDocumentTracker extends IWidgetTracker<MimeDocument> {
  // Inherits all IWidgetTracker methods:
  // - currentWidget: MimeDocument | null
  // - widgetAdded: ISignal<this, MimeDocument>
  // - widgetUpdated: ISignal<this, MimeDocument>
  // - activeWidget: MimeDocument | null
  // - size: number
  // - filter(fn: (widget: MimeDocument) => boolean): MimeDocument[]
  // - find(fn: (widget: MimeDocument) => boolean): MimeDocument | undefined
  // - forEach(fn: (widget: MimeDocument) => void): void
  // - has(widget: MimeDocument): boolean
  // - inject(widget: MimeDocument): void
}

/**
 * Service token for MIME document tracker
 */
const IMimeDocumentTracker: Token<IMimeDocumentTracker>;

Usage Examples:

import { IMimeDocumentTracker } from "@jupyterlab/application";
import { JupyterFrontEndPlugin } from "@jupyterlab/application";

const mimeTrackerPlugin: JupyterFrontEndPlugin<void> = {
  id: 'mime-tracker-example',
  autoStart: true,
  requires: [IMimeDocumentTracker],
  activate: (app, tracker: IMimeDocumentTracker) => {
    // Listen for new MIME documents
    tracker.widgetAdded.connect((sender, widget) => {
      console.log('New MIME document:', widget.context.path);
      console.log('MIME type:', widget.content.mimeType);
    });
    
    // Access current MIME document
    if (tracker.currentWidget) {
      const current = tracker.currentWidget;
      console.log('Current MIME document path:', current.context.path);
      console.log('Current MIME type:', current.content.mimeType);
    }
    
    // Find specific MIME documents
    const htmlDocs = tracker.filter(widget => 
      widget.content.mimeType === 'text/html'
    );
    console.log(`Found ${htmlDocs.length} HTML documents`);
    
    // Find first markdown document
    const markdownDoc = tracker.find(widget =>
      widget.content.mimeType === 'text/markdown'
    );
    if (markdownDoc) {
      console.log('Found markdown document:', markdownDoc.title.label);
    }
  }
};

createRendermimePlugins Function

Creates multiple plugins for rendering MIME type extensions in the JupyterLab environment.

/**
 * Creates plugins for multiple rendermime extensions
 * @param extensions - Array of rendermime extension modules
 * @returns Array of JupyterFrontEnd plugins
 */
function createRendermimePlugins(
  extensions: IRenderMime.IExtensionModule[]
): JupyterFrontEndPlugin<void | IMimeDocumentTracker, any, any>[];

Usage Examples:

import { createRendermimePlugins } from "@jupyterlab/application";
import { IRenderMime } from "@jupyterlab/rendermime-interfaces";

// Define custom MIME extensions
const customExtensions: IRenderMime.IExtensionModule[] = [
  {
    // CSV renderer extension
    id: 'csv-renderer',
    rendererFactory: {
      safe: true,
      mimeTypes: ['text/csv'],
      createRenderer: (options) => new CSVRenderer(options)
    },
    dataType: 'string',
    fileTypes: [{
      name: 'csv',
      extensions: ['.csv'],
      mimeTypes: ['text/csv'],
      icon: 'ui-components:csv'
    }],
    documentWidgetFactoryOptions: {
      name: 'CSV Viewer',
      primaryFileType: 'csv',
      fileTypes: ['csv']
    }
  },
  {
    // JSON renderer extension
    id: 'json-renderer',
    rendererFactory: {
      safe: true,
      mimeTypes: ['application/json'],
      createRenderer: (options) => new JSONRenderer(options)
    },
    dataType: 'string',
    fileTypes: [{
      name: 'json',
      extensions: ['.json'],
      mimeTypes: ['application/json'],
      icon: 'ui-components:json'
    }],
    documentWidgetFactoryOptions: {
      name: 'JSON Viewer', 
      primaryFileType: 'json',
      fileTypes: ['json']
    }
  }
];

// Create plugins for all extensions
const mimePlugins = createRendermimePlugins(customExtensions);

// Register plugins with application
mimePlugins.forEach(plugin => {
  app.registerPlugin(plugin);
});

// Example custom renderer implementation
class CSVRenderer implements IRenderMime.IRenderer {
  constructor(options: IRenderMime.IRendererOptions) {
    this.mimeType = 'text/csv';
  }

  readonly mimeType: string;

  render(model: IRenderMime.IMimeModel): Promise<void> {
    const data = model.data[this.mimeType] as string;
    const element = document.createElement('div');
    
    // Simple CSV to table conversion
    const rows = data.split('\n').map(row => row.split(','));
    const table = document.createElement('table');
    
    rows.forEach((row, index) => {
      const tr = document.createElement('tr');
      row.forEach(cell => {
        const td = document.createElement(index === 0 ? 'th' : 'td');
        td.textContent = cell.trim();
        tr.appendChild(td);
      });
      table.appendChild(tr);
    });
    
    element.appendChild(table);
    return Promise.resolve();
  }
}

createRendermimePlugin Function

Creates a single plugin for a specific MIME type renderer.

/**
 * Creates a plugin for a single rendermime extension
 * @param tracker - Widget tracker for MIME documents
 * @param item - Single rendermime extension
 * @returns JupyterFrontEnd plugin
 */
function createRendermimePlugin(
  tracker: WidgetTracker<MimeDocument>,
  item: IRenderMime.IExtension
): JupyterFrontEndPlugin<void>;

Usage Examples:

import { createRendermimePlugin } from "@jupyterlab/application";
import { WidgetTracker } from "@jupyterlab/apputils";
import { MimeDocument } from "@jupyterlab/docregistry";

// Create a widget tracker for custom MIME documents
const customMimeTracker = new WidgetTracker<MimeDocument>({
  namespace: 'custom-mime-documents'
});

// Define a single MIME extension
const xmlExtension: IRenderMime.IExtension = {
  id: 'xml-renderer',
  rendererFactory: {
    safe: true,
    mimeTypes: ['application/xml', 'text/xml'],
    createRenderer: (options) => new XMLRenderer(options)
  },
  dataType: 'string',
  fileTypes: [{
    name: 'xml',
    extensions: ['.xml', '.xsd', '.xsl'],
    mimeTypes: ['application/xml', 'text/xml'],
    icon: 'ui-components:xml'
  }],
  documentWidgetFactoryOptions: {
    name: 'XML Viewer',
    primaryFileType: 'xml',
    fileTypes: ['xml'],
    defaultFor: ['xml']
  }
};

// Create plugin for the extension
const xmlPlugin = createRendermimePlugin(customMimeTracker, xmlExtension);

// Register with application
app.registerPlugin(xmlPlugin);

// Example XML renderer
class XMLRenderer implements IRenderMime.IRenderer {
  constructor(options: IRenderMime.IRendererOptions) {
    this.mimeType = 'application/xml';
  }

  readonly mimeType: string;

  async render(model: IRenderMime.IMimeModel): Promise<void> {
    const data = model.data[this.mimeType] as string;
    const element = document.createElement('div');
    element.className = 'xml-viewer';
    
    try {
      // Parse and format XML
      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(data, 'application/xml');
      
      if (xmlDoc.documentElement.nodeName === 'parsererror') {
        throw new Error('Invalid XML');
      }
      
      // Create formatted display
      element.innerHTML = this.formatXML(data);
    } catch (error) {
      element.textContent = `Error parsing XML: ${error.message}`;
      element.className += ' error';
    }
    
    return Promise.resolve();
  }
  
  private formatXML(xml: string): string {
    // Simple XML formatting implementation
    const formatted = xml
      .replace(/></g, '>\n<')
      .replace(/^\s*\n/gm, '');
    
    return `<pre><code class="xml">${this.escapeHtml(formatted)}</code></pre>`;
  }
  
  private escapeHtml(text: string): string {
    return text
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }
}

MIME Extension Configuration

Detailed configuration options for creating comprehensive MIME type support.

/**
 * Complete MIME extension configuration interface
 */
interface IRenderMime.IExtensionModule {
  /** Unique identifier for the extension */
  id: string;
  
  /** Factory for creating renderers */
  rendererFactory: IRenderMime.IRendererFactory;
  
  /** Data type the renderer expects ('string' | 'json') */
  dataType?: 'string' | 'json';
  
  /** File types associated with this MIME type */
  fileTypes?: DocumentRegistry.IFileType[];
  
  /** Options for document widget factory */
  documentWidgetFactoryOptions?: DocumentRegistry.IWidgetFactoryOptions;
}

/**
 * Renderer factory interface
 */
interface IRenderMime.IRendererFactory {
  /** Whether the renderer is safe (doesn't execute code) */
  safe: boolean;
  
  /** MIME types this renderer handles */
  mimeTypes: string[];
  
  /** Function to create renderer instances */
  createRenderer: (options: IRenderMime.IRendererOptions) => IRenderMime.IRenderer;
  
  /** Default render timeout in milliseconds */
  defaultRank?: number;
  
  /** Whether renderer can render untrusted content */
  trusted?: boolean;
}

/**
 * File type configuration for MIME extensions
 */
interface DocumentRegistry.IFileType {
  /** Internal name for the file type */
  name: string;
  
  /** Display name for the file type */
  displayName?: string;
  
  /** File extensions (with dots) */
  extensions: string[];
  
  /** MIME types for this file type */
  mimeTypes: string[];
  
  /** Icon identifier */
  icon?: string;
  
  /** Icon class name */
  iconClass?: string;
  
  /** Icon label */
  iconLabel?: string;
  
  /** Content type category */
  contentType?: 'file' | 'directory' | 'notebook';
  
  /** File format ('text' | 'base64' | 'json') */
  fileFormat?: 'text' | 'base64' | 'json';
}

Advanced MIME Rendering Example

Complete example showing how to create a sophisticated MIME renderer with multiple features.

// Advanced MIME rendering example
interface AdvancedMimeRenderer {
  /** Support for multiple MIME variants */
  supportedMimeTypes: string[];
  
  /** Configurable rendering options */
  renderingOptions: RenderingOptions;
  
  /** Error handling and fallbacks */
  errorHandling: ErrorHandlingOptions;
  
  /** Performance optimization */
  optimizations: PerformanceOptions;
}

interface RenderingOptions {
  theme?: 'light' | 'dark' | 'auto';
  maxSize?: number;
  enableSyntaxHighlighting?: boolean;
  enableLineNumbers?: boolean;
}

interface ErrorHandlingOptions {
  showErrorDetails?: boolean;
  fallbackRenderer?: string;
  retryAttempts?: number;
}

interface PerformanceOptions {
  lazyLoading?: boolean;
  virtualScrolling?: boolean;
  renderTimeout?: number;
}

Complete Advanced Example:

import { 
  createRendermimePlugin, 
  IMimeDocumentTracker 
} from "@jupyterlab/application";
import { IRenderMime } from "@jupyterlab/rendermime-interfaces";
import { WidgetTracker } from "@jupyterlab/apputils";
import { MimeDocument } from "@jupyterlab/docregistry";

// Advanced GeoJSON renderer with mapping capabilities
class GeoJSONRenderer implements IRenderMime.IRenderer {
  private options: RenderingOptions;
  
  constructor(rendererOptions: IRenderMime.IRendererOptions) {
    this.mimeType = 'application/geo+json';
    this.options = {
      theme: 'auto',
      maxSize: 10 * 1024 * 1024, // 10MB
      enableSyntaxHighlighting: true,
      enableLineNumbers: false
    };
  }

  readonly mimeType = 'application/geo+json';

  async render(model: IRenderMime.IMimeModel): Promise<void> {
    const data = model.data[this.mimeType] as any;
    const element = document.createElement('div');
    element.className = 'geojson-viewer';
    
    try {
      // Check size limits
      const dataSize = JSON.stringify(data).length;
      if (dataSize > this.options.maxSize!) {
        throw new Error(`Data too large: ${dataSize} bytes`);
      }
      
      // Create map container
      const mapContainer = document.createElement('div');
      mapContainer.className = 'geojson-map';
      mapContainer.style.height = '400px';
      mapContainer.style.width = '100%';
      
      // Create data inspector
      const inspector = document.createElement('div');
      inspector.className = 'geojson-inspector';
      
      // Add map (simplified - would use real mapping library)
      await this.renderMap(mapContainer, data);
      
      // Add data inspector
      this.renderInspector(inspector, data);
      
      // Create tabs for map vs data view
      const tabs = this.createTabs([
        { label: 'Map View', content: mapContainer },
        { label: 'Data View', content: inspector }
      ]);
      
      element.appendChild(tabs);
      
    } catch (error) {
      this.renderError(element, error as Error);
    }
    
    return Promise.resolve();
  }
  
  private async renderMap(container: HTMLElement, data: any): Promise<void> {
    // Simplified map rendering - in real implementation would use
    // libraries like Leaflet, Mapbox, or OpenLayers
    container.innerHTML = `
      <div class="mock-map">
        <p>GeoJSON Map View</p>
        <p>Features: ${data.features?.length || 0}</p>
        <p>Type: ${data.type}</p>
      </div>
    `;
  }
  
  private renderInspector(container: HTMLElement, data: any): void {
    const formatted = JSON.stringify(data, null, 2);
    const pre = document.createElement('pre');
    const code = document.createElement('code');
    code.className = 'language-json';
    code.textContent = formatted;
    pre.appendChild(code);
    container.appendChild(pre);
    
    // Add syntax highlighting if enabled
    if (this.options.enableSyntaxHighlighting) {
      this.applySyntaxHighlighting(code);
    }
  }
  
  private createTabs(tabs: { label: string; content: HTMLElement }[]): HTMLElement {
    const container = document.createElement('div');
    container.className = 'tab-container';
    
    const tabHeaders = document.createElement('div');
    tabHeaders.className = 'tab-headers';
    
    const tabContents = document.createElement('div');
    tabContents.className = 'tab-contents';
    
    tabs.forEach((tab, index) => {
      // Create header
      const header = document.createElement('button');
      header.textContent = tab.label;
      header.className = 'tab-header';
      if (index === 0) header.classList.add('active');
      
      // Create content wrapper
      const content = document.createElement('div');
      content.className = 'tab-content';
      content.style.display = index === 0 ? 'block' : 'none';
      content.appendChild(tab.content);
      
      // Add click handler
      header.addEventListener('click', () => {
        // Update headers
        tabHeaders.querySelectorAll('.tab-header').forEach(h => 
          h.classList.remove('active')
        );
        header.classList.add('active');
        
        // Update contents
        tabContents.querySelectorAll('.tab-content').forEach(c => 
          (c as HTMLElement).style.display = 'none'
        );
        content.style.display = 'block';
      });
      
      tabHeaders.appendChild(header);
      tabContents.appendChild(content);
    });
    
    container.appendChild(tabHeaders);
    container.appendChild(tabContents);
    return container;
  }
  
  private applySyntaxHighlighting(element: HTMLElement): void {
    // Simplified syntax highlighting - would use library like Prism.js
    element.innerHTML = element.innerHTML
      .replace(/"([^"]+)":/g, '<span class="key">"$1":</span>')
      .replace(/: "([^"]+)"/g, ': <span class="string">"$1"</span>')
      .replace(/: (\d+)/g, ': <span class="number">$1</span>');
  }
  
  private renderError(container: HTMLElement, error: Error): void {
    container.innerHTML = `
      <div class="error">
        <h3>Error rendering GeoJSON</h3>
        <p>${error.message}</p>
        <details>
          <summary>Error Details</summary>
          <pre>${error.stack}</pre>
        </details>
      </div>
    `;
  }
}

// Create comprehensive GeoJSON extension
const geoJSONExtension: IRenderMime.IExtensionModule = {
  id: 'geojson-renderer',
  rendererFactory: {
    safe: true,
    mimeTypes: ['application/geo+json'],
    createRenderer: (options) => new GeoJSONRenderer(options),
    defaultRank: 0,
    trusted: true
  },
  dataType: 'json',
  fileTypes: [{
    name: 'geojson',
    displayName: 'GeoJSON',
    extensions: ['.geojson', '.json'],
    mimeTypes: ['application/geo+json'],
    icon: 'ui-components:json',
    contentType: 'file',
    fileFormat: 'text'
  }],
  documentWidgetFactoryOptions: {
    name: 'GeoJSON Viewer',
    primaryFileType: 'geojson',
    fileTypes: ['geojson'],
    defaultFor: ['geojson']
  }
};

// Create plugin
const tracker = new WidgetTracker<MimeDocument>({ 
  namespace: 'geojson-documents' 
});

const geoJSONPlugin = createRendermimePlugin(tracker, geoJSONExtension);

// Register with application
app.registerPlugin(geoJSONPlugin);

Best Practices

Performance Considerations

// Performance optimization patterns
class PerformantMimeRenderer implements IRenderMime.IRenderer {
  private renderCache = new Map<string, HTMLElement>();
  
  readonly mimeType = 'custom/large-data';
  
  async render(model: IRenderMime.IMimeModel): Promise<void> {
    const data = model.data[this.mimeType];
    const cacheKey = this.getCacheKey(data);
    
    // Check cache first
    if (this.renderCache.has(cacheKey)) {
      const cached = this.renderCache.get(cacheKey)!;
      return Promise.resolve();
    }
    
    // Lazy loading for large datasets
    if (this.isLargeDataset(data)) {
      return this.renderLazy(data, cacheKey);
    }
    
    // Regular rendering for small datasets
    return this.renderImmediate(data, cacheKey);
  }
  
  private isLargeDataset(data: any): boolean {
    return JSON.stringify(data).length > 100 * 1024; // 100KB
  }
  
  private async renderLazy(data: any, cacheKey: string): Promise<void> {
    // Implement virtual scrolling or pagination
    // Load data in chunks
  }
  
  private async renderImmediate(data: any, cacheKey: string): Promise<void> {
    // Render all data immediately
  }
  
  private getCacheKey(data: any): string {
    // Create cache key from data hash
    return btoa(JSON.stringify(data)).substring(0, 32);
  }
}

docs

application-framework.md

index.md

layout-restoration.md

mime-rendering.md

service-tokens.md

shell-management.md

status-management.md

url-routing.md

utility-functions.md

tile.json