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
Helper functions for semantic command management, connection lost handling, and tree path updates for enhanced application functionality.
Adds semantic commands to the application with automatic signal setup and command lifecycle management.
/**
* Add semantic commands to the application and set up command changed signal handling
* @param options - Semantic command configuration options
*/
function addSemanticCommand(options: ISemanticCommandOptions): void;
/**
* Semantic command configuration options
*/
interface ISemanticCommandOptions {
/** Unique identifier for the semantic command */
id: string;
/** Application command registry */
commands: CommandRegistry;
/** Application shell for widget context */
shell: JupyterFrontEnd.IShell;
/** Single semantic command or array of semantic commands */
semanticCommands: SemanticCommand | SemanticCommand[];
/** Default command options when no semantic command is enabled */
default?: ISemanticCommandDefault;
/** Override options for the command (excluding execute function) */
overrides?: Omit<CommandRegistry.ICommandOptions, 'execute'>;
/** Translation bundle for internationalization */
trans?: TranslationBundle;
}
/**
* Default command options for semantic commands
*/
interface ISemanticCommandDefault {
/** Default command ID to execute if no command is enabled */
execute?: string;
/** Default command label */
label?: string;
/** Default command caption */
caption?: string;
/** Whether the default command is enabled */
isEnabled?: boolean;
/** Whether the default command is toggled */
isToggled?: boolean;
/** Whether the default command is visible */
isVisible?: boolean;
}Usage Examples:
import { addSemanticCommand, ISemanticCommandOptions } from "@jupyterlab/application";
import { SemanticCommand } from "@jupyterlab/apputils";
import { CommandRegistry } from "@lumino/commands";
// Create semantic commands for file operations
const fileCommands = [
new SemanticCommand({
id: 'file:save',
selector: '.jp-Notebook',
rank: 100
}),
new SemanticCommand({
id: 'file:save-as',
selector: '.jp-Notebook',
rank: 200
})
];
// Add semantic command with comprehensive options
addSemanticCommand({
id: 'semantic:save',
commands: app.commands,
shell: app.shell,
semanticCommands: fileCommands,
default: {
execute: 'file:new',
label: 'Save',
caption: 'Save the current document',
isEnabled: true,
isVisible: true
},
overrides: {
icon: 'ui-components:save',
mnemonic: 0 // Underline first character
},
trans: translator.load('myapp')
});
// Single semantic command example
const copyCommand = new SemanticCommand({
id: 'edit:copy',
selector: '.jp-CodeCell',
rank: 50
});
addSemanticCommand({
id: 'semantic:copy',
commands: app.commands,
shell: app.shell,
semanticCommands: copyCommand,
default: {
label: 'Copy',
isEnabled: false
}
});
// Using the semantic command
app.commands.execute('semantic:save');
app.commands.execute('semantic:copy');Legacy function for creating semantic command options (use addSemanticCommand instead).
/**
* Create command options from semantic commands list and default values
* @param app - Jupyter Application or object with commands and shell
* @param semanticCommands - Single semantic command or array of commands
* @param defaultValues - Default values for command options
* @param trans - Translation bundle
* @returns Command options for registering with CommandRegistry
*
* @deprecated Please use addSemanticCommand. This function will be removed from the public API in JupyterLab 5.
*/
function createSemanticCommand(
app: JupyterFrontEnd | { commands: CommandRegistry; shell: JupyterFrontEnd.IShell },
semanticCommands: SemanticCommand | SemanticCommand[],
defaultValues: ISemanticCommandDefault,
trans: TranslationBundle
): CommandRegistry.ICommandOptions;Migration Example:
// Old approach (deprecated)
const commandOptions = createSemanticCommand(
app,
semanticCommands,
defaultValues,
trans
);
app.commands.addCommand('my-command', commandOptions);
// New approach (recommended)
addSemanticCommand({
id: 'my-command',
commands: app.commands,
shell: app.shell,
semanticCommands: semanticCommands,
default: defaultValues,
trans: trans
});Default connection lost dialog handler for server connection failures.
/**
* Default connection lost handler function that shows a dialog when server connection is lost
* @param manager - Service manager instance
* @param err - Network error that occurred
* @param translator - Optional translator for internationalization
* @returns Promise resolving when handling is complete
*/
function ConnectionLost(
manager: ServiceManager.IManager,
err: ServerConnection.NetworkError,
translator?: ITranslator
): Promise<void>;Usage Examples:
import { ConnectionLost } from "@jupyterlab/application";
import { ServiceManager, ServerConnection } from "@jupyterlab/services";
import { ITranslator } from "@jupyterlab/translation";
// Basic usage with service manager
const serviceManager = new ServiceManager();
const translator = app.serviceManager.translator;
// Handle connection failure
serviceManager.connectionFailure.connect(async (sender, error) => {
await ConnectionLost(serviceManager, error, translator);
});
// Custom connection lost handling
async function handleConnectionLost(
manager: ServiceManager.IManager,
error: ServerConnection.NetworkError
) {
console.log('Connection lost:', error.message);
// Use default handler
await ConnectionLost(manager, error);
// Additional custom handling
console.log('Connection lost dialog completed');
}
// Providing as a service
const connectionLostPlugin: JupyterFrontEndPlugin<IConnectionLost> = {
id: 'default-connection-lost',
provides: IConnectionLost,
activate: (): IConnectionLost => {
return ConnectionLost;
}
};Function interface for updating tree path in the application.
/**
* Function interface for updating the current tree path
* @param treePath - New tree path to set
*/
type ITreePathUpdater = (treePath: string) => void;
/**
* Service token for tree path updater
*/
const ITreePathUpdater: Token<ITreePathUpdater>;Usage Examples:
import { ITreePathUpdater } from "@jupyterlab/application";
import { JupyterFrontEndPlugin } from "@jupyterlab/application";
// Using tree path updater in a plugin
const pathUpdaterPlugin: JupyterFrontEndPlugin<void> = {
id: 'path-updater-example',
autoStart: true,
requires: [ITreePathUpdater],
activate: (app, updatePath: ITreePathUpdater) => {
// Update path when files are opened
app.shell.currentChanged.connect((sender, args) => {
const widget = args.newValue;
if (widget && widget.title.caption) {
// Extract path from widget caption
const path = widget.title.caption;
updatePath(path);
console.log('Updated tree path to:', path);
}
});
// Update path programmatically
const navigateToFile = (filePath: string) => {
updatePath(filePath);
console.log('Navigated to:', filePath);
};
// Example usage
navigateToFile('/notebooks/analysis.ipynb');
navigateToFile('/data/dataset.csv');
}
};
// Providing a tree path updater implementation
const treePathProviderPlugin: JupyterFrontEndPlugin<ITreePathUpdater> = {
id: 'tree-path-provider',
provides: ITreePathUpdater,
activate: (app): ITreePathUpdater => {
return (treePath: string) => {
// Update application state
console.log('Setting tree path:', treePath);
// Could update URL, breadcrumbs, or internal state
if (treePath.startsWith('/')) {
document.title = `JupyterLab - ${treePath}`;
}
// Emit custom event for other components
const event = new CustomEvent('tree-path-changed', {
detail: { path: treePath }
});
window.dispatchEvent(event);
};
}
};Advanced patterns for setting up sophisticated semantic command hierarchies.
// Advanced semantic command patterns
interface AdvancedSemanticCommandSetup {
/** Hierarchical command organization */
commandHierarchy: CommandHierarchy;
/** Conditional command enabling */
conditionalCommands: ConditionalCommandOptions;
/** Context-sensitive commands */
contextSensitiveCommands: ContextSensitiveOptions;
}
interface CommandHierarchy {
/** Parent command category */
category: string;
/** Child command definitions */
children: SemanticCommandDefinition[];
}
interface ConditionalCommandOptions {
/** Conditions for enabling commands */
enableConditions: Array<(context: any) => boolean>;
/** Commands to enable based on conditions */
conditionalCommands: SemanticCommandMapping[];
}Complete Advanced Example:
import {
addSemanticCommand,
ISemanticCommandOptions
} from "@jupyterlab/application";
import { SemanticCommand } from "@jupyterlab/apputils";
// Advanced semantic command setup
class AdvancedCommandManager {
private app: JupyterFrontEnd;
private commandCategories: Map<string, SemanticCommand[]> = new Map();
constructor(app: JupyterFrontEnd) {
this.app = app;
this.setupCommandHierarchy();
}
private setupCommandHierarchy(): void {
// File operations category
const fileCommands = [
new SemanticCommand({
id: 'file:save',
selector: '.jp-Document',
rank: 100
}),
new SemanticCommand({
id: 'file:save-as',
selector: '.jp-Document',
rank: 200
}),
new SemanticCommand({
id: 'file:export',
selector: '.jp-Notebook',
rank: 300
})
];
// Edit operations category
const editCommands = [
new SemanticCommand({
id: 'edit:copy',
selector: '.jp-CodeCell.jp-mod-active, .jp-MarkdownCell.jp-mod-active',
rank: 10
}),
new SemanticCommand({
id: 'edit:paste',
selector: '.jp-CodeCell.jp-mod-active, .jp-MarkdownCell.jp-mod-active',
rank: 20
}),
new SemanticCommand({
id: 'edit:cut',
selector: '.jp-CodeCell.jp-mod-active, .jp-MarkdownCell.jp-mod-active',
rank: 30
})
];
// View operations category
const viewCommands = [
new SemanticCommand({
id: 'view:toggle-line-numbers',
selector: '.jp-CodeCell',
rank: 100
}),
new SemanticCommand({
id: 'view:toggle-header',
selector: '.jp-Notebook',
rank: 200
})
];
this.commandCategories.set('file', fileCommands);
this.commandCategories.set('edit', editCommands);
this.commandCategories.set('view', viewCommands);
// Register all command categories
this.registerCommandCategories();
}
private registerCommandCategories(): void {
// File category commands
addSemanticCommand({
id: 'semantic:file-save',
commands: this.app.commands,
shell: this.app.shell,
semanticCommands: this.commandCategories.get('file')!,
default: {
execute: 'file:new',
label: 'Save Document',
caption: 'Save the current document',
isEnabled: true
},
overrides: {
icon: 'ui-components:save',
iconClass: 'jp-SaveIcon'
}
});
// Edit category commands
addSemanticCommand({
id: 'semantic:edit-clipboard',
commands: this.app.commands,
shell: this.app.shell,
semanticCommands: this.commandCategories.get('edit')!,
default: {
label: 'Clipboard Operation',
caption: 'Perform clipboard operation on selected content',
isEnabled: false
},
overrides: {
icon: 'ui-components:edit'
}
});
// View category commands with conditional logic
addSemanticCommand({
id: 'semantic:view-toggle',
commands: this.app.commands,
shell: this.app.shell,
semanticCommands: this.commandCategories.get('view')!,
default: {
label: 'Toggle View',
caption: 'Toggle view settings',
isEnabled: true,
isVisible: true
}
});
}
// Method to add new semantic commands dynamically
addDynamicCommand(
category: string,
commandId: string,
selector: string,
rank: number = 1000
): void {
const newCommand = new SemanticCommand({
id: commandId,
selector: selector,
rank: rank
});
if (!this.commandCategories.has(category)) {
this.commandCategories.set(category, []);
}
this.commandCategories.get(category)!.push(newCommand);
// Re-register the category
addSemanticCommand({
id: `semantic:${category}-dynamic`,
commands: this.app.commands,
shell: this.app.shell,
semanticCommands: this.commandCategories.get(category)!,
default: {
label: `${category} Operations`,
isEnabled: true
}
});
}
// Method to get current command status
getCommandStatus(semanticCommandId: string): {
isEnabled: boolean;
isVisible: boolean;
label: string;
} {
const commands = this.app.commands;
return {
isEnabled: commands.isEnabled(semanticCommandId),
isVisible: commands.isVisible(semanticCommandId),
label: commands.label(semanticCommandId)
};
}
}
// Usage
const commandManager = new AdvancedCommandManager(app);
// Add dynamic commands
commandManager.addDynamicCommand(
'notebook',
'notebook:run-all',
'.jp-Notebook',
50
);
// Check command status
const saveStatus = commandManager.getCommandStatus('semantic:file-save');
console.log('Save command status:', saveStatus);Advanced patterns for customizing connection lost behavior.
// Advanced connection lost handling
class AdvancedConnectionLostHandler {
private retryAttempts: number = 0;
private maxRetries: number = 3;
private retryDelay: number = 2000;
async handleConnectionLost(
manager: ServiceManager.IManager,
error: ServerConnection.NetworkError,
translator?: ITranslator
): Promise<void> {
console.log(`Connection lost (attempt ${this.retryAttempts + 1}):`, error.message);
// Try automatic reconnection first
if (this.retryAttempts < this.maxRetries) {
await this.attemptReconnection(manager, error, translator);
} else {
// Fall back to default dialog
await ConnectionLost(manager, error, translator);
this.retryAttempts = 0; // Reset for next time
}
}
private async attemptReconnection(
manager: ServiceManager.IManager,
error: ServerConnection.NetworkError,
translator?: ITranslator
): Promise<void> {
this.retryAttempts++;
// Show subtle notification instead of modal dialog
this.showReconnectionNotification(this.retryAttempts, this.maxRetries);
try {
// Wait before retry
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
// Test connection
await manager.ready;
// Success - show success notification
this.showReconnectionSuccess();
this.retryAttempts = 0;
} catch (retryError) {
// Retry failed
if (this.retryAttempts >= this.maxRetries) {
// Show final failure dialog
await ConnectionLost(manager, error, translator);
this.retryAttempts = 0;
} else {
// Try again
await this.attemptReconnection(manager, error, translator);
}
}
}
private showReconnectionNotification(attempt: number, maxAttempts: number): void {
const notification = document.createElement('div');
notification.className = 'reconnection-notification';
notification.innerHTML = `
<div class="reconnection-content">
<span class="reconnection-icon">🔄</span>
<span class="reconnection-text">
Reconnecting... (${attempt}/${maxAttempts})
</span>
</div>
`;
document.body.appendChild(notification);
// Auto-remove after delay
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, this.retryDelay);
}
private showReconnectionSuccess(): void {
const notification = document.createElement('div');
notification.className = 'success-notification';
notification.innerHTML = `
<div class="success-content">
<span class="success-icon">✅</span>
<span class="success-text">Reconnected successfully</span>
</div>
`;
document.body.appendChild(notification);
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 3000);
}
}
// Usage
const advancedHandler = new AdvancedConnectionLostHandler();
const connectionLostPlugin: JupyterFrontEndPlugin<IConnectionLost> = {
id: 'advanced-connection-lost',
provides: IConnectionLost,
activate: (app): IConnectionLost => {
return advancedHandler.handleConnectionLost.bind(advancedHandler);
}
};Example showing how all utility functions work together in a real application.
import {
addSemanticCommand,
ConnectionLost,
ITreePathUpdater
} from "@jupyterlab/application";
// Complete integration example
const utilityIntegrationPlugin: JupyterFrontEndPlugin<void> = {
id: 'utility-integration',
autoStart: true,
requires: [ITreePathUpdater],
activate: (app, updatePath: ITreePathUpdater) => {
// Set up semantic commands for file operations
const fileCommands = [
new SemanticCommand({
id: 'file:save',
selector: '.jp-Document',
rank: 100
})
];
addSemanticCommand({
id: 'integrated:save',
commands: app.commands,
shell: app.shell,
semanticCommands: fileCommands,
default: {
label: 'Save',
isEnabled: true
}
});
// Set up connection lost handling
app.serviceManager.connectionFailure.connect(async (sender, error) => {
await ConnectionLost(app.serviceManager, error);
});
// Set up tree path updates
app.shell.currentChanged.connect((sender, args) => {
const widget = args.newValue;
if (widget && widget.title.caption) {
updatePath(widget.title.caption);
}
});
// Integrate all utilities for file operations
app.commands.addCommand('integrated:open-file', {
execute: async (args: any) => {
const filePath = args.path as string;
// Update tree path
updatePath(filePath);
// Execute semantic save command
await app.commands.execute('integrated:save');
console.log('File operation completed:', filePath);
}
});
}
};