@jupyterlab/notebook-extension 4.6.0-alpha.5 → 4.6.0-beta.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.js CHANGED
@@ -1,14 +1,15 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
4
  /**
4
5
  * @packageDocumentation
5
6
  * @module notebook-extension
6
7
  */
7
8
  import { ILabShell, ILayoutRestorer, IRouter } from '@jupyterlab/application';
8
- import { createToolbarFactory, Dialog, ICommandPalette, IKernelStatusModel, InputDialog, ISanitizer, ISessionContextDialogs, IToolbarWidgetRegistry, MainAreaWidget, Sanitizer, SemanticCommand, SessionContextDialogs, showDialog, Toolbar, WidgetTracker } from '@jupyterlab/apputils';
9
+ import { Clipboard, createToolbarFactory, Dialog, ICommandPalette, IKernelStatusModel, InputDialog, ISanitizer, ISessionContextDialogs, IToolbarWidgetRegistry, MainAreaWidget, Sanitizer, SemanticCommand, SessionContextDialogs, showDialog, Toolbar, WidgetTracker } from '@jupyterlab/apputils';
9
10
  import { MarkdownCell } from '@jupyterlab/cells';
10
11
  import { IEditorServices, IPositionModel } from '@jupyterlab/codeeditor';
11
- import { PageConfig } from '@jupyterlab/coreutils';
12
+ import { compareVersions, PageConfig } from '@jupyterlab/coreutils';
12
13
  import { IEditorExtensionRegistry, IEditorLanguageRegistry } from '@jupyterlab/codemirror';
13
14
  import { ICompletionProviderManager } from '@jupyterlab/completer';
14
15
  import { IDocumentManager } from '@jupyterlab/docmanager';
@@ -127,6 +128,8 @@ var CommandIDs;
127
128
  CommandIDs.disableOutputScrolling = 'notebook:disable-output-scrolling';
128
129
  CommandIDs.toggleOutputScrolling = 'notebook:toggle-output-scrolling';
129
130
  CommandIDs.selectLastRunCell = 'notebook:select-last-run-cell';
131
+ CommandIDs.selectLastModifiedCell = 'notebook:select-last-modified-cell';
132
+ CommandIDs.selectNextModifiedCell = 'notebook:select-next-modified-cell';
130
133
  CommandIDs.replaceSelection = 'notebook:replace-selection';
131
134
  CommandIDs.autoClosingBrackets = 'notebook:toggle-autoclosing-brackets';
132
135
  CommandIDs.toggleCollapseCmd = 'notebook:toggle-heading-collapse';
@@ -139,6 +142,9 @@ var CommandIDs;
139
142
  CommandIDs.accessPreviousHistory = 'notebook:access-previous-history-entry';
140
143
  CommandIDs.accessNextHistory = 'notebook:access-next-history-entry';
141
144
  CommandIDs.virtualScrollbar = 'notebook:toggle-virtual-scrollbar';
145
+ CommandIDs.copySelectedtext = 'notebook:copy-selected-text';
146
+ CommandIDs.pasteText = 'notebook:paste-text';
147
+ CommandIDs.cutSelectedtext = 'notebook:cut-selected-text';
142
148
  })(CommandIDs || (CommandIDs = {}));
143
149
  /**
144
150
  * The name of the factory that creates notebooks.
@@ -391,23 +397,40 @@ export const exportPlugin = {
391
397
  : formatLabel;
392
398
  },
393
399
  execute: async (args) => {
394
- var _a, _b;
400
+ var _a, _b, _c;
395
401
  const current = getCurrent(tracker, shell, args);
396
402
  if (!current) {
397
403
  return;
398
404
  }
399
405
  const { context } = current;
406
+ const serverVersion = PageConfig.getNotebookVersion();
407
+ const supportsHTMLSanitizationOption = compareVersions(serverVersion, [4, 0, 0]) <
408
+ 0 /* Jupyter Server only */ &&
409
+ compareVersions(serverVersion, [2, 18, 0]) >= 0;
410
+ let sanitizeHtml = false;
411
+ if (args['format'] === 'html' && supportsHTMLSanitizationOption) {
412
+ const result = await InputDialog.getBoolean({
413
+ title: trans.__('Export as HTML'),
414
+ label: trans.__('Sanitize HTML output'),
415
+ value: true
416
+ });
417
+ if (!result.button.accept) {
418
+ return;
419
+ }
420
+ sanitizeHtml = (_a = result.value) !== null && _a !== void 0 ? _a : false;
421
+ }
400
422
  const exportOptions = {
401
423
  format: args['format'],
402
424
  path: current.context.path,
403
425
  exporterOptions: {
404
- download: true
426
+ download: true,
427
+ sanitizeHtml
405
428
  }
406
429
  };
