@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyterlab/notebook-extension",
3
- "version": "4.6.0-alpha.5",
3
+ "version": "4.6.0-beta.1",
4
4
  "description": "JupyterLab - Notebook Extension",
5
5
  "homepage": "https://github.com/jupyterlab/jupyterlab",
6
6
  "bugs": {
@@ -36,44 +36,44 @@
36
36
  "watch": "tsc -b --watch"
37
37
  },
38
38
  "dependencies": {
39
- "@jupyter/ydoc": "^3.1.0",
40
- "@jupyterlab/application": "^4.6.0-alpha.5",
41
- "@jupyterlab/apputils": "^4.7.0-alpha.5",
42
- "@jupyterlab/cell-toolbar": "^4.6.0-alpha.5",
43
- "@jupyterlab/cells": "^4.6.0-alpha.5",
44
- "@jupyterlab/codeeditor": "^4.6.0-alpha.5",
45
- "@jupyterlab/codemirror": "^4.6.0-alpha.5",
46
- "@jupyterlab/completer": "^4.6.0-alpha.5",
47
- "@jupyterlab/coreutils": "^6.6.0-alpha.5",
48
- "@jupyterlab/docmanager": "^4.6.0-alpha.5",
49
- "@jupyterlab/docmanager-extension": "^4.6.0-alpha.5",
50
- "@jupyterlab/docregistry": "^4.6.0-alpha.5",
51
- "@jupyterlab/documentsearch": "^4.6.0-alpha.5",
52
- "@jupyterlab/filebrowser": "^4.6.0-alpha.5",
53
- "@jupyterlab/launcher": "^4.6.0-alpha.5",
54
- "@jupyterlab/logconsole": "^4.6.0-alpha.5",
55
- "@jupyterlab/lsp": "^4.6.0-alpha.5",
56
- "@jupyterlab/mainmenu": "^4.6.0-alpha.5",
57
- "@jupyterlab/metadataform": "^4.6.0-alpha.5",
58
- "@jupyterlab/nbformat": "^4.6.0-alpha.5",
59
- "@jupyterlab/notebook": "^4.6.0-alpha.5",
60
- "@jupyterlab/observables": "^5.6.0-alpha.5",
61
- "@jupyterlab/property-inspector": "^4.6.0-alpha.5",
62
- "@jupyterlab/rendermime": "^4.6.0-alpha.5",
63
- "@jupyterlab/services": "^7.6.0-alpha.5",
64
- "@jupyterlab/settingregistry": "^4.6.0-alpha.5",
65
- "@jupyterlab/statedb": "^4.6.0-alpha.5",
66
- "@jupyterlab/statusbar": "^4.6.0-alpha.5",
67
- "@jupyterlab/toc": "^6.6.0-alpha.5",
68
- "@jupyterlab/translation": "^4.6.0-alpha.5",
69
- "@jupyterlab/ui-components": "^4.6.0-alpha.5",
39
+ "@jupyter/ydoc": "^4.0.0-a3",
40
+ "@jupyterlab/application": "^4.6.0-beta.1",
41
+ "@jupyterlab/apputils": "^4.7.0-beta.1",
42
+ "@jupyterlab/cell-toolbar": "^4.6.0-beta.1",
43
+ "@jupyterlab/cells": "^4.6.0-beta.1",
44
+ "@jupyterlab/codeeditor": "^4.6.0-beta.1",
45
+ "@jupyterlab/codemirror": "^4.6.0-beta.1",
46
+ "@jupyterlab/completer": "^4.6.0-beta.1",
47
+ "@jupyterlab/coreutils": "^6.6.0-beta.1",
48
+ "@jupyterlab/docmanager": "^4.6.0-beta.1",
49
+ "@jupyterlab/docmanager-extension": "^4.6.0-beta.1",
50
+ "@jupyterlab/docregistry": "^4.6.0-beta.1",
51
+ "@jupyterlab/documentsearch": "^4.6.0-beta.1",
52
+ "@jupyterlab/filebrowser": "^4.6.0-beta.1",
53
+ "@jupyterlab/launcher": "^4.6.0-beta.1",
54
+ "@jupyterlab/logconsole": "^4.6.0-beta.1",
55
+ "@jupyterlab/lsp": "^4.6.0-beta.1",
56
+ "@jupyterlab/mainmenu": "^4.6.0-beta.1",
57
+ "@jupyterlab/metadataform": "^4.6.0-beta.1",
58
+ "@jupyterlab/nbformat": "^4.6.0-beta.1",
59
+ "@jupyterlab/notebook": "^4.6.0-beta.1",
60
+ "@jupyterlab/observables": "^5.6.0-beta.1",
61
+ "@jupyterlab/property-inspector": "^4.6.0-beta.1",
62
+ "@jupyterlab/rendermime": "^4.6.0-beta.1",
63
+ "@jupyterlab/services": "^7.6.0-beta.1",
64
+ "@jupyterlab/settingregistry": "^4.6.0-beta.1",
65
+ "@jupyterlab/statedb": "^4.6.0-beta.1",
66
+ "@jupyterlab/statusbar": "^4.6.0-beta.1",
67
+ "@jupyterlab/toc": "^6.6.0-beta.1",
68
+ "@jupyterlab/translation": "^4.6.0-beta.1",
69
+ "@jupyterlab/ui-components": "^4.6.0-beta.1",
70
70
  "@lumino/algorithm": "^2.0.4",
71
71
  "@lumino/commands": "^2.3.3",
72
72
  "@lumino/coreutils": "^2.2.2",
73
73
  "@lumino/disposable": "^2.1.5",
74
74
  "@lumino/messaging": "^2.0.4",
75
75
  "@lumino/polling": "^2.1.5",
76
- "@lumino/widgets": "^2.7.5",
76
+ "@lumino/widgets": "^2.8.0",
77
77
  "@rjsf/utils": "^5.13.4",
78
78
  "react": "^18.2.0"
79
79
  },
