@jupyterlab/notebook-extension 4.7.0-alpha.0 → 4.7.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -3,6 +3,20 @@
3
3
  * @module notebook-extension
4
4
  */
5
5
  import type { JupyterFrontEndPlugin } from '@jupyterlab/application';
6
+ import { Token } from '@lumino/coreutils';
7
+ /**
8
+ * An interface describing how export guidance is presented to users.
9
+ */
10
+ export interface INotebookExportGuidance {
11
+ /**
12
+ * Show guidance for enabling notebook exports.
13
+ */
14
+ showExportHelp(): Promise<void>;
15
+ }
16
+ /**
17
+ * A token describing a service for showing notebook export guidance.
18
+ */
19
+ export declare const INotebookExportGuidance: Token<INotebookExportGuidance>;
6
20
  /**
7
21
  * A plugin providing a CommandEdit status item.
8
22
  */
@@ -19,6 +33,10 @@ export declare const executionIndicator: JupyterFrontEndPlugin<void>;
19
33
  * A plugin providing export commands in the main menu and command palette
20
34
  */
21
35
  export declare const exportPlugin: JupyterFrontEndPlugin<void>;
36
+ /**
37
+ * A plugin providing the default notebook export guidance service.
38
+ */
39
+ export declare const exportGuidanceDialogPlugin: JupyterFrontEndPlugin<INotebookExportGuidance>;
22
40
  /**
23
41
  * A plugin that adds a notebook trust status item to the status bar.
24
42
  */
package/lib/index.js CHANGED
@@ -31,7 +31,7 @@ import { ITableOfContentsRegistry } from '@jupyterlab/toc';
31
31
  import { ITranslator, nullTranslator } from '@jupyterlab/translation';
32
32
  import { addAboveIcon, addBelowIcon, buildIcon, copyIcon, cutIcon, duplicateIcon, fastForwardIcon, IFormRendererRegistry, infoIcon, moveDownIcon, moveUpIcon, notebookIcon, pasteIcon, refreshIcon, runIcon, stopIcon } from '@jupyterlab/ui-components';
33
33
  import { ArrayExt } from '@lumino/algorithm';
34
- import { JSONExt, UUID } from '@lumino/coreutils';
34
+ import { JSONExt, Token, UUID } from '@lumino/coreutils';
35
35
  import { DisposableSet } from '@lumino/disposable';
36
36
  import { MessageLoop } from '@lumino/messaging';
37
37
  import { Panel } from '@lumino/widgets';
@@ -62,6 +62,7 @@ var CommandIDs;
62
62
  CommandIDs.closeAndShutdown = 'notebook:close-and-shutdown';
63
63
  CommandIDs.trust = 'notebook:trust';
64
64
  CommandIDs.exportToFormat = 'notebook:export-to-format';
65
+ CommandIDs.showExportGuidance = 'notebook:show-export-guidance';
65
66
  CommandIDs.run = 'notebook:run-cell';
66
67
  CommandIDs.runAndAdvance = 'notebook:run-cell-and-select-next';
67
68
  CommandIDs.runAndInsert = 'notebook:run-cell-and-insert-below';