407
430
  if (context.model.dirty && !context.model.readOnly) {
408
431
  await context.save();
409
432
  }
410
- return (_b = (_a = services.nbconvert).exportAs) === null || _b === void 0 ? void 0 : _b.call(_a, exportOptions);
433
+ return (_c = (_b = services.nbconvert).exportAs) === null || _c === void 0 ? void 0 : _c.call(_b, exportOptions);
411
434
  },
412
435
  isEnabled,
413
436
  describedBy: {
@@ -654,7 +677,7 @@ const tocPlugin = {
654
677
  optional: [IMarkdownParser, ISettingRegistry],
655
678
  autoStart: true,
656
679
  activate: (app, tracker, tocRegistry, sanitizer, mdParser, settingRegistry) => {
657
- const nbTocFactory = new NotebookToCFactory(tracker, mdParser, sanitizer);
680
+ const nbTocFactory = new NotebookToCFactory(tracker, mdParser, sanitizer, app.commands);
658
681
  tocRegistry.add(nbTocFactory);
659
682
  if (settingRegistry) {
660
683
  Promise.all([app.restored, settingRegistry.load(trackerPlugin.id)])
@@ -842,7 +865,8 @@ const openWithNoKernelPlugin = {
842
865
  label: trans.__('Notebook (no kernel)'),
843
866
  factory: FACTORY,
844
867
  kernelPreference: {
845
- shouldStart: false
868
+ shouldStart: false,
869
+ shouldReuse: false
846
870
  }
847
871
  }
848
872
  }));
@@ -904,6 +928,10 @@ function activateNotebookTools(app, tracker, editorServices, languages, state, t
904
928
  return true;
905
929
  };
906
930
  notebookTools.title.icon = buildIcon;
931
+ notebookTools.title.dataset = {
932
+ ...notebookTools.title.dataset,
933
+ jpTabLabel: trans.__('Notebook Tools')
934
+ };
907
935
  notebookTools.title.caption = trans.__('Notebook Tools');
908
936
  notebookTools.id = id;
909
937
  MessageLoop.installMessageHook(notebookTools, hook);
@@ -1878,6 +1906,9 @@ function addCommands(app, tracker, translator, sessionDialogs, settings, isEnabl
1878
1906
  tracker.selectionChanged.connect(() => {
1879
1907
  commands.notifyCommandChanged(CommandIDs.duplicateBelow);
1880
1908
  commands.notifyCommandChanged(CommandIDs.deleteCell);
1909
+ commands.notifyCommandChanged(CommandIDs.copySelectedtext);
1910
+ commands.notifyCommandChanged(CommandIDs.pasteText);
1911
+ commands.notifyCommandChanged(CommandIDs.cutSelectedtext);
1881
1912
  commands.notifyCommandChanged(CommandIDs.copy);
1882
1913
  commands.notifyCommandChanged(CommandIDs.cut);
1883
1914
  commands.notifyCommandChanged(CommandIDs.pasteBelow);
@@ -1894,6 +1925,16 @@ function addCommands(app, tracker, translator, sessionDialogs, settings, isEnabl
1894
1925
  commands.notifyCommandChanged(CommandIDs.deleteCell);
1895
1926
  commands.notifyCommandChanged(CommandIDs.moveUp);
1896
1927
  commands.notifyCommandChanged(CommandIDs.moveDown);
1928
+ commands.notifyCommandChanged(CommandIDs.selectLastModifiedCell);
1929
+ commands.notifyCommandChanged(CommandIDs.selectNextModifiedCell);
1930
+ });
1931
+ tracker.widgetAdded.connect((_, panel) => {
1932
+ panel.content.stateChanged.connect((_, args) => {
1933
+ if (args.name === 'lastModifiedCellStack') {
1934
+ commands.notifyCommandChanged(CommandIDs.selectLastModifiedCell);
1935
+ commands.notifyCommandChanged(CommandIDs.selectNextModifiedCell);
1936
+ }
1937
+ });
1897
1938
  });
1898
1939
  commands.addCommand(CommandIDs.runAndAdvance, {
1899
1940
  label: args => {
@@ -2589,6 +2630,143 @@ function addCommands(app, tracker, translator, sessionDialogs, settings, isEnabl
2589
2630
  }
2590
2631
  }
2591
2632
  });
2633
+ commands.addCommand(CommandIDs.copySelectedtext, {
2634
+ label: trans.__('Copy Selected Text'),
2635
+ caption: trans.__('Copy selected text from the active cell or output area'),
2636
+ execute: async (args) => {
2637
+ var _a;
2638
+ const current = getCurrent(tracker, shell, args);
2639
+ if (!current) {
2640
+ return;
2641
+ }
2642
+ // copying from the editor (input area)
2643
+ const editor = (_a = current.content.activeCell) === null || _a === void 0 ? void 0 : _a.editor;
2644
+ if (editor) {
2645
+ const selection = editor.getSelection();
2646
+ const start = editor.getOffsetAt(selection.start);
2647
+ const end = editor.getOffsetAt(selection.end);
2648
+ const text = editor.model.sharedModel.getSource().slice(start, end);
2649
+ if (text) {
2650
+ await navigator.clipboard.writeText(text);
2651
+ return;
2652
+ }
2653
+ }
2654
+ // fallback to DOM selection (output)
2655
+ const domSelection = window.getSelection();
2656
+ const selectedText = domSelection === null || domSelection === void 0 ? void 0 : domSelection.toString();
2657
+ if (selectedText && selectedText.trim().length > 0) {
2658
+ await navigator.clipboard.writeText(selectedText);
2659
+ }
2660
+ },
2661
+ isEnabled: args => {
2662
+ var _a;
2663
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2664
+ if (!current) {
2665
+ return false;
2666
+ }
2667
+ const editor = (_a = current.content.activeCell) === null || _a === void 0 ? void 0 : _a.editor;
2668
+ if (editor) {
2669
+ const selection = editor.getSelection();
2670
+ const hasEditorSelection = selection.start.line !== selection.end.line ||
2671
+ selection.start.column !== selection.end.column;
2672
+ if (hasEditorSelection) {
2673
+ return true;
2674
+ }
2675
+ }
2676
+ // Check for text selection in output area
2677
+ const domSelection = window.getSelection();
2678
+ return !!domSelection && domSelection.toString().trim().length > 0;
2679
+ },
2680
+ describedBy: {
2681
+ args: {
2682
+ type: 'object',
2683
+ properties: {}
2684
+ }
2685
+ }
2686
+ });
2687
+ commands.addCommand(CommandIDs.cutSelectedtext, {
2688
+ label: trans.__('Cut Selected Text'),
2689
+ caption: trans.__('Cut selected text from the active cell or output area'),
2690
+ execute: async (args) => {
2691
+ var _a, _b;
2692
+ const current = getCurrent(tracker, shell, args);
2693
+ if (!current) {
2694
+ return;
2695
+ }
2696
+ const editor = (_a = current.content.activeCell) === null || _a === void 0 ? void 0 : _a.editor;
2697
+ if (editor) {
2698
+ const selection = editor.getSelection();
2699
+ const start = editor.getOffsetAt(selection.start);
2700
+ const end = editor.getOffsetAt(selection.end);
2701
+ const text = editor.model.sharedModel.getSource().slice(start, end);
2702
+ if (text) {
2703
+ await navigator.clipboard.writeText(text);
2704
+ (_b = editor.replaceSelection) === null || _b === void 0 ? void 0 : _b.call(editor, '');
2705
+ return;
2706
+ }
2707
+ }
2708
+ },
2709
+ isEnabled: args => {
2710
+ var _a;
2711
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2712
+ if (!current) {
2713
+ return false;
2714
+ }
2715
+ const editor = (_a = current.content.activeCell) === null || _a === void 0 ? void 0 : _a.editor;
2716
+ if (!editor) {
2717
+ return false;
2718
+ }
2719
+ const selection = editor.getSelection();
2720
+ const hasEditorSelection = selection.start.line !== selection.end.line ||
2721
+ selection.start.column !== selection.end.column;
2722
+ return hasEditorSelection;
2723
+ },
2724
+ describedBy: {
2725
+ args: {
2726
+ type: 'object',
2727
+ properties: {}
2728
+ }
2729
+ }
2730
+ });
2731
+ commands.addCommand(CommandIDs.pasteText, {
2732
+ label: trans.__('Paste Text'),
2733
+ caption: trans.__('Paste text from the clipboard into the active cell editor'),
2734
+ execute: async (args) => {
2735
+ var _a, _b;
2736
+ const current = getCurrent(tracker, shell, args);
2737
+ if (!current) {
2738
+ return;
2739
+ }
2740
+ const editor = (_a = current.content.activeCell) === null || _a === void 0 ? void 0 : _a.editor;
2741
+ if (!editor) {
2742
+ return;
2743
+ }
2744
+ try {
2745
+ const text = await navigator.clipboard.readText();
2746
+ if (text) {
2747
+ (_b = editor.replaceSelection) === null || _b === void 0 ? void 0 : _b.call(editor, text);
2748
+ }
2749
+ }
2750
+ catch (err) {
2751
+ // browser limitation fallback (e.g Firefox)
2752
+ Clipboard.showPasteUnavailableDialog(trans);
2753
+ }
2754
+ },
2755
+ isEnabled: args => {
2756
+ var _a;
2757
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2758
+ if (!current) {
2759
+ return false;
2760
+ }
2761
+ return !!((_a = current.content.activeCell) === null || _a === void 0 ? void 0 : _a.editor);
2762
+ },
2763
+ describedBy: {
2764
+ args: {
2765
+ type: 'object',
2766
+ properties: {}
2767
+ }
2768
+ }
2769
+ });
2592
2770
  commands.addCommand(CommandIDs.split, {
2593
2771
  label: trans.__('Split Cell'),
2594
2772
  execute: args => {
@@ -2615,7 +2793,15 @@ function addCommands(app, tracker, translator, sessionDialogs, settings, isEnabl
2615
2793
  return NotebookActions.mergeCells(current.content, false, addExtraLine, translator);
2616
2794
  }
2617
2795
  },
2618
- isEnabled,
2796
+ isVisible: args => {
2797
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2798
+ if (!current) {
2799
+ return false;
2800
+ }
2801
+ // Enable only if more than one cell is selected
2802
+ const notebook = current.content;
2803
+ return notebook && notebook.selectedCells.length > 1;
2804
+ },
2619
2805
  describedBy: {
2620
2806
  args: {
2621
2807
  type: 'object',
@@ -3534,6 +3720,44 @@ function addCommands(app, tracker, translator, sessionDialogs, settings, isEnabl
3534
3720
  }
3535
3721
  }
3536
3722
  });
3723
+ commands.addCommand(CommandIDs.selectLastModifiedCell, {
3724
+ label: trans.__('Select Last Modified Cell'),
3725
+ execute: async (args) => {
3726
+ const current = getCurrent(tracker, shell, args);
3727
+ if (current) {
3728
+ await NotebookActions.selectLastModifiedCell(current.content);
3729
+ }
3730
+ },
3731
+ isEnabled: args => {
3732
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
3733
+ return !!current && current.content.hasNavigableModifiedCellBack();
3734
+ },
3735
+ describedBy: {
3736
+ args: {
3737
+ type: 'object',
3738
+ properties: {}
3739
+ }
3740
+ }
3741
+ });
3742
+ commands.addCommand(CommandIDs.selectNextModifiedCell, {
3743
+ label: trans.__('Select Next Modified Cell'),
3744
+ execute: async (args) => {
3745
+ const current = getCurrent(tracker, shell, args);
3746
+ if (current) {
3747
+ await NotebookActions.selectNextModifiedCell(current.content);
3748
+ }
3749
+ },
3750
+ isEnabled: args => {
3751
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
3752
+ return !!current && current.content.hasNavigableModifiedCellForward();
3753
+ },
3754
+ describedBy: {
3755
+ args: {
3756
+ type: 'object',
3757
+ properties: {}
3758
+ }
3759
+ }
3760
+ });
3537
3761
  commands.addCommand(CommandIDs.replaceSelection, {
3538
3762
  label: trans.__('Replace Selection in Notebook Cell'),
3539
3763
  execute: args => {
@@ -3764,6 +3988,9 @@ function populatePalette(palette, translator) {
3764
3988
  CommandIDs.pasteAbove,
3765
3989
  CommandIDs.pasteAndReplace,
3766
3990
  CommandIDs.deleteCell,
3991
+ CommandIDs.copySelectedtext,
3992
+ CommandIDs.pasteText,
3993
+ CommandIDs.cutSelectedtext,
3767
3994
  CommandIDs.split,
3768
3995
  CommandIDs.merge,
3769
3996
  CommandIDs.mergeAbove,
@@ -3802,7 +4029,9 @@ function populatePalette(palette, translator) {
3802
4029
  CommandIDs.toggleRenderSideBySideCurrentNotebook,
3803
4030
  CommandIDs.setSideBySideRatio,
3804
4031
  CommandIDs.enableOutputScrolling,
3805
- CommandIDs.disableOutputScrolling
4032
+ CommandIDs.disableOutputScrolling,
4033
+ CommandIDs.selectLastModifiedCell,
4034
+ CommandIDs.selectNextModifiedCell
3806
4035
  ].forEach(command => {
3807
4036
  palette.addItem({ command, category });
3808
4037
  });