package/schema/panel.json CHANGED
@@ -88,7 +88,10 @@
88
88
  "type": {
89
89
  "title": "Item type",
90
90
  "type": "string",
91
- "enum": ["command", "spacer"]
91
+ "oneOf": [
92
+ { "const": "command", "title": "Command" },
93
+ { "const": "spacer", "title": "Spacer" }
94
+ ]
92
95
  },
93
96
  "rank": {
94
97
  "title": "Item rank",
@@ -250,6 +250,21 @@
250
250
  "selector": ".jp-Notebook .jp-Cell",
251
251
  "rank": 3
252
252
  },
253
+ {
254
+ "command": "notebook:copy-selected-text",
255
+ "selector": ".jp-Notebook .jp-Cell",
256
+ "rank": 3
257
+ },
258
+ {
259
+ "command": "notebook:cut-selected-text",
260
+ "selector": ".jp-Notebook .jp-InputArea-editor",
261
+ "rank": 3
262
+ },
263
+ {
264
+ "command": "notebook:paste-text",
265
+ "selector": ".jp-Notebook .jp-InputArea-editor",
266
+ "rank": 3
267
+ },
253
268
  {
254
269
  "type": "separator",
255
270
  "selector": ".jp-Notebook .jp-Cell",
@@ -305,11 +320,6 @@
305
320
  "selector": ".jp-Notebook .jp-CodeCell",
306
321
  "rank": 12
307
322
  },
308
- {
309
- "command": "notebook:clear-all-cell-outputs",
310
- "selector": ".jp-Notebook",
311
- "rank": 13
312
- },
313
323
  {
314
324
  "type": "separator",
315
325
  "selector": ".jp-Notebook",
@@ -699,7 +709,11 @@
699
709
  "title": "Default cell type",
700
710
  "description": "The default type (markdown, code, or raw) for new cells",
701
711
  "type": "string",
702
- "enum": ["code", "markdown", "raw"],
712
+ "oneOf": [
713
+ { "const": "code", "title": "Code" },
714
+ { "const": "markdown", "title": "Markdown" },
715
+ { "const": "raw", "title": "Raw" }
716
+ ],
703
717
  "default": "code"
704
718
  },