@@ -156,6 +157,14 @@ const FACTORY = 'Notebook';
156
157
  * (returned from nbconvert's export list)
157
158
  */
158
159
  const FORMAT_EXCLUDE = ['notebook', 'python', 'custom'];
160
+ /**
161
+ * Documentation page describing notebook export support.
162
+ */
163
+ const NOTEBOOK_EXPORT_DOCS_URL = 'https://jupyterlab.readthedocs.io/en/stable/user/export.html';
164
+ /**
165
+ * A token describing a service for showing notebook export guidance.
166
+ */
167
+ export const INotebookExportGuidance = new Token('@jupyterlab/notebook-extension:INotebookExportGuidance', 'A service for showing notebook export guidance.');
159
168
  /**
160
169
  * Setting Id storing the customized toolbar definition.
161
170
  */
@@ -378,9 +387,8 @@ export const exportPlugin = {
378
387
  description: 'Adds the export notebook commands.',
379
388
  autoStart: true,
380
389
  requires: [ITranslator, INotebookTracker],
381
- optional: [IMainMenu, ICommandPalette],
382
- activate: (app, translator, tracker, mainMenu, palette) => {
383
- var _a;
390
+ optional: [IMainMenu, ICommandPalette, INotebookExportGuidance],
391
+ activate: (app, translator, tracker, mainMenu, palette, exportGuidance) => {
384
392
  const trans = translator.load('jupyterlab');
385
393
  const { commands, shell } = app;
386
394
  const services = app.serviceManager;
@@ -455,63 +463,153 @@ export const exportPlugin = {
455
463
  }
456
464
  }
457
465
  });
466
+ commands.addCommand(CommandIDs.showExportGuidance, {
467
+ label: trans.__('Enable notebook exports'),
468
+ execute: () => {
469
+ if (exportGuidance) {
470
+ return exportGuidance.showExportHelp();
471
+ }
472
+ window.open(NOTEBOOK_EXPORT_DOCS_URL, '_blank', 'noopener,noreferrer');
473
+ return undefined;
474
+ },
475
+ describedBy: {
476
+ args: {
477
+ type: 'object',
478
+ properties: {}
479
+ }
480
+ }
481
+ });
458
482
  // Add a notebook group to the File menu.
459
- let exportTo;
460
- if (mainMenu) {
461
- exportTo = (_a = mainMenu.fileMenu.items.find(item => {
483
+ const fileMenu = mainMenu === null || mainMenu === void 0 ? void 0 : mainMenu.fileMenu;
484
+ const getExportMenu = () => {
485
+ var _a, _b;
486
+ return ((_b = (_a = fileMenu === null || fileMenu === void 0 ? void 0 : fileMenu.items.find(item => {
462
487
  var _a;
463
488
  return item.type === 'submenu' &&
464
489
  ((_a = item.submenu) === null || _a === void 0 ? void 0 : _a.id) === 'jp-mainmenu-file-notebookexport';
465
- })) === null || _a === void 0 ? void 0 : _a.submenu;
466
- }
490
+ })) === null || _a === void 0 ? void 0 : _a.submenu) !== null && _b !== void 0 ? _b : undefined);
491
+ };
467
492
  let formatsInitialized = false;
493
+ let paletteInitialized = false;
494
+ let exportFormats = null;
495
+ const updateExportMenu = () => {
496
+ const exportTo = getExportMenu();
497
+ if (!exportTo || !exportFormats) {
498
+ return;
499
+ }
500
+ exportTo.clearItems();
501
+ if (exportFormats.length === 0) {
502
+ exportTo.addItem({
503
+ command: CommandIDs.showExportGuidance
504
+ });
505
+ return;
506
+ }
507
+ exportFormats.forEach(({ format, label }) => {
508
+ exportTo.addItem({
509
+ command: CommandIDs.exportToFormat,
510
+ args: {
511
+ format,
512
+ label,
513
+ isPalette: false
514
+ }
515
+ });
516
+ });
517
+ if (palette && !paletteInitialized) {
518
+ const category = trans.__('Notebook Operations');
519
+ exportFormats.forEach(({ format, label }) => {
520
+ palette.addItem({
521
+ command: CommandIDs.exportToFormat,
522
+ category,
523
+ args: {
524
+ format,
525
+ label,
526
+ isPalette: true
527
+ }
528
+ });
529
+ });
530
+ paletteInitialized = true;
531
+ }
532
+ };
468
533
  /** Request formats only when a notebook might use them. */
469
534
  const maybeInitializeFormats = async () => {
470
535
  if (formatsInitialized) {
536
+ updateExportMenu();
537
+ return;
538
+ }
539
+ if (tracker.size === 0) {
471
540
  return;
472
541
  }
473
542
  tracker.widgetAdded.disconnect(maybeInitializeFormats);
474
543
  formatsInitialized = true;
475
- const response = await services.nbconvert.getExportFormats(false);
476
- if (!response) {
477
- return;
544
+ let response = null;
545
+ try {
546
+ response = await services.nbconvert.getExportFormats(false);
547
+ }
548
+ catch (_a) {
549
+ // Ignore fetch errors and fallback to export guidance.
478
550
  }
479
551
  const formatLabels = Private.getFormatLabels(translator);
480
- // Convert export list to palette and menu items.
481
- const formatList = Object.keys(response);
482
- formatList.forEach(function (key) {
552
+ // Convert export list to menu and palette items.
553
+ exportFormats = Object.keys(response !== null && response !== void 0 ? response : {})
554
+ .filter(key => !FORMAT_EXCLUDE.includes(key))
555
+ .map(key => {
483
556
  const formattedKey = key[0].toLocaleUpperCase() + key.slice(1);
484
557
  const capCaseKey = trans.__(formattedKey);
485
558
  const labelStr = formatLabels[key] ? formatLabels[key] : capCaseKey;
486
- let args = {
559
+ return {
487
560
  format: key,
488
- label: labelStr,
489
- isPalette: false
561
+ label: labelStr
490
562
  };
491
- if (FORMAT_EXCLUDE.indexOf(key) === -1) {
492
- if (exportTo) {
493
- exportTo.addItem({
494
- command: CommandIDs.exportToFormat,
495
- args: args
496
- });
497
- }
498
- if (palette) {
499
- args = {
500
- format: key,
501
- label: labelStr,
502
- isPalette: true
503
- };
504
- const category = trans.__('Notebook Operations');
505
- palette.addItem({
506
- command: CommandIDs.exportToFormat,
507
- category,
508
- args
509
- });
510
- }
511
- }
512
563
  });
564
+ updateExportMenu();
513
565
  };
514
566
  tracker.widgetAdded.connect(maybeInitializeFormats);
567
+ if (tracker.size > 0) {
568
+ void maybeInitializeFormats();
569
+ }
570
+ void app.restored.then(() => {
571
+ void maybeInitializeFormats();
572
+ });
573
+ }
574
+ };
575
+ /**
576
+ * A plugin providing the default notebook export guidance service.
577
+ */
578
+ export const exportGuidanceDialogPlugin = {
579
+ id: '@jupyterlab/notebook-extension:export-guidance-dialog',
580
+ description: 'Provides the default notebook export guidance shown when no exporters are available.',
581
+ provides: INotebookExportGuidance,
582
+ autoStart: true,
583
+ requires: [ITranslator],
584
+ activate: (app, translator) => {
585
+ const trans = translator.load('jupyterlab');
586
+ const { commands } = app;
587
+ const openExportDocs = (url, docsLabel) => {
588
+ if (commands.hasCommand('help:open')) {
589
+ return commands.execute('help:open', {
590
+ url,
591
+ text: docsLabel,
592
+ newBrowserTab: true
593
+ });
594
+ }
595
+ window.open(url, '_blank', 'noopener,noreferrer');
596
+ return undefined;
597
+ };
598
+ return {
599
+ showExportHelp: async () => {
600
+ const result = await showDialog({
601
+ title: trans.__('Notebook exports are unavailable'),
602
+ body: trans.__('No notebook export formats are currently available. To enable exports, install nbconvert in the server environment or use another exporter supported by your deployment.'),
603
+ buttons: [
604
+ Dialog.cancelButton({ label: trans.__('Close') }),
605
+ Dialog.okButton({ label: trans.__('Open Documentation') })
606
+ ]
607
+ });
608
+ if (result.button.accept) {
609
+ void openExportDocs(NOTEBOOK_EXPORT_DOCS_URL, trans.__('Notebook Export Documentation'));
610
+ }
611
+ }
612
+ };
515
613
  }
516
614
  };
517
615
  /**
@@ -902,6 +1000,7 @@ const plugins = [
902
1000
  trackerPlugin,
903
1001
  pageHandlerPlugin,
904
1002
  executionIndicator,
1003
+ exportGuidanceDialogPlugin,
905
1004
  exportPlugin,
906
1005
  tools,
907
1006
  cellCounterItem,
@@ -1643,13 +1742,17 @@ function activateNotebookHandler(app, factory, extensions, executor, palette, de
1643
1742
  setSideBySideOutputRatio(factory.notebookConfig.sideBySideOutputRatio);
1644
1743
  const sideBySideMarginStyle = `.jp-mod-sideBySide.jp-Notebook .jp-Notebook-cell {
1645
1744
  margin-left: ${factory.notebookConfig.sideBySideLeftMarginOverride} !important;
1646
- margin-right: ${factory.notebookConfig.sideBySideRightMarginOverride} !important;`;
1745
+ margin-right: ${factory.notebookConfig.sideBySideRightMarginOverride} !important;
1746
+ }`;
1647
1747
  const sideBySideMarginTag = document.getElementById(SIDE_BY_SIDE_STYLE_ID);
1648
1748
  if (sideBySideMarginTag) {
1649
- sideBySideMarginTag.innerText = sideBySideMarginStyle;
1749
+ sideBySideMarginTag.textContent = sideBySideMarginStyle;
1650
1750
  }
1651
1751
  else {
1652
- document.head.insertAdjacentHTML('beforeend', `<style id="${SIDE_BY_SIDE_STYLE_ID}">${sideBySideMarginStyle}}</style>`);
1752
+ const style = document.createElement('style');
1753
+ style.id = SIDE_BY_SIDE_STYLE_ID;
1754
+ style.textContent = sideBySideMarginStyle;
1755
+ document.head.appendChild(style);
1653
1756
  }
1654
1757
  factory.autoStartDefault = settings.get('autoStartDefaultKernel')
1655
1758
  .composite;