JupyterLab application framework providing the core application class, shell management, plugin system, layout restoration, and routing capabilities.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Plugin creation utilities for MIME type rendering extensions, enabling support for custom content types and document formats.
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);
}
}
};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();
}
}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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
}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';
}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);// 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);
}
}