705
719
  "autoStartDefaultKernel": {
@@ -717,7 +731,10 @@
717
731
  "inputHistoryScope": {
718
732
  "type": "string",
719
733
  "default": "global",
720
- "enum": ["global", "session"],
734
+ "oneOf": [
735
+ { "const": "global", "title": "Global" },
736
+ { "const": "session", "title": "Session" }
737
+ ],
721
738
  "title": "Input History Scope",
722
739
  "description": "Whether the line history for standard input (e.g. the ipdb prompt) should kept separately for different kernel sessions (`session`) or combined (`global`)."
723
740
  },
@@ -813,7 +830,10 @@
813
830
  "renderingLayout": {
814
831
  "title": "Rendering Layout",
815
832
  "description": "Global setting to define the rendering layout in notebooks. 'default' or 'side-by-side' are supported.",
816
- "enum": ["default", "side-by-side"],
833
+ "oneOf": [
834
+ { "const": "default", "title": "Default" },
835
+ { "const": "side-by-side", "title": "Side by side" }
836
+ ],
817
837
  "default": "default"
818
838
  },
819
839
  "sideBySideLeftMarginOverride": {
@@ -838,7 +858,12 @@
838
858
  "windowingMode": {
839
859
  "title": "Windowing mode",
840
860
  "description": "- 'defer': Improve loading time - Wait for idle CPU cycles to attach out of viewport cells\n- 'full': Best performance with side effects - Attach to the DOM only cells in viewport\n- 'none': Worst performance without side effects - Attach all cells to the viewport\n- 'contentVisibility': Browser-optimized rendering - Use content-visibility to skip offscreen cells",
841
- "enum": ["defer", "full", "none", "contentVisibility"],
861
+ "oneOf": [
862
+ { "const": "defer", "title": "Defer" },
863
+ { "const": "full", "title": "Full" },
864
+ { "const": "none", "title": "None" },
865
+ { "const": "contentVisibility", "title": "Content visibility" }
866
+ ],
842
867
  "default": "contentVisibility"
843
868
  },
844
869
  "accessKernelHistory": {
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
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
@@ -14,6 +15,7 @@ import type {
14
15
  import { ILabShell, ILayoutRestorer, IRouter } from '@jupyterlab/application';
15
16
  import type { ISessionContext } from '@jupyterlab/apputils';
16
17
  import {
18
+ Clipboard,
17
19
  createToolbarFactory,
18
20
  Dialog,
19
21
  ICommandPalette,
@@ -35,7 +37,7 @@ import { MarkdownCell } from '@jupyterlab/cells';
35
37
  import type { CodeEditor } from '@jupyterlab/codeeditor';
36
38
  import { IEditorServices, IPositionModel } from '@jupyterlab/codeeditor';
37
39
  import type { IChangedArgs } from '@jupyterlab/coreutils';
38
- import { PageConfig } from '@jupyterlab/coreutils';
40
+ import { compareVersions, PageConfig } from '@jupyterlab/coreutils';
39
41
 
40
42
  import {
41
43
  IEditorExtensionRegistry,
@@ -317,6 +319,10 @@ namespace CommandIDs {
317
319
 
318
320
  export const selectLastRunCell = 'notebook:select-last-run-cell';
319
321
 
322
+ export const selectLastModifiedCell = 'notebook:select-last-modified-cell';
323
+
324
+ export const selectNextModifiedCell = 'notebook:select-next-modified-cell';
325
+
320
326
  export const replaceSelection = 'notebook:replace-selection';
321
327
 
322
328
  export const autoClosingBrackets = 'notebook:toggle-autoclosing-brackets';
@@ -340,6 +346,12 @@ namespace CommandIDs {
340
346
  export const accessNextHistory = 'notebook:access-next-history-entry';
341
347
 
342
348
  export const virtualScrollbar = 'notebook:toggle-virtual-scrollbar';
349
+
350
+ export const copySelectedtext = 'notebook:copy-selected-text';
351
+
352
+ export const pasteText = 'notebook:paste-text';
353
+
354
+ export const cutSelectedtext = 'notebook:cut-selected-text';
343
355
  }
344
356
 
345
357
  /**
@@ -664,11 +676,32 @@ export const exportPlugin: JupyterFrontEndPlugin<void> = {
664
676
 
665
677
  const { context } = current;
666
678
 
679
+ const serverVersion = PageConfig.getNotebookVersion();
680
+ const supportsHTMLSanitizationOption =
681
+ compareVersions(serverVersion, [4, 0, 0]) <
682
+ 0 /* Jupyter Server only */ &&
683
+ compareVersions(serverVersion, [2, 18, 0]) >= 0;
684
+
685
+ let sanitizeHtml = false;
686
+
687
+ if (args['format'] === 'html' && supportsHTMLSanitizationOption) {
688
+ const result = await InputDialog.getBoolean({
689
+ title: trans.__('Export as HTML'),
690
+ label: trans.__('Sanitize HTML output'),
691
+ value: true
692
+ });
693
+ if (!result.button.accept) {
694
+ return;
695
+ }
696
+ sanitizeHtml = result.value ?? false;
697
+ }
698
+
667
699
  const exportOptions: NbConvert.IExportOptions = {
668
700
  format: args['format'] as string,
669
701
  path: current.context.path,
670
702
  exporterOptions: {
671
- download: true
703
+ download: true,
704
+ sanitizeHtml
672
705
  }
673
706
  };
674
707
 
@@ -983,7 +1016,12 @@ const tocPlugin: JupyterFrontEndPlugin<void> = {
983
1016
  mdParser: IMarkdownParser | null,
984
1017
  settingRegistry: ISettingRegistry | null
985
1018
  ): void => {
986
- const nbTocFactory = new NotebookToCFactory(tracker, mdParser, sanitizer);
1019
+ const nbTocFactory = new NotebookToCFactory(
1020
+ tracker,
1021
+ mdParser,
1022
+ sanitizer,
1023
+ app.commands
1024
+ );
987
1025
  tocRegistry.add(nbTocFactory);
988
1026
  if (settingRegistry) {
989
1027
  Promise.all([app.restored, settingRegistry.load(trackerPlugin.id)])
@@ -1230,7 +1268,8 @@ const openWithNoKernelPlugin: JupyterFrontEndPlugin<void> = {
1230
1268
  label: trans.__('Notebook (no kernel)'),
1231
1269
  factory: FACTORY,
1232
1270
  kernelPreference: {
1233
- shouldStart: false
1271
+ shouldStart: false,
1272
+ shouldReuse: false
1234
1273
  }
1235
1274
  }
1236
1275
  })
@@ -1305,6 +1344,10 @@ function activateNotebookTools(
1305
1344
  return true;
1306
1345
  };
1307
1346
  notebookTools.title.icon = buildIcon;
1347
+ notebookTools.title.dataset = {
1348
+ ...notebookTools.title.dataset,
1349
+ jpTabLabel: trans.__('Notebook Tools')
1350
+ };
1308
1351
  notebookTools.title.caption = trans.__('Notebook Tools');
1309
1352
  notebookTools.id = id;
1310
1353
 
@@ -2581,6 +2624,9 @@ function addCommands(
2581
2624
  tracker.selectionChanged.connect(() => {
2582
2625
  commands.notifyCommandChanged(CommandIDs.duplicateBelow);
2583
2626
  commands.notifyCommandChanged(CommandIDs.deleteCell);
2627
+ commands.notifyCommandChanged(CommandIDs.copySelectedtext);
2628
+ commands.notifyCommandChanged(CommandIDs.pasteText);
2629
+ commands.notifyCommandChanged(CommandIDs.cutSelectedtext);
2584
2630
  commands.notifyCommandChanged(CommandIDs.copy);
2585
2631
  commands.notifyCommandChanged(CommandIDs.cut);
2586
2632
  commands.notifyCommandChanged(CommandIDs.pasteBelow);
@@ -2597,6 +2643,16 @@ function addCommands(
2597
2643
  commands.notifyCommandChanged(CommandIDs.deleteCell);
2598
2644
  commands.notifyCommandChanged(CommandIDs.moveUp);
2599
2645
  commands.notifyCommandChanged(CommandIDs.moveDown);
2646
+ commands.notifyCommandChanged(CommandIDs.selectLastModifiedCell);
2647
+ commands.notifyCommandChanged(CommandIDs.selectNextModifiedCell);
2648
+ });
2649
+ tracker.widgetAdded.connect((_, panel) => {
2650
+ panel.content.stateChanged.connect((_, args) => {
2651
+ if (args.name === 'lastModifiedCellStack') {
2652
+ commands.notifyCommandChanged(CommandIDs.selectLastModifiedCell);
2653
+ commands.notifyCommandChanged(CommandIDs.selectNextModifiedCell);
2654
+ }
2655
+ });
2600
2656
  });
2601
2657
 
2602
2658
  commands.addCommand(CommandIDs.runAndAdvance, {
@@ -3471,6 +3527,156 @@ function addCommands(
3471
3527
  }
3472
3528
  }
3473
3529
  });
3530
+
3531
+ commands.addCommand(CommandIDs.copySelectedtext, {
3532
+ label: trans.__('Copy Selected Text'),
3533
+ caption: trans.__('Copy selected text from the active cell or output area'),
3534
+ execute: async args => {
3535
+ const current = getCurrent(tracker, shell, args);
3536
+ if (!current) {
3537
+ return;
3538
+ }
3539
+
3540
+ // copying from the editor (input area)
3541
+ const editor = current.content.activeCell?.editor;
3542
+ if (editor) {
3543
+ const selection = editor.getSelection();
3544
+ const start = editor.getOffsetAt(selection.start);
3545
+ const end = editor.getOffsetAt(selection.end);
3546
+ const text = editor.model.sharedModel.getSource().slice(start, end);
3547
+ if (text) {
3548
+ await navigator.clipboard.writeText(text);
3549
+ return;
3550
+ }
3551
+ }
3552
+
3553
+ // fallback to DOM selection (output)
3554
+ const domSelection = window.getSelection();
3555
+ const selectedText = domSelection?.toString();
3556
+ if (selectedText && selectedText.trim().length > 0) {
3557
+ await navigator.clipboard.writeText(selectedText);
3558
+ }
3559
+ },
3560
+
3561
+ isEnabled: args => {
3562
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
3563
+ if (!current) {
3564
+ return false;
3565
+ }
3566
+
3567
+ const editor = current.content.activeCell?.editor;
3568
+ if (editor) {
3569
+ const selection = editor.getSelection();
3570
+ const hasEditorSelection =
3571
+ selection.start.line !== selection.end.line ||
3572
+ selection.start.column !== selection.end.column;
3573
+ if (hasEditorSelection) {
3574
+ return true;
3575
+ }
3576
+ }
3577
+
3578
+ // Check for text selection in output area
3579
+ const domSelection = window.getSelection();
3580
+ return !!domSelection && domSelection.toString().trim().length > 0;
3581
+ },
3582
+ describedBy: {
3583
+ args: {
3584
+ type: 'object',
3585
+ properties: {}
3586
+ }
3587
+ }
3588
+ });
3589
+
3590
+ commands.addCommand(CommandIDs.cutSelectedtext, {
3591
+ label: trans.__('Cut Selected Text'),
3592
+ caption: trans.__('Cut selected text from the active cell or output area'),
3593
+ execute: async args => {
3594
+ const current = getCurrent(tracker, shell, args);
3595
+ if (!current) {
3596
+ return;
3597
+ }
3598
+
3599
+ const editor = current.content.activeCell?.editor;
3600
+ if (editor) {
3601
+ const selection = editor.getSelection();
3602
+ const start = editor.getOffsetAt(selection.start);
3603
+ const end = editor.getOffsetAt(selection.end);
3604
+ const text = editor.model.sharedModel.getSource().slice(start, end);
3605
+ if (text) {
3606
+ await navigator.clipboard.writeText(text);
3607
+ editor.replaceSelection?.('');
3608
+ return;
3609
+ }
3610
+ }
3611
+ },
3612
+
3613
+ isEnabled: args => {
3614
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
3615
+ if (!current) {
3616
+ return false;
3617
+ }
3618
+
3619
+ const editor = current.content.activeCell?.editor;
3620
+ if (!editor) {
3621
+ return false;
3622
+ }
3623
+
3624
+ const selection = editor.getSelection();
3625
+ const hasEditorSelection =
3626
+ selection.start.line !== selection.end.line ||
3627
+ selection.start.column !== selection.end.column;
3628
+ return hasEditorSelection;
3629
+ },
3630
+ describedBy: {
3631
+ args: {
3632
+ type: 'object',
3633
+ properties: {}
3634
+ }
3635
+ }
3636
+ });
3637
+
3638
+ commands.addCommand(CommandIDs.pasteText, {
3639
+ label: trans.__('Paste Text'),
3640
+ caption: trans.__(
3641
+ 'Paste text from the clipboard into the active cell editor'
3642
+ ),
3643
+ execute: async args => {
3644
+ const current = getCurrent(tracker, shell, args);
3645
+ if (!current) {
3646
+ return;
3647
+ }
3648
+
3649
+ const editor = current.content.activeCell?.editor;
3650
+ if (!editor) {
3651
+ return;
3652
+ }
3653
+
3654
+ try {
3655
+ const text = await navigator.clipboard.readText();
3656
+ if (text) {
3657
+ editor.replaceSelection?.(text);
3658
+ }
3659
+ } catch (err) {
3660
+ // browser limitation fallback (e.g Firefox)
3661
+ Clipboard.showPasteUnavailableDialog(trans);
3662
+ }
3663
+ },
3664
+
3665
+ isEnabled: args => {
3666
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
3667
+ if (!current) {
3668
+ return false;
3669
+ }
3670
+ return !!current.content.activeCell?.editor;
3671
+ },
3672
+ describedBy: {
3673
+ args: {
3674
+ type: 'object',
3675
+ properties: {}
3676
+ }
3677
+ }
3678
+ });
3679
+
3474
3680
  commands.addCommand(CommandIDs.split, {
3475
3681
  label: trans.__('Split Cell'),
3476
3682
  execute: args => {
@@ -3505,7 +3711,16 @@ function addCommands(
3505
3711
  );
3506
3712
  }
3507
3713
  },
3508
- isEnabled,
3714
+ isVisible: args => {
3715
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
3716
+ if (!current) {
3717
+ return false;
3718
+ }
3719
+
3720
+ // Enable only if more than one cell is selected
3721
+ const notebook = current.content;
3722
+ return notebook && notebook.selectedCells.length > 1;
3723
+ },
3509
3724
  describedBy: {
3510
3725
  args: {
3511
3726
  type: 'object',
@@ -4544,6 +4759,44 @@ function addCommands(
4544
4759
  }
4545
4760
  }
4546
4761
  });
4762
+ commands.addCommand(CommandIDs.selectLastModifiedCell, {
4763
+ label: trans.__('Select Last Modified Cell'),
4764
+ execute: async args => {
4765
+ const current = getCurrent(tracker, shell, args);
4766
+ if (current) {
4767
+ await NotebookActions.selectLastModifiedCell(current.content);
4768
+ }
4769
+ },
4770
+ isEnabled: args => {
4771
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
4772
+ return !!current && current.content.hasNavigableModifiedCellBack();
4773
+ },
4774
+ describedBy: {
4775
+ args: {
4776
+ type: 'object',
4777
+ properties: {}
4778
+ }
4779
+ }
4780
+ });
4781
+ commands.addCommand(CommandIDs.selectNextModifiedCell, {
4782
+ label: trans.__('Select Next Modified Cell'),
4783
+ execute: async args => {
4784
+ const current = getCurrent(tracker, shell, args);
4785
+ if (current) {
4786
+ await NotebookActions.selectNextModifiedCell(current.content);
4787
+ }
4788
+ },
4789
+ isEnabled: args => {
4790
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
4791
+ return !!current && current.content.hasNavigableModifiedCellForward();
4792
+ },
4793
+ describedBy: {
4794
+ args: {
4795
+ type: 'object',
4796
+ properties: {}
4797
+ }
4798
+ }
4799
+ });
4547
4800
  commands.addCommand(CommandIDs.replaceSelection, {
4548
4801
  label: trans.__('Replace Selection in Notebook Cell'),
4549
4802
  execute: args => {
@@ -4800,6 +5053,9 @@ function populatePalette(
4800
5053
  CommandIDs.pasteAbove,
4801
5054
  CommandIDs.pasteAndReplace,
4802
5055
  CommandIDs.deleteCell,
5056
+ CommandIDs.copySelectedtext,
5057
+ CommandIDs.pasteText,
5058
+ CommandIDs.cutSelectedtext,
4803
5059
  CommandIDs.split,
4804
5060
  CommandIDs.merge,
4805
5061
  CommandIDs.mergeAbove,
@@ -4838,7 +5094,9 @@ function populatePalette(
4838
5094
  CommandIDs.toggleRenderSideBySideCurrentNotebook,
4839
5095
  CommandIDs.setSideBySideRatio,
4840
5096
  CommandIDs.enableOutputScrolling,
4841
- CommandIDs.disableOutputScrolling
5097
+ CommandIDs.disableOutputScrolling,
5098
+ CommandIDs.selectLastModifiedCell,
5099
+ CommandIDs.selectNextModifiedCell
4842
5100
  ].forEach(command => {
4843
5101
  palette.addItem({ command, category });
4844
5102
  });