0
# Utility Functions
1
2
Helper functions for semantic command management, connection lost handling, and tree path updates for enhanced application functionality.
3
4
## Capabilities
5
6
### addSemanticCommand Function
7
8
Adds semantic commands to the application with automatic signal setup and command lifecycle management.
9
10
```typescript { .api }
11
/**
12
* Add semantic commands to the application and set up command changed signal handling
13
* @param options - Semantic command configuration options
14
*/
15
function addSemanticCommand(options: ISemanticCommandOptions): void;
16
17
/**
18
* Semantic command configuration options
19
*/
20
interface ISemanticCommandOptions {
21
/** Unique identifier for the semantic command */
22
id: string;
23
24
/** Application command registry */
25
commands: CommandRegistry;
26
27
/** Application shell for widget context */
28
shell: JupyterFrontEnd.IShell;
29
30
/** Single semantic command or array of semantic commands */
31
semanticCommands: SemanticCommand | SemanticCommand[];
32
33
/** Default command options when no semantic command is enabled */
34
default?: ISemanticCommandDefault;
35
36
/** Override options for the command (excluding execute function) */
37
overrides?: Omit<CommandRegistry.ICommandOptions, 'execute'>;
38
39
/** Translation bundle for internationalization */
40
trans?: TranslationBundle;
41
}
42
43
/**
44
* Default command options for semantic commands
45
*/
46
interface ISemanticCommandDefault {
47
/** Default command ID to execute if no command is enabled */
48
execute?: string;
49
50
/** Default command label */
51
label?: string;
52
53
/** Default command caption */
54
caption?: string;
55
56
/** Whether the default command is enabled */
57
isEnabled?: boolean;
58
59
/** Whether the default command is toggled */
60
isToggled?: boolean;
61
62
/** Whether the default command is visible */
63
isVisible?: boolean;
64
}
65
```
66
67
**Usage Examples:**
68
69
```typescript
70
import { addSemanticCommand, ISemanticCommandOptions } from "@jupyterlab/application";
71
import { SemanticCommand } from "@jupyterlab/apputils";
72
import { CommandRegistry } from "@lumino/commands";
73
74
// Create semantic commands for file operations
75
const fileCommands = [
76
new SemanticCommand({
77
id: 'file:save',
78
selector: '.jp-Notebook',
79
rank: 100
80
}),
81
new SemanticCommand({
82
id: 'file:save-as',
83
selector: '.jp-Notebook',
84
rank: 200
85
})
86
];
87
88
// Add semantic command with comprehensive options
89
addSemanticCommand({
90
id: 'semantic:save',
91
commands: app.commands,
92
shell: app.shell,
93
semanticCommands: fileCommands,
94
default: {
95
execute: 'file:new',
96
label: 'Save',
97
caption: 'Save the current document',
98
isEnabled: true,
99
isVisible: true
100
},
101
overrides: {
102
icon: 'ui-components:save',
103
mnemonic: 0 // Underline first character
104
},
105
trans: translator.load('myapp')
106
});
107
108
// Single semantic command example
109
const copyCommand = new SemanticCommand({
110
id: 'edit:copy',
111
selector: '.jp-CodeCell',
112
rank: 50
113
});
114
115
addSemanticCommand({
116
id: 'semantic:copy',
117
commands: app.commands,
118
shell: app.shell,
119
semanticCommands: copyCommand,
120
default: {
121
label: 'Copy',
122
isEnabled: false
123
}
124
});
125
126
// Using the semantic command
127
app.commands.execute('semantic:save');
128
app.commands.execute('semantic:copy');
129
```
130
131
### createSemanticCommand Function (Deprecated)
132
133
Legacy function for creating semantic command options (use `addSemanticCommand` instead).
134
135
```typescript { .api }
136
/**
137
* Create command options from semantic commands list and default values
138
* @param app - Jupyter Application or object with commands and shell
139
* @param semanticCommands - Single semantic command or array of commands
140
* @param defaultValues - Default values for command options
141
* @param trans - Translation bundle
142
* @returns Command options for registering with CommandRegistry
143
*
144
* @deprecated Please use addSemanticCommand. This function will be removed from the public API in JupyterLab 5.
145
*/
146
function createSemanticCommand(
147
app: JupyterFrontEnd | { commands: CommandRegistry; shell: JupyterFrontEnd.IShell },
148
semanticCommands: SemanticCommand | SemanticCommand[],
149
defaultValues: ISemanticCommandDefault,
150
trans: TranslationBundle
151
): CommandRegistry.ICommandOptions;
152
```
153
154
**Migration Example:**
155
156
```typescript
157
// Old approach (deprecated)
158
const commandOptions = createSemanticCommand(
159
app,
160
semanticCommands,
161
defaultValues,
162
trans
163
);
164
app.commands.addCommand('my-command', commandOptions);
165
166
// New approach (recommended)
167
addSemanticCommand({
168
id: 'my-command',
169
commands: app.commands,
170
shell: app.shell,
171
semanticCommands: semanticCommands,
172
default: defaultValues,
173
trans: trans
174
});
175
```
176
177
### ConnectionLost Function
178
179
Default connection lost dialog handler for server connection failures.
180
181
```typescript { .api }
182
/**
183
* Default connection lost handler function that shows a dialog when server connection is lost
184
* @param manager - Service manager instance
185
* @param err - Network error that occurred
186
* @param translator - Optional translator for internationalization
187
* @returns Promise resolving when handling is complete
188
*/
189
function ConnectionLost(
190
manager: ServiceManager.IManager,
191
err: ServerConnection.NetworkError,
192
translator?: ITranslator
193
): Promise<void>;
194
```
195
196
**Usage Examples:**
197
198
```typescript
199
import { ConnectionLost } from "@jupyterlab/application";
200
import { ServiceManager, ServerConnection } from "@jupyterlab/services";
201
import { ITranslator } from "@jupyterlab/translation";
202
203
// Basic usage with service manager
204
const serviceManager = new ServiceManager();
205
const translator = app.serviceManager.translator;
206
207
// Handle connection failure
208
serviceManager.connectionFailure.connect(async (sender, error) => {
209
await ConnectionLost(serviceManager, error, translator);
210
});
211
212
// Custom connection lost handling
213
async function handleConnectionLost(
214
manager: ServiceManager.IManager,
215
error: ServerConnection.NetworkError
216
) {
217
console.log('Connection lost:', error.message);
218
219
// Use default handler
220
await ConnectionLost(manager, error);
221
222
// Additional custom handling
223
console.log('Connection lost dialog completed');
224
}
225
226
// Providing as a service
227
const connectionLostPlugin: JupyterFrontEndPlugin<IConnectionLost> = {
228
id: 'default-connection-lost',
229
provides: IConnectionLost,
230
activate: (): IConnectionLost => {
231
return ConnectionLost;
232
}
233
};
234
```
235
236
### ITreePathUpdater Type
237
238
Function interface for updating tree path in the application.
239
240
```typescript { .api }
241
/**
242
* Function interface for updating the current tree path
243
* @param treePath - New tree path to set
244
*/
245
type ITreePathUpdater = (treePath: string) => void;
246
247
/**
248
* Service token for tree path updater
249
*/
250
const ITreePathUpdater: Token<ITreePathUpdater>;
251
```
252
253
**Usage Examples:**
254
255
```typescript
256
import { ITreePathUpdater } from "@jupyterlab/application";
257
import { JupyterFrontEndPlugin } from "@jupyterlab/application";
258
259
// Using tree path updater in a plugin
260
const pathUpdaterPlugin: JupyterFrontEndPlugin<void> = {
261
id: 'path-updater-example',
262
autoStart: true,
263
requires: [ITreePathUpdater],
264
activate: (app, updatePath: ITreePathUpdater) => {
265
// Update path when files are opened
266
app.shell.currentChanged.connect((sender, args) => {
267
const widget = args.newValue;
268
if (widget && widget.title.caption) {
269
// Extract path from widget caption
270
const path = widget.title.caption;
271
updatePath(path);
272
console.log('Updated tree path to:', path);
273
}
274
});
275
276
// Update path programmatically
277
const navigateToFile = (filePath: string) => {
278
updatePath(filePath);
279
console.log('Navigated to:', filePath);
280
};
281
282
// Example usage
283
navigateToFile('/notebooks/analysis.ipynb');
284
navigateToFile('/data/dataset.csv');
285
}
286
};
287
288
// Providing a tree path updater implementation
289
const treePathProviderPlugin: JupyterFrontEndPlugin<ITreePathUpdater> = {
290
id: 'tree-path-provider',
291
provides: ITreePathUpdater,
292
activate: (app): ITreePathUpdater => {
293
return (treePath: string) => {
294
// Update application state
295
console.log('Setting tree path:', treePath);
296
297
// Could update URL, breadcrumbs, or internal state
298
if (treePath.startsWith('/')) {
299
document.title = `JupyterLab - ${treePath}`;
300
}
301
302
// Emit custom event for other components
303
const event = new CustomEvent('tree-path-changed', {
304
detail: { path: treePath }
305
});
306
window.dispatchEvent(event);
307
};
308
}
309
};
310
```
311
312
## Advanced Usage Patterns
313
314
### Complex Semantic Command Setup
315
316
Advanced patterns for setting up sophisticated semantic command hierarchies.
317
318
```typescript { .api }
319
// Advanced semantic command patterns
320
interface AdvancedSemanticCommandSetup {
321
/** Hierarchical command organization */
322
commandHierarchy: CommandHierarchy;
323
324
/** Conditional command enabling */
325
conditionalCommands: ConditionalCommandOptions;
326
327
/** Context-sensitive commands */
328
contextSensitiveCommands: ContextSensitiveOptions;
329
}
330
331
interface CommandHierarchy {
332
/** Parent command category */
333
category: string;
334
335
/** Child command definitions */
336
children: SemanticCommandDefinition[];
337
}
338
339
interface ConditionalCommandOptions {
340
/** Conditions for enabling commands */
341
enableConditions: Array<(context: any) => boolean>;
342
343
/** Commands to enable based on conditions */
344
conditionalCommands: SemanticCommandMapping[];
345
}
346
```
347
348
**Complete Advanced Example:**
349
350
```typescript
351
import {
352
addSemanticCommand,
353
ISemanticCommandOptions
354
} from "@jupyterlab/application";
355
import { SemanticCommand } from "@jupyterlab/apputils";
356
357
// Advanced semantic command setup
358
class AdvancedCommandManager {
359
private app: JupyterFrontEnd;
360
private commandCategories: Map<string, SemanticCommand[]> = new Map();
361
362
constructor(app: JupyterFrontEnd) {
363
this.app = app;
364
this.setupCommandHierarchy();
365
}
366
367
private setupCommandHierarchy(): void {
368
// File operations category
369
const fileCommands = [
370
new SemanticCommand({
371
id: 'file:save',
372
selector: '.jp-Document',
373
rank: 100
374
}),
375
new SemanticCommand({
376
id: 'file:save-as',
377
selector: '.jp-Document',
378
rank: 200
379
}),
380
new SemanticCommand({
381
id: 'file:export',
382
selector: '.jp-Notebook',
383
rank: 300
384
})
385
];
386
387
// Edit operations category
388
const editCommands = [
389
new SemanticCommand({
390
id: 'edit:copy',
391
selector: '.jp-CodeCell.jp-mod-active, .jp-MarkdownCell.jp-mod-active',
392
rank: 10
393
}),
394
new SemanticCommand({
395
id: 'edit:paste',
396
selector: '.jp-CodeCell.jp-mod-active, .jp-MarkdownCell.jp-mod-active',
397
rank: 20
398
}),
399
new SemanticCommand({
400
id: 'edit:cut',
401
selector: '.jp-CodeCell.jp-mod-active, .jp-MarkdownCell.jp-mod-active',
402
rank: 30
403
})
404
];
405
406
// View operations category
407
const viewCommands = [
408
new SemanticCommand({
409
id: 'view:toggle-line-numbers',
410
selector: '.jp-CodeCell',
411
rank: 100
412
}),
413
new SemanticCommand({
414
id: 'view:toggle-header',
415
selector: '.jp-Notebook',
416
rank: 200
417
})
418
];
419
420
this.commandCategories.set('file', fileCommands);
421
this.commandCategories.set('edit', editCommands);
422
this.commandCategories.set('view', viewCommands);
423
424
// Register all command categories
425
this.registerCommandCategories();
426
}
427
428
private registerCommandCategories(): void {
429
// File category commands
430
addSemanticCommand({
431
id: 'semantic:file-save',
432
commands: this.app.commands,
433
shell: this.app.shell,
434
semanticCommands: this.commandCategories.get('file')!,
435
default: {
436
execute: 'file:new',
437
label: 'Save Document',
438
caption: 'Save the current document',
439
isEnabled: true
440
},
441
overrides: {
442
icon: 'ui-components:save',
443
iconClass: 'jp-SaveIcon'
444
}
445
});
446
447
// Edit category commands
448
addSemanticCommand({
449
id: 'semantic:edit-clipboard',
450
commands: this.app.commands,
451
shell: this.app.shell,
452
semanticCommands: this.commandCategories.get('edit')!,
453
default: {
454
label: 'Clipboard Operation',
455
caption: 'Perform clipboard operation on selected content',
456
isEnabled: false
457
},
458
overrides: {
459
icon: 'ui-components:edit'
460
}
461
});
462
463
// View category commands with conditional logic
464
addSemanticCommand({
465
id: 'semantic:view-toggle',
466
commands: this.app.commands,
467
shell: this.app.shell,
468
semanticCommands: this.commandCategories.get('view')!,
469
default: {
470
label: 'Toggle View',
471
caption: 'Toggle view settings',
472
isEnabled: true,
473
isVisible: true
474
}
475
});
476
}
477
478
// Method to add new semantic commands dynamically
479
addDynamicCommand(
480
category: string,
481
commandId: string,
482
selector: string,
483
rank: number = 1000
484
): void {
485
const newCommand = new SemanticCommand({
486
id: commandId,
487
selector: selector,
488
rank: rank
489
});
490
491
if (!this.commandCategories.has(category)) {
492
this.commandCategories.set(category, []);
493
}
494
495
this.commandCategories.get(category)!.push(newCommand);
496
497
// Re-register the category
498
addSemanticCommand({
499
id: `semantic:${category}-dynamic`,
500
commands: this.app.commands,
501
shell: this.app.shell,
502
semanticCommands: this.commandCategories.get(category)!,
503
default: {
504
label: `${category} Operations`,
505
isEnabled: true
506
}
507
});
508
}
509
510
// Method to get current command status
511
getCommandStatus(semanticCommandId: string): {
512
isEnabled: boolean;
513
isVisible: boolean;
514
label: string;
515
} {
516
const commands = this.app.commands;
517
return {
518
isEnabled: commands.isEnabled(semanticCommandId),
519
isVisible: commands.isVisible(semanticCommandId),
520
label: commands.label(semanticCommandId)
521
};
522
}
523
}
524
525
// Usage
526
const commandManager = new AdvancedCommandManager(app);
527
528
// Add dynamic commands
529
commandManager.addDynamicCommand(
530
'notebook',
531
'notebook:run-all',
532
'.jp-Notebook',
533
50
534
);
535
536
// Check command status
537
const saveStatus = commandManager.getCommandStatus('semantic:file-save');
538
console.log('Save command status:', saveStatus);
539
```
540
541
### Connection Lost Customization
542
543
Advanced patterns for customizing connection lost behavior.
544
545
```typescript { .api }
546
// Advanced connection lost handling
547
class AdvancedConnectionLostHandler {
548
private retryAttempts: number = 0;
549
private maxRetries: number = 3;
550
private retryDelay: number = 2000;
551
552
async handleConnectionLost(
553
manager: ServiceManager.IManager,
554
error: ServerConnection.NetworkError,
555
translator?: ITranslator
556
): Promise<void> {
557
console.log(`Connection lost (attempt ${this.retryAttempts + 1}):`, error.message);
558
559
// Try automatic reconnection first
560
if (this.retryAttempts < this.maxRetries) {
561
await this.attemptReconnection(manager, error, translator);
562
} else {
563
// Fall back to default dialog
564
await ConnectionLost(manager, error, translator);
565
this.retryAttempts = 0; // Reset for next time
566
}
567
}
568
569
private async attemptReconnection(
570
manager: ServiceManager.IManager,
571
error: ServerConnection.NetworkError,
572
translator?: ITranslator
573
): Promise<void> {
574
this.retryAttempts++;
575
576
// Show subtle notification instead of modal dialog
577
this.showReconnectionNotification(this.retryAttempts, this.maxRetries);
578
579
try {
580
// Wait before retry
581
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
582
583
// Test connection
584
await manager.ready;
585
586
// Success - show success notification
587
this.showReconnectionSuccess();
588
this.retryAttempts = 0;
589
590
} catch (retryError) {
591
// Retry failed
592
if (this.retryAttempts >= this.maxRetries) {
593
// Show final failure dialog
594
await ConnectionLost(manager, error, translator);
595
this.retryAttempts = 0;
596
} else {
597
// Try again
598
await this.attemptReconnection(manager, error, translator);
599
}
600
}
601
}
602
603
private showReconnectionNotification(attempt: number, maxAttempts: number): void {
604
const notification = document.createElement('div');
605
notification.className = 'reconnection-notification';
606
notification.innerHTML = `
607
<div class="reconnection-content">
608
<span class="reconnection-icon">π</span>
609
<span class="reconnection-text">
610
Reconnecting... (${attempt}/${maxAttempts})
611
</span>
612
</div>
613
`;
614
615
document.body.appendChild(notification);
616
617
// Auto-remove after delay
618
setTimeout(() => {
619
if (notification.parentNode) {
620
notification.parentNode.removeChild(notification);
621
}
622
}, this.retryDelay);
623
}
624
625
private showReconnectionSuccess(): void {
626
const notification = document.createElement('div');
627
notification.className = 'success-notification';
628
notification.innerHTML = `
629
<div class="success-content">
630
<span class="success-icon">β </span>
631
<span class="success-text">Reconnected successfully</span>
632
</div>
633
`;
634
635
document.body.appendChild(notification);
636
637
setTimeout(() => {
638
if (notification.parentNode) {
639
notification.parentNode.removeChild(notification);
640
}
641
}, 3000);
642
}
643
}
644
645
// Usage
646
const advancedHandler = new AdvancedConnectionLostHandler();
647
648
const connectionLostPlugin: JupyterFrontEndPlugin<IConnectionLost> = {
649
id: 'advanced-connection-lost',
650
provides: IConnectionLost,
651
activate: (app): IConnectionLost => {
652
return advancedHandler.handleConnectionLost.bind(advancedHandler);
653
}
654
};
655
```
656
657
## Integration Examples
658
659
### Complete Utility Integration
660
661
Example showing how all utility functions work together in a real application.
662
663
```typescript
664
import {
665
addSemanticCommand,
666
ConnectionLost,
667
ITreePathUpdater
668
} from "@jupyterlab/application";
669
670
// Complete integration example
671
const utilityIntegrationPlugin: JupyterFrontEndPlugin<void> = {
672
id: 'utility-integration',
673
autoStart: true,
674
requires: [ITreePathUpdater],
675
activate: (app, updatePath: ITreePathUpdater) => {
676
// Set up semantic commands for file operations
677
const fileCommands = [
678
new SemanticCommand({
679
id: 'file:save',
680
selector: '.jp-Document',
681
rank: 100
682
})
683
];
684
685
addSemanticCommand({
686
id: 'integrated:save',
687
commands: app.commands,
688
shell: app.shell,
689
semanticCommands: fileCommands,
690
default: {
691
label: 'Save',
692
isEnabled: true
693
}
694
});
695
696
// Set up connection lost handling
697
app.serviceManager.connectionFailure.connect(async (sender, error) => {
698
await ConnectionLost(app.serviceManager, error);
699
});
700
701
// Set up tree path updates
702
app.shell.currentChanged.connect((sender, args) => {
703
const widget = args.newValue;
704
if (widget && widget.title.caption) {
705
updatePath(widget.title.caption);
706
}
707
});
708
709
// Integrate all utilities for file operations
710
app.commands.addCommand('integrated:open-file', {
711
execute: async (args: any) => {
712
const filePath = args.path as string;
713
714
// Update tree path
715
updatePath(filePath);
716
717
// Execute semantic save command
718
await app.commands.execute('integrated:save');
719
720
console.log('File operation completed:', filePath);
721
}
722
});
723
}
724
};
725
```