@jupyterlab/notebook-extension 4.0.0-alpha.20 → 4.0.0-alpha.21

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/src/index.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  * @module notebook-extension
6
6
  */
7
7
 
8
+ import type { FieldProps } from '@rjsf/utils';
9
+
8
10
  import {
9
11
  ILabShell,
10
12
  ILayoutRestorer,
@@ -54,6 +56,7 @@ import {
54
56
  ILSPFeatureManager
55
57
  } from '@jupyterlab/lsp';
56
58
  import { IMainMenu } from '@jupyterlab/mainmenu';
59
+ import { IMetadataFormProvider } from '@jupyterlab/metadataform';
57
60
  import * as nbformat from '@jupyterlab/nbformat';
58
61
  import {
59
62
  CommandEditStatus,
@@ -77,7 +80,11 @@ import {
77
80
  } from '@jupyterlab/notebook';
78
81
  import { IObservableList } from '@jupyterlab/observables';
79
82
  import { IPropertyInspectorProvider } from '@jupyterlab/property-inspector';
80
- import { IMarkdownParser, IRenderMimeRegistry } from '@jupyterlab/rendermime';
83
+ import {
84
+ IMarkdownParser,
85
+ IRenderMime,
86
+ IRenderMimeRegistry
87
+ } from '@jupyterlab/rendermime';
81
88
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
82
89
  import { IStateDB } from '@jupyterlab/statedb';
83
90
  import { IStatusBar } from '@jupyterlab/statusbar';
@@ -91,6 +98,7 @@ import {
91
98
  cutIcon,
92
99
  duplicateIcon,
93
100
  fastForwardIcon,
101
+ IFormRenderer,
94
102
  IFormRendererRegistry,
95
103
  moveDownIcon,
96
104
  moveUpIcon,
@@ -102,7 +110,6 @@ import { CommandRegistry } from '@lumino/commands';
102
110
  import {
103
111
  JSONExt,
104
112
  JSONObject,
105
- JSONValue,
106
113
  ReadonlyJSONValue,
107
114
  ReadonlyPartialJSONObject,
108
115
  UUID
@@ -111,6 +118,11 @@ import { DisposableSet, IDisposable } from '@lumino/disposable';
111
118
  import { Message, MessageLoop } from '@lumino/messaging';
112
119
  import { Menu, Panel, Widget } from '@lumino/widgets';
113
120
  import { logNotebookOutput } from './nboutput';
121
+ import { ActiveCellTool } from './tool-widgets/activeCellToolWidget';
122
+ import {
123
+ CellMetadataField,
124
+ NotebookMetadataField
125
+ } from './tool-widgets/metadataEditorFields';
114
126
 
115
127
  /**
116
128
  * The command IDs used by the notebook plugin.
@@ -848,7 +860,7 @@ const tocPlugin: JupyterFrontEndPlugin<void> = {
848
860
  app: JupyterFrontEnd,
849
861
  tracker: INotebookTracker,
850
862
  tocRegistry: ITableOfContentsRegistry,
851
- sanitizer: ISanitizer,
863
+ sanitizer: IRenderMime.ISanitizer,
852
864
  mdParser: IMarkdownParser | null
853
865
  ): void => {
854
866
  tocRegistry.add(new NotebookToCFactory(tracker, mdParser, sanitizer));
@@ -867,6 +879,156 @@ const languageServerPlugin: JupyterFrontEndPlugin<void> = {
867
879
  autoStart: true
868
880
  };
869
881
 
882
+ const updateRawMimetype: JupyterFrontEndPlugin<void> = {
883
+ id: '@jupyterlab/notebook-extension:update-raw-mimetype',
884
+ autoStart: true,
885
+ requires: [INotebookTracker, IMetadataFormProvider, ITranslator],
886
+ activate: (
887
+ app: JupyterFrontEnd,
888
+ tracker: INotebookTracker,
889
+ metadataForms: IMetadataFormProvider,
890
+ translator: ITranslator
891
+ ) => {
892
+ const trans = translator.load('jupyterlab');
893
+ let formatsInitialized = false;
894
+
895
+ async function maybeInitializeFormats() {
896
+ if (formatsInitialized) {
897
+ return;
898
+ }
899
+ if (!metadataForms.get('commonToolsSection')) {
900
+ return;
901
+ }
902
+
903
+ const properties = metadataForms
904
+ .get('commonToolsSection')!
905
+ .getProperties('/raw_mimetype');
906
+
907
+ if (!properties) {
908
+ return;
909
+ }
910
+
911
+ tracker.widgetAdded.disconnect(maybeInitializeFormats);
912
+
913
+ formatsInitialized = true;
914
+
915
+ const services = app.serviceManager;
916
+ const response = await services.nbconvert.getExportFormats(false);
917
+ if (!response) {
918
+ return;
919
+ }
920
+
921
+ // convert exportList to palette and menu items
922
+ const formatList = Object.keys(response);
923
+ const formatLabels = Private.getFormatLabels(translator);
924
+ type enumeration = {
925
+ const: string;
926
+ title: string;
927
+ };
928
+ formatList.forEach(function (key) {
929
+ const mimetypeExists =
930
+ (properties!.oneOf as Array<enumeration>)?.filter(
931
+ value => value.const === key
932
+ ).length > 0;
933
+ if (!mimetypeExists) {
934
+ const altOption = trans.__(key[0].toUpperCase() + key.substr(1));
935
+ const option = formatLabels[key] ? formatLabels[key] : altOption;
936
+ const mimeTypeValue = response[key].output_mimetype;
937
+
938
+ (properties!.oneOf as Array<enumeration>)!.push({
939
+ const: mimeTypeValue,
940
+ title: option
941
+ });
942
+ }
943
+ });
944
+
945
+ metadataForms
946
+ .get('commonToolsSection')!
947
+ .setProperties('/raw_mimetype', properties);
948
+ }
949
+ tracker.widgetAdded.connect(maybeInitializeFormats);
950
+ }
951
+ };
952
+
953
+ /**
954
+ * Registering metadata editor fields.
955
+ */
956
+ const customMetadataEditorFields: JupyterFrontEndPlugin<void> = {
957
+ id: '@jupyterlab/notebook-extension:metadata-editor',
958
+ autoStart: true,
959
+ requires: [INotebookTracker, IEditorServices, IFormRendererRegistry],
960
+ optional: [ITranslator],
961
+ activate: (
962
+ app: JupyterFrontEnd,
963
+ tracker: INotebookTracker,
964
+ editorServices: IEditorServices,
965
+ formRegistry: IFormRendererRegistry,
966
+ translator?: ITranslator
967
+ ) => {
968
+ const editorFactory: CodeEditor.Factory = options =>
969
+ editorServices.factoryService.newInlineEditor(options);
970
+ // Register the custom fields.
971
+ const cellComponent: IFormRenderer = {
972
+ fieldRenderer: (props: FieldProps) => {
973
+ return new CellMetadataField({
974
+ editorFactory,
975
+ tracker,
976
+ label: 'Cell metadata',
977
+ translator: translator
978
+ }).render(props);
979
+ }
980
+ };
981
+ formRegistry.addRenderer(
982
+ 'notebook-extension:metadata-editor.cell-metadata',
983
+ cellComponent
984
+ );
985
+
986
+ const notebookComponent: IFormRenderer = {
987
+ fieldRenderer: (props: FieldProps) => {
988
+ return new NotebookMetadataField({
989
+ editorFactory,
990
+ tracker,
991
+ label: 'Notebook metadata',
992
+ translator: translator
993
+ }).render(props);
994
+ }
995
+ };
996
+ formRegistry.addRenderer(
997
+ 'notebook-extension:metadata-editor.notebook-metadata',
998
+ notebookComponent
999
+ );
1000
+ }
1001
+ };
1002
+
1003
+ /**
1004
+ * Registering active cell field.
1005
+ */
1006
+ const activeCellTool: JupyterFrontEndPlugin<void> = {
1007
+ id: '@jupyterlab/notebook-extension:active-cell-tool',
1008
+ autoStart: true,
1009
+ requires: [INotebookTracker, IFormRendererRegistry, IEditorLanguageRegistry],
1010
+ activate: (
1011
+ // Register the custom field.
1012
+ app: JupyterFrontEnd,
1013
+ tracker: INotebookTracker,
1014
+ formRegistry: IFormRendererRegistry,
1015
+ languages: IEditorLanguageRegistry
1016
+ ) => {
1017
+ const component: IFormRenderer = {
1018
+ fieldRenderer: (props: FieldProps) => {
1019
+ return new ActiveCellTool({
1020
+ tracker,
1021
+ languages
1022
+ }).render(props);
1023
+ }
1024
+ };
1025
+ formRegistry.addRenderer(
1026
+ 'notebook-extension:active-cell-tool.renderer',
1027
+ component
1028
+ );
1029
+ }
1030
+ };
1031
+
870
1032
  /**
871
1033
  * Export the plugins as default.
872
1034
  */
@@ -888,7 +1050,10 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
888
1050
  completerPlugin,
889
1051
  searchProvider,
890
1052
  tocPlugin,
891
- languageServerPlugin
1053
+ languageServerPlugin,
1054
+ updateRawMimetype,
1055
+ customMetadataEditorFields,
1056
+ activeCellTool
892
1057
  ];
893
1058
  export default plugins;
894
1059
 
@@ -907,23 +1072,6 @@ function activateNotebookTools(
907
1072
  const trans = translator.load('jupyterlab');
908
1073
  const id = 'notebook-tools';
909
1074
  const notebookTools = new NotebookTools({ tracker, translator });
910
- const activeCellTool = new NotebookTools.ActiveCellTool(languages);
911
- const editable = NotebookTools.createEditableToggle(translator);
912
- const slideShow = NotebookTools.createSlideShowSelector(translator);
913
- const tocBaseNumbering = NotebookTools.createToCBaseNumbering(translator);
914
- const editorFactory: CodeEditor.Factory = options =>
915
- editorServices.factoryService.newInlineEditor(options);
916
- const cellMetadataEditor = new NotebookTools.CellMetadataEditorTool({
917
- editorFactory,
918
- collapsed: false,
919
- translator
920
- });
921
- const notebookMetadataEditor = new NotebookTools.NotebookMetadataEditorTool({
922
- editorFactory,
923
- translator
924
- });
925
-
926
- const services = app.serviceManager;
927
1075
 
928
1076
  // Create message hook for triggers to save to the database.
929
1077
  const hook = (sender: any, message: Message): boolean => {
@@ -940,77 +1088,10 @@ function activateNotebookTools(
940
1088
  }
941
1089
  return true;
942
1090
  };
943
- const optionsMap: { [key: string]: JSONValue } = {};
944
- optionsMap.None = null;
945
-
946
- let formatsInitialized = false;
947
-
948
- async function maybeInitializeFormats() {
949
- if (formatsInitialized) {
950
- return;
951
- }
952
-
953
- tracker.widgetAdded.disconnect(maybeInitializeFormats);
954
-
955
- formatsInitialized = true;
956
-
957
- const response = await services.nbconvert.getExportFormats(false);
958
- if (!response) {
959
- return;
960
- }
961
- /**
962
- * The excluded Cell Inspector Raw NbConvert Formats
963
- * (returned from nbconvert's export list)
964
- */
965
- const rawFormatExclude = ['pdf', 'slides', 'script', 'notebook', 'custom'];
966
- let optionValueArray: any = [
967
- [trans.__('PDF'), 'pdf'],
968
- [trans.__('Slides'), 'slides'],
969
- [trans.__('Script'), 'script'],
970
- [trans.__('Notebook'), 'notebook'],
971
- [trans.__('Custom'), 'custom']
972
- ];
973
-
974
- // convert exportList to palette and menu items
975
- const formatList = Object.keys(response);
976
- const formatLabels = Private.getFormatLabels(translator);
977
- formatList.forEach(function (key) {
978
- if (rawFormatExclude.indexOf(key) === -1) {
979
- const altOption = trans.__(key[0].toUpperCase() + key.substr(1));
980
- const option = formatLabels[key] ? formatLabels[key] : altOption;
981
- const mimeTypeValue = response[key].output_mimetype;
982
- optionValueArray.push([option, mimeTypeValue]);
983
- }
984
- });
985
- const nbConvert = NotebookTools.createNBConvertSelector(
986
- optionValueArray,
987
- translator
988
- );
989
- notebookTools.addItem({ tool: nbConvert, section: 'common', rank: 3 });
990
- }
991
-
992
- tracker.widgetAdded.connect(maybeInitializeFormats);
993
-
994
1091
  notebookTools.title.icon = buildIcon;
995
1092
  notebookTools.title.caption = trans.__('Notebook Tools');
996
1093
  notebookTools.id = id;
997
1094
 
998
- notebookTools.addItem({ tool: activeCellTool, section: 'common', rank: 1 });
999
- notebookTools.addItem({ tool: editable, section: 'common', rank: 2 });
1000
- notebookTools.addItem({ tool: slideShow, section: 'common', rank: 3 });
1001
- notebookTools.addItem({ tool: tocBaseNumbering, section: 'common', rank: 4 });
1002
-
1003
- notebookTools.addItem({
1004
- tool: cellMetadataEditor,
1005
- section: 'advanced',
1006
- rank: 1
1007
- });
1008
- notebookTools.addItem({
1009
- tool: notebookMetadataEditor,
1010
- section: 'advanced',
1011
- rank: 2
1012
- });
1013
-
1014
1095
  MessageLoop.installMessageHook(notebookTools, hook);
1015
1096
 
1016
1097
  if (inspectorProvider) {
@@ -1657,9 +1738,8 @@ function activateNotebookHandler(
1657
1738
  showEditorForReadOnlyMarkdown: settings.get(
1658
1739
  'showEditorForReadOnlyMarkdown'
1659
1740
  ).composite as boolean,
1660
- disableDocumentWideUndoRedo: settings.get(
1661
- 'experimentalDisableDocumentWideUndoRedo'
1662
- ).composite as boolean,
1741
+ disableDocumentWideUndoRedo: !settings.get('documentWideUndoRedo')
1742
+ .composite as boolean,
1663
1743
  renderingLayout: settings.get('renderingLayout').composite as
1664
1744
  | 'default'
1665
1745
  | 'side-by-side',
@@ -1691,8 +1771,8 @@ function activateNotebookHandler(
1691
1771
  factory.shutdownOnClose = settings.get('kernelShutdown')
1692
1772
  .composite as boolean;
1693
1773
 
1694
- modelFactory.disableDocumentWideUndoRedo = settings.get(
1695
- 'experimentalDisableDocumentWideUndoRedo'
1774
+ modelFactory.disableDocumentWideUndoRedo = !settings.get(
1775
+ 'documentWideUndoRedo'
1696
1776
  ).composite as boolean;
1697
1777
 
1698
1778
  updateTracker({
@@ -1805,7 +1885,7 @@ function activateNotebookCompleterService(
1805
1885
  notebooks: INotebookTracker,
1806
1886
  manager: ICompletionProviderManager | null,
1807
1887
  translator: ITranslator | null,
1808
- appSanitizer: ISanitizer | null
1888
+ appSanitizer: IRenderMime.ISanitizer | null
1809
1889
  ): void {
1810
1890
  if (!manager) {
1811
1891
  return;
@@ -1976,9 +2056,43 @@ function addCommands(
1976
2056
  }
1977
2057
  );
1978
2058
 
2059
+ tracker.selectionChanged.connect(() => {
2060
+ commands.notifyCommandChanged(CommandIDs.duplicateBelow);
2061
+ commands.notifyCommandChanged(CommandIDs.deleteCell);
2062
+ commands.notifyCommandChanged(CommandIDs.copy);
2063
+ commands.notifyCommandChanged(CommandIDs.cut);
2064
+ commands.notifyCommandChanged(CommandIDs.pasteBelow);
2065
+ commands.notifyCommandChanged(CommandIDs.pasteAbove);
2066
+ commands.notifyCommandChanged(CommandIDs.pasteAndReplace);
2067
+ commands.notifyCommandChanged(CommandIDs.moveUp);
2068
+ commands.notifyCommandChanged(CommandIDs.moveDown);
2069
+ commands.notifyCommandChanged(CommandIDs.run);
2070
+ commands.notifyCommandChanged(CommandIDs.runAll);
2071
+ commands.notifyCommandChanged(CommandIDs.runAndAdvance);
2072
+ commands.notifyCommandChanged(CommandIDs.runAndInsert);
2073
+ });
2074
+ tracker.activeCellChanged.connect(() => {
2075
+ commands.notifyCommandChanged(CommandIDs.moveUp);
2076
+ commands.notifyCommandChanged(CommandIDs.moveDown);
2077
+ });
2078
+
1979
2079
  commands.addCommand(CommandIDs.runAndAdvance, {
1980
- label: trans.__('Run Selected Cells'),
1981
- caption: trans.__('Run the selected cells'),
2080
+ label: args => {
2081
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2082
+ return trans._n(
2083
+ 'Run Selected Cell',
2084
+ 'Run Selected Cells',
2085
+ current?.content.selectedCells.length ?? 1
2086
+ );
2087
+ },
2088
+ caption: args => {
2089
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2090
+ return trans._n(
2091
+ 'Run this cell and advance',
2092
+ 'Run these %1 cells and advance',
2093
+ current?.content.selectedCells.length ?? 1
2094
+ );
2095
+ },
1982
2096
  execute: args => {
1983
2097
  const current = getCurrent(tracker, shell, args);
1984
2098
 
@@ -1991,8 +2105,14 @@ function addCommands(
1991
2105
  isEnabled
1992
2106
  });
1993
2107
  commands.addCommand(CommandIDs.run, {
1994
- label: trans.__('Run Selected Cells and Do not Advance'),
1995
- caption: trans.__('Run the selected cells and do not advance'),
2108
+ label: args => {
2109
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2110
+ return trans._n(
2111
+ 'Run Selected Cell and Do not Advance',
2112
+ 'Run Selected Cells and Do not Advance',
2113
+ current?.content.selectedCells.length ?? 1
2114
+ );
2115
+ },
1996
2116
  execute: args => {
1997
2117
  const current = getCurrent(tracker, shell, args);
1998
2118
 
@@ -2005,7 +2125,14 @@ function addCommands(
2005
2125
  isEnabled
2006
2126
  });
2007
2127
  commands.addCommand(CommandIDs.runAndInsert, {
2008
- label: trans.__('Run Selected Cells and Insert Below'),
2128
+ label: args => {
2129
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2130
+ return trans._n(
2131
+ 'Run Selected Cell and Insert Below',
2132
+ 'Run Selected Cells and Insert Below',
2133
+ current?.content.selectedCells.length ?? 1
2134
+ );
2135
+ },
2009
2136
  execute: args => {
2010
2137
  const current = getCurrent(tracker, shell, args);
2011
2138
 
@@ -2270,8 +2397,22 @@ function addCommands(
2270
2397
  isEnabled
2271
2398
  });
2272
2399
  commands.addCommand(CommandIDs.cut, {
2273
- label: trans.__('Cut Cells'),
2274
- caption: trans.__('Cut the selected cells'),
2400
+ label: args => {
2401
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2402
+ return trans._n(
2403
+ 'Cut Cell',
2404
+ 'Cut Cells',
2405
+ current?.content.selectedCells.length ?? 1
2406
+ );
2407
+ },
2408
+ caption: args => {
2409
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2410
+ return trans._n(
2411
+ 'Cut this cell',
2412
+ 'Cut these %1 cells',
2413
+ current?.content.selectedCells.length ?? 1
2414
+ );
2415
+ },
2275
2416
  execute: args => {
2276
2417
  const current = getCurrent(tracker, shell, args);
2277
2418
 
@@ -2283,8 +2424,22 @@ function addCommands(
2283
2424
  isEnabled
2284
2425
  });
2285
2426
  commands.addCommand(CommandIDs.copy, {
2286
- label: trans.__('Copy Cells'),
2287
- caption: trans.__('Copy the selected cells'),
2427
+ label: args => {
2428
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2429
+ return trans._n(
2430
+ 'Copy Cell',
2431
+ 'Copy Cells',
2432
+ current?.content.selectedCells.length ?? 1
2433
+ );
2434
+ },
2435
+ caption: args => {
2436
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2437
+ return trans._n(
2438
+ 'Copy this cell',
2439
+ 'Copy these %1 cells',
2440
+ current?.content.selectedCells.length ?? 1
2441
+ );
2442
+ },
2288
2443
  execute: args => {
2289
2444
  const current = getCurrent(tracker, shell, args);
2290
2445
 
@@ -2296,8 +2451,22 @@ function addCommands(
2296
2451
  isEnabled
2297
2452
  });
2298
2453
  commands.addCommand(CommandIDs.pasteBelow, {
2299
- label: trans.__('Paste Cells Below'),
2300
- caption: trans.__('Paste cells from the clipboard'),
2454
+ label: args => {
2455
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2456
+ return trans._n(
2457
+ 'Paste Cell Below',
2458
+ 'Paste Cells Below',
2459
+ current?.content.selectedCells.length ?? 1
2460
+ );
2461
+ },
2462
+ caption: args => {
2463
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2464
+ return trans._n(
2465
+ 'Paste this cell from the clipboard',
2466
+ 'Paste these %1 cells from the clipboard',
2467
+ current?.content.selectedCells.length ?? 1
2468
+ );
2469
+ },
2301
2470
  execute: args => {
2302
2471
  const current = getCurrent(tracker, shell, args);
2303
2472
 
@@ -2309,7 +2478,22 @@ function addCommands(
2309
2478
  isEnabled
2310
2479
  });
2311
2480
  commands.addCommand(CommandIDs.pasteAbove, {
2312
- label: trans.__('Paste Cells Above'),
2481
+ label: args => {
2482
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2483
+ return trans._n(
2484
+ 'Paste Cell Above',
2485
+ 'Paste Cells Above',
2486
+ current?.content.selectedCells.length ?? 1
2487
+ );
2488
+ },
2489
+ caption: args => {
2490
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2491
+ return trans._n(
2492
+ 'Paste this cell from the clipboard',
2493
+ 'Paste these %1 cells from the clipboard',
2494
+ current?.content.selectedCells.length ?? 1
2495
+ );
2496
+ },
2313
2497
  execute: args => {
2314
2498
  const current = getCurrent(tracker, shell, args);
2315
2499
 
@@ -2320,10 +2504,22 @@ function addCommands(
2320
2504
  isEnabled
2321
2505
  });
2322
2506
  commands.addCommand(CommandIDs.duplicateBelow, {
2323
- label: trans.__('Duplicate Cells Below'),
2324
- caption: trans.__(
2325
- 'Copy the selected cells and paste them below the selection'
2326
- ),
2507
+ label: args => {
2508
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2509
+ return trans._n(
2510
+ 'Duplicate Cell Below',
2511
+ 'Duplicate Cells Below',
2512
+ current?.content.selectedCells.length ?? 1
2513
+ );
2514
+ },
2515
+ caption: args => {
2516
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2517
+ return trans._n(
2518
+ 'Create a duplicate of this cell below',
2519
+ 'Create duplicates of %1 cells below',
2520
+ current?.content.selectedCells.length ?? 1
2521
+ );
2522
+ },
2327
2523
  execute: args => {
2328
2524
  const current = getCurrent(tracker, shell, args);
2329
2525
 
@@ -2335,7 +2531,14 @@ function addCommands(
2335
2531
  isEnabled
2336
2532
  });
2337
2533
  commands.addCommand(CommandIDs.pasteAndReplace, {
2338
- label: trans.__('Paste Cells and Replace'),
2534
+ label: args => {
2535
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2536
+ return trans._n(
2537
+ 'Paste Cell and Replace',
2538
+ 'Paste Cells and Replace',
2539
+ current?.content.selectedCells.length ?? 1
2540
+ );
2541
+ },
2339
2542
  execute: args => {
2340
2543
  const current = getCurrent(tracker, shell, args);
2341
2544
 
@@ -2346,7 +2549,23 @@ function addCommands(
2346
2549
  isEnabled
2347
2550
  });
2348
2551
  commands.addCommand(CommandIDs.deleteCell, {
2349
- label: trans.__('Delete Cells'),
2552
+ label: args => {
2553
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2554
+ return trans._n(
2555
+ 'Delete Cell',
2556
+ 'Delete Cells',
2557
+ current?.content.selectedCells.length ?? 1
2558
+ );
2559
+ },
2560
+ caption: args => {
2561
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2562
+ return trans._n(
2563
+ 'Delete this cell',
2564
+ 'Delete these %1 cells',
2565
+ current?.content.selectedCells.length ?? 1
2566
+ );
2567
+ },
2568
+
2350
2569
  execute: args => {
2351
2570
  const current = getCurrent(tracker, shell, args);
2352
2571
 
@@ -2402,6 +2621,7 @@ function addCommands(
2402
2621
  });
2403
2622
  commands.addCommand(CommandIDs.insertAbove, {
2404
2623
  label: trans.__('Insert Cell Above'),
2624
+ caption: trans.__('Insert a cell above'),
2405
2625
  execute: args => {
2406
2626
  const current = getCurrent(tracker, shell, args);
2407
2627
 
@@ -2562,7 +2782,22 @@ function addCommands(
2562
2782
  isEnabled
2563
2783
  });
2564
2784
  commands.addCommand(CommandIDs.moveUp, {
2565
- label: trans.__('Move Cells Up'),
2785
+ label: args => {
2786
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2787
+ return trans._n(
2788
+ 'Move Cell Up',
2789
+ 'Move Cells Up',
2790
+ current?.content.selectedCells.length ?? 1
2791
+ );
2792
+ },
2793
+ caption: args => {
2794
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2795
+ return trans._n(
2796
+ 'Move this cell up',
2797
+ 'Move these %1 cells up',
2798
+ current?.content.selectedCells.length ?? 1
2799
+ );
2800
+ },
2566
2801
  execute: args => {
2567
2802
  const current = getCurrent(tracker, shell, args);
2568
2803
 
@@ -2574,11 +2809,32 @@ function addCommands(
2574
2809
  );
2575
2810
  }
2576
2811
  },
2577
- isEnabled,
2812
+ isEnabled: args => {
2813
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2814
+ if (!current) {
2815
+ return false;
2816
+ }
2817
+ return current.content.activeCellIndex >= 1;
2818
+ },
2578
2819
  icon: args => (args.toolbar ? moveUpIcon : undefined)
2579
2820
  });
2580
2821
  commands.addCommand(CommandIDs.moveDown, {
2581
- label: trans.__('Move Cells Down'),
2822
+ label: args => {
2823
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2824
+ return trans._n(
2825
+ 'Move Cell Down',
2826
+ 'Move Cells Down',
2827
+ current?.content.selectedCells.length ?? 1
2828
+ );
2829
+ },
2830
+ caption: args => {
2831
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2832
+ return trans._n(
2833
+ 'Move this cell down',
2834
+ 'Move these %1 cells down',
2835
+ current?.content.selectedCells.length ?? 1
2836
+ );
2837
+ },
2582
2838
  execute: args => {
2583
2839
  const current = getCurrent(tracker, shell, args);
2584
2840
 
@@ -2590,7 +2846,15 @@ function addCommands(
2590
2846
  );
2591
2847
  }
2592
2848
  },
2593
- isEnabled,
2849
+ isEnabled: args => {
2850
+ const current = getCurrent(tracker, shell, { ...args, activate: false });
2851
+ if (!current || !current.content.model) {
2852
+ return false;
2853
+ }
2854
+
2855
+ const length = current.content.model.cells.length;
2856
+ return current.content.activeCellIndex < length - 1;
2857
+ },
2594
2858
  icon: args => (args.toolbar ? moveDownIcon : undefined)
2595
2859
  });
2596
2860
  commands.addCommand(CommandIDs.toggleAllLines, {
@@ -2667,7 +2931,11 @@ function addCommands(
2667
2931
  const current = getCurrent(tracker, shell, args);
2668
2932
 
2669
2933
  if (current) {
2670
- return current.content.activeCell?.editor?.redo();
2934
+ const cell = current.content.activeCell;
2935
+ if (cell) {
2936
+ cell.inputHidden = false;
2937
+ return cell.editor?.redo();
2938
+ }
2671
2939
  }
2672
2940
  }
2673
2941
  });
@@ -2677,7 +2945,11 @@ function addCommands(
2677
2945
  const current = getCurrent(tracker, shell, args);
2678
2946
 
2679
2947
  if (current) {
2680
- return current.content.activeCell?.editor?.undo();
2948
+ const cell = current.content.activeCell;
2949
+ if (cell) {
2950
+ cell.inputHidden = false;
2951
+ return cell.editor?.undo();
2952
+ }
2681
2953
  }
2682
2954
  }
2683
2955
  });