@jupyterlab/filebrowser-extension 4.0.0-alpha.9 → 4.0.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
@@ -8,16 +8,20 @@ import { ILabShell, ILayoutRestorer, IRouter, ITreePathUpdater, JupyterFrontEnd,
8
8
  import { Clipboard, createToolbarFactory, ICommandPalette, InputDialog, IToolbarWidgetRegistry, setToolbar, showErrorMessage, WidgetTracker } from '@jupyterlab/apputils';
9
9
  import { PageConfig, PathExt } from '@jupyterlab/coreutils';
10
10
  import { IDocumentManager } from '@jupyterlab/docmanager';
11
- import { FileBrowser, FileUploadStatus, FilterFileBrowserModel, IFileBrowserCommands, IFileBrowserFactory, Uploader } from '@jupyterlab/filebrowser';
11
+ import { FileBrowser, FileUploadStatus, FilterFileBrowserModel, IDefaultFileBrowser, IFileBrowserCommands, IFileBrowserFactory, Uploader } from '@jupyterlab/filebrowser';
12
12
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
13
13
  import { IStateDB } from '@jupyterlab/statedb';
14
14
  import { IStatusBar } from '@jupyterlab/statusbar';
15
15
  import { ITranslator } from '@jupyterlab/translation';
16
- import { addIcon, closeIcon, copyIcon, cutIcon, downloadIcon, editIcon, fileIcon, folderIcon, linkIcon, markdownIcon, newFolderIcon, pasteIcon, refreshIcon, stopIcon, textEditorIcon } from '@jupyterlab/ui-components';
17
- import { find, map, reduce, toArray } from '@lumino/algorithm';
16
+ import { addIcon, closeIcon, copyIcon, cutIcon, downloadIcon, editIcon, fileIcon, FilenameSearcher, folderIcon, linkIcon, markdownIcon, newFolderIcon, pasteIcon, refreshIcon, stopIcon, textEditorIcon } from '@jupyterlab/ui-components';
17
+ import { find, map } from '@lumino/algorithm';
18
18
  import { CommandRegistry } from '@lumino/commands';
19
19
  const FILE_BROWSER_FACTORY = 'FileBrowser';
20
20
  const FILE_BROWSER_PLUGIN_ID = '@jupyterlab/filebrowser-extension:browser';
21
+ /**
22
+ * The class name added to the filebrowser filterbox node.
23
+ */
24
+ const FILTERBOX_CLASS = 'jp-FileBrowser-filterBox';
21
25
  /**
22
26
  * The command IDs used by the file browser plugin.
23
27
  */
@@ -25,8 +29,6 @@ var CommandIDs;
25
29
  (function (CommandIDs) {
26
30
  CommandIDs.copy = 'filebrowser:copy';
27
31
  CommandIDs.copyDownloadLink = 'filebrowser:copy-download-link';
28
- // For main browser only.
29
- CommandIDs.createLauncher = 'filebrowser:create-main-launcher';
30
32
  CommandIDs.cut = 'filebrowser:cut';
31
33
  CommandIDs.del = 'filebrowser:delete';
32
34
  CommandIDs.download = 'filebrowser:download';
@@ -55,6 +57,7 @@ var CommandIDs;
55
57
  CommandIDs.toggleBrowser = 'filebrowser:toggle-main';
56
58
  CommandIDs.toggleNavigateToCurrentDirectory = 'filebrowser:toggle-navigate-to-current-directory';
57
59
  CommandIDs.toggleLastModified = 'filebrowser:toggle-last-modified';
60
+ CommandIDs.toggleFileSize = 'filebrowser:toggle-file-size';
58
61
  CommandIDs.search = 'filebrowser:search';
59
62
  CommandIDs.toggleHiddenFiles = 'filebrowser:toggle-hidden-files';
60
63
  CommandIDs.toggleFileCheckboxes = 'filebrowser:toggle-file-checkboxes';
@@ -68,7 +71,7 @@ const namespace = 'filebrowser';
68
71
  */
69
72
  const browser = {
70
73
  id: FILE_BROWSER_PLUGIN_ID,
71
- requires: [IFileBrowserFactory, ITranslator],
74
+ requires: [IDefaultFileBrowser, IFileBrowserFactory, ITranslator],
72
75
  optional: [
73
76
  ILayoutRestorer,
74
77
  ISettingRegistry,
@@ -77,9 +80,8 @@ const browser = {
77
80
  ],
78
81
  provides: IFileBrowserCommands,
79
82
  autoStart: true,
80
- activate: async (app, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
81
- const trans = translator.load('jupyterlab');
82
- const browser = factory.defaultBrowser;
83
+ activate: async (app, defaultFileBrowser, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
84
+ const browser = defaultFileBrowser;
83
85
  // Let the application restorer track the primary file browser (that is
84
86
  // automatically created) for restoration of application state (e.g. setting
85
87
  // the file browser as the current side bar widget).
@@ -94,22 +96,7 @@ const browser = {
94
96
  if (preferredPath) {
95
97
  await browser.model.cd(preferredPath);
96
98
  }
97
- addCommands(app, factory, translator, settingRegistry, commandPalette);
98
- // Show the current file browser shortcut in its title.
99
- const updateBrowserTitle = () => {
100
- const binding = find(app.commands.keyBindings, b => b.command === CommandIDs.toggleBrowser);
101
- if (binding) {
102
- const ks = CommandRegistry.formatKeystroke(binding.keys.join(' '));
103
- browser.title.caption = trans.__('File Browser (%1)', ks);
104
- }
105
- else {
106
- browser.title.caption = trans.__('File Browser');
107
- }
108
- };
109
- updateBrowserTitle();
110
- app.commands.keyBindingChanged.connect(() => {
111
- updateBrowserTitle();
112
- });
99
+ addCommands(app, browser, factory, translator, settingRegistry, commandPalette);
113
100
  return void Promise.all([app.restored, browser.model.restored]).then(() => {
114
101
  if (treePathUpdater) {
115
102
  browser.model.pathChanged.connect((sender, args) => {
@@ -124,7 +111,7 @@ const browser = {
124
111
  const fileBrowserConfig = {
125
112
  navigateToCurrentDirectory: false,
126
113
  showLastModifiedColumn: true,
127
- useFuzzyFilter: true,
114
+ showFileSizeColumn: false,
128
115
  showHiddenFiles: false,
129
116
  showFileCheckboxes: false
130
117
  };
@@ -157,14 +144,8 @@ const factory = {
157
144
  id: '@jupyterlab/filebrowser-extension:factory',
158
145
  provides: IFileBrowserFactory,
159
146
  requires: [IDocumentManager, ITranslator],
160
- optional: [
161
- IStateDB,
162
- IRouter,
163
- JupyterFrontEnd.ITreeResolver,
164
- JupyterLab.IInfo
165
- ],
166
- activate: async (app, docManager, translator, state, router, tree, info) => {
167
- const { commands } = app;
147
+ optional: [IStateDB, JupyterLab.IInfo],
148
+ activate: async (app, docManager, translator, state, info) => {
168
149
  const tracker = new WidgetTracker({ namespace });
169
150
  const createFileBrowser = (id, options = {}) => {
170
151
  var _a;
@@ -190,13 +171,26 @@ const factory = {
190
171
  void tracker.add(widget);
191
172
  return widget;
192
173
  };
174
+ return { createFileBrowser, tracker };
175
+ }
176
+ };
177
+ /**
178
+ * The default file browser factory provider.
179
+ */
180
+ const defaultFileBrowser = {
181
+ id: '@jupyterlab/filebrowser-extension:default-file-browser',
182
+ provides: IDefaultFileBrowser,
183
+ requires: [IFileBrowserFactory],
184
+ optional: [IRouter, JupyterFrontEnd.ITreeResolver, ILabShell],
185
+ activate: async (app, fileBrowserFactory, router, tree, labShell) => {
186
+ const { commands } = app;
193
187
  // Manually restore and load the default file browser.
194
- const defaultBrowser = createFileBrowser('filebrowser', {
188
+ const defaultBrowser = fileBrowserFactory.createFileBrowser('filebrowser', {
195
189
  auto: false,
196
190
  restore: false
197
191
  });
198
- void Private.restoreBrowser(defaultBrowser, commands, router, tree);
199
- return { createFileBrowser, defaultBrowser, tracker };
192
+ void Private.restoreBrowser(defaultBrowser, commands, router, tree, app, labShell);
193
+ return defaultBrowser;
200
194
  }
201
195
  };
202
196
  /**
@@ -231,7 +225,7 @@ const downloadPlugin = {
231
225
  return;
232
226
  }
233
227
  return widget.model.manager.services.contents
234
- .getDownloadUrl(widget.selectedItems().next().path)
228
+ .getDownloadUrl(widget.selectedItems().next().value.path)
235
229
  .then(url => {
236
230
  Clipboard.copyToSystem(url);
237
231
  });
@@ -249,6 +243,7 @@ const browserWidget = {
249
243
  id: '@jupyterlab/filebrowser-extension:widget',
250
244
  requires: [
251
245
  IDocumentManager,
246
+ IDefaultFileBrowser,
252
247
  IFileBrowserFactory,
253
248
  ISettingRegistry,
254
249
  IToolbarWidgetRegistry,
@@ -256,23 +251,65 @@ const browserWidget = {
256
251
  ILabShell,
257
252
  IFileBrowserCommands
258
253
  ],
254
+ optional: [ICommandPalette],
259
255
  autoStart: true,
260
- activate: (app, docManager, factory, settings, toolbarRegistry, translator, labShell) => {
256
+ activate: (app, docManager, browser, factory, settingRegistry, toolbarRegistry, translator, labShell,
257
+ // Wait until file browser commands are ready before activating file browser widget
258
+ fileBrowserCommands, commandPalette) => {
261
259
  const { commands } = app;
262
- const { defaultBrowser: browser, tracker } = factory;
260
+ const { tracker } = factory;
263
261
  const trans = translator.load('jupyterlab');
264
262
  // Set attributes when adding the browser to the UI
265
263
  browser.node.setAttribute('role', 'region');
266
264
  browser.node.setAttribute('aria-label', trans.__('File Browser Section'));
267
265
  browser.title.icon = folderIcon;
266
+ // Show the current file browser shortcut in its title.
267
+ const updateBrowserTitle = () => {
268
+ const binding = find(app.commands.keyBindings, b => b.command === CommandIDs.toggleBrowser);
269
+ if (binding) {
270
+ const ks = binding.keys.map(CommandRegistry.formatKeystroke).join(', ');
271
+ browser.title.caption = trans.__('File Browser (%1)', ks);
272
+ }
273
+ else {
274
+ browser.title.caption = trans.__('File Browser');
275
+ }
276
+ };
277
+ updateBrowserTitle();
278
+ app.commands.keyBindingChanged.connect(() => {
279
+ updateBrowserTitle();
280
+ });
268
281
  // Toolbar
269
- toolbarRegistry.registerFactory(FILE_BROWSER_FACTORY, 'uploader', (browser) => new Uploader({ model: browser.model, translator }));
270
- setToolbar(browser, createToolbarFactory(toolbarRegistry, settings, FILE_BROWSER_FACTORY, browserWidget.id, translator));
271
- labShell.add(browser, 'left', { rank: 100 });
282
+ toolbarRegistry.addFactory(FILE_BROWSER_FACTORY, 'uploader', (browser) => new Uploader({ model: browser.model, translator }));
283
+ toolbarRegistry.addFactory(FILE_BROWSER_FACTORY, 'fileNameSearcher', (browser) => {
284
+ const searcher = FilenameSearcher({
285
+ updateFilter: (filterFn, query) => {
286
+ browser.model.setFilter(value => {
287
+ return filterFn(value.name.toLowerCase());
288
+ });
289
+ },
290
+ useFuzzyFilter: true,
291
+ placeholder: trans.__('Filter files by name'),
292
+ forceRefresh: true
293
+ });
294
+ searcher.addClass(FILTERBOX_CLASS);
295
+ return searcher;
296
+ });
297
+ setToolbar(browser, createToolbarFactory(toolbarRegistry, settingRegistry, FILE_BROWSER_FACTORY, browserWidget.id, translator));
298
+ labShell.add(browser, 'left', { rank: 100, type: 'File Browser' });
299
+ commands.addCommand(CommandIDs.toggleBrowser, {
300
+ label: trans.__('File Browser'),
301
+ execute: () => {
302
+ if (browser.isHidden) {
303
+ return commands.execute(CommandIDs.showBrowser, void 0);
304
+ }
305
+ return commands.execute(CommandIDs.hideBrowser, void 0);
306
+ }
307
+ });
272
308
  commands.addCommand(CommandIDs.showBrowser, {
309
+ label: trans.__('Open the file browser for the provided `path`.'),
273
310
  execute: args => {
274
311
  const path = args.path || '';
275
- const browserForPath = Private.getBrowserForPath(path, factory);
312
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
276
313
  // Check for browser not found
277
314
  if (!browserForPath) {
278
315
  return;
@@ -285,20 +322,18 @@ const browserWidget = {
285
322
  else {
286
323
  const areas = ['left', 'right'];
287
324
  for (const area of areas) {
288
- const it = labShell.widgets(area);
289
- let widget = it.next();
290
- while (widget) {
325
+ for (const widget of labShell.widgets(area)) {
291
326
  if (widget.contains(browserForPath)) {
292
327
  labShell.activateById(widget.id);
293
328
  return;
294
329
  }
295
- widget = it.next();
296
330
  }
297
331
  }
298
332
  }
299
333
  }
300
334
  });
301
335
  commands.addCommand(CommandIDs.hideBrowser, {
336
+ label: trans.__('Hide the file browser.'),
302
337
  execute: () => {
303
338
  const widget = tracker.currentWidget;
304
339
  if (widget && !widget.isHidden) {
@@ -306,6 +341,25 @@ const browserWidget = {
306
341
  }
307
342
  }
308
343
  });
344
+ commands.addCommand(CommandIDs.toggleNavigateToCurrentDirectory, {
345
+ label: trans.__('Show Active File in File Browser'),
346
+ isToggled: () => browser.navigateToCurrentDirectory,
347
+ execute: () => {
348
+ const value = !browser.navigateToCurrentDirectory;
349
+ const key = 'navigateToCurrentDirectory';
350
+ return settingRegistry
351
+ .set(FILE_BROWSER_PLUGIN_ID, key, value)
352
+ .catch((reason) => {
353
+ console.error(`Failed to set navigateToCurrentDirectory setting`);
354
+ });
355
+ }
356
+ });
357
+ if (commandPalette) {
358
+ commandPalette.addItem({
359
+ command: CommandIDs.toggleNavigateToCurrentDirectory,
360
+ category: trans.__('File Operations')
361
+ });
362
+ }
309
363
  // If the layout is a fresh session without saved data and not in single document
310
364
  // mode, open file browser.
311
365
  void labShell.restored.then(layout => {
@@ -314,17 +368,6 @@ const browserWidget = {
314
368
  }
315
369
  });
316
370
  void Promise.all([app.restored, browser.model.restored]).then(() => {
317
- function maybeCreate() {
318
- // Create a launcher if there are no open items.
319
- if (labShell.isEmpty('main') &&
320
- commands.hasCommand('launcher:create')) {
321
- void Private.createLauncher(commands, browser);
322
- }
323
- }
324
- // When layout is modified, create a launcher if there are no open items.
325
- labShell.layoutModified.connect(() => {
326
- maybeCreate();
327
- });
328
371
  // Whether to automatically navigate to a document's current directory
329
372
  labShell.currentChanged.connect(async (_, change) => {
330
373
  if (browser.navigateToCurrentDirectory && change.newValue) {
@@ -333,7 +376,7 @@ const browserWidget = {
333
376
  if (context) {
334
377
  const { path } = context;
335
378
  try {
336
- await Private.navigateToPath(path, factory, translator);
379
+ await Private.navigateToPath(path, browser, factory, translator);
337
380
  }
338
381
  catch (reason) {
339
382
  console.warn(`${CommandIDs.goToPath} failed to open: ${path}`, reason);
@@ -341,7 +384,6 @@ const browserWidget = {
341
384
  }
342
385
  }
343
386
  });
344
- maybeCreate();
345
387
  });
346
388
  }
347
389
  };
@@ -368,17 +410,17 @@ const shareFile = {
368
410
  execute: () => {
369
411
  const widget = tracker.currentWidget;
370
412
  const model = widget === null || widget === void 0 ? void 0 : widget.selectedItems().next();
371
- if (!model) {
413
+ if (model === undefined || model.done) {
372
414
  return;
373
415
  }
374
416
  Clipboard.copyToSystem(PageConfig.getUrl({
375
417
  workspace: PageConfig.defaultWorkspace,
376
- treePath: model.path,
418
+ treePath: model.value.path,
377
419
  toShare: true
378
420
  }));
379
421
  },
380
422
  isVisible: () => !!tracker.currentWidget &&
381
- toArray(tracker.currentWidget.selectedItems()).length === 1,
423
+ Array.from(tracker.currentWidget.selectedItems()).length === 1,
382
424
  icon: linkIcon.bindprops({ stylesheet: 'menuItem' }),
383
425
  label: trans.__('Copy Shareable Link')
384
426
  });
@@ -397,6 +439,7 @@ const openWithPlugin = {
397
439
  activate: (app, factory) => {
398
440
  const { docRegistry } = app;
399
441
  const { tracker } = factory;
442
+ let items = [];
400
443
  function updateOpenWithMenu(contextMenu) {
401
444
  var _a, _b;
402
445
  const openWith = (_b = (_a = contextMenu.menu.items.find(item => {
@@ -408,6 +451,9 @@ const openWithPlugin = {
408
451
  return; // Bail early if the open with menu is not displayed
409
452
  }
410
453
  // clear the current menu items
454
+ items.forEach(item => item.dispose());
455
+ items.length = 0;
456
+ // Ensure that the menu is empty
411
457
  openWith.clearItems();
412
458
  // get the widget factories that could be used to open all of the items
413
459
  // in the current filebrowser selection
@@ -417,12 +463,10 @@ const openWithPlugin = {
417
463
  }))
418
464
  : new Set();
419
465
  // make new menu items from the widget factories
420
- factories.forEach(factory => {
421
- openWith.addItem({
422
- args: { factory: factory.name, label: factory.label || factory.name },
423
- command: CommandIDs.open
424
- });
425
- });
466
+ items = [...factories].map(factory => openWith.addItem({
467
+ args: { factory: factory.name, label: factory.label || factory.name },
468
+ command: CommandIDs.open
469
+ }));
426
470
  }
427
471
  app.contextMenu.opened.connect(updateOpenWithMenu);
428
472
  }
@@ -445,19 +489,38 @@ const openBrowserTabPlugin = {
445
489
  const trans = translator.load('jupyterlab');
446
490
  const { tracker } = factory;
447
491
  commands.addCommand(CommandIDs.openBrowserTab, {
448
- execute: () => {
492
+ execute: args => {
449
493
  const widget = tracker.currentWidget;
450
494
  if (!widget) {
451
495
  return;
452
496
  }
453
- return Promise.all(toArray(map(widget.selectedItems(), item => {
454
- return commands.execute('docmanager:open-browser-tab', {
455
- path: item.path
456
- });
497
+ const mode = args['mode'];
498
+ return Promise.all(Array.from(map(widget.selectedItems(), item => {
499
+ if (mode === 'single-document') {
500
+ const url = PageConfig.getUrl({
501
+ mode: 'single-document',
502
+ treePath: item.path
503
+ });
504
+ const opened = window.open();
505
+ if (opened) {
506
+ opened.opener = null;
507
+ opened.location.href = url;
508
+ }
509
+ else {
510
+ throw new Error('Failed to open new browser tab.');
511
+ }
512
+ }
513
+ else {
514
+ return commands.execute('docmanager:open-browser-tab', {
515
+ path: item.path
516
+ });
517
+ }
457
518
  })));
458
519
  },
459
520
  icon: addIcon.bindprops({ stylesheet: 'menuItem' }),
460
- label: trans.__('Open in New Browser Tab'),
521
+ label: args => args['mode'] === 'single-document'
522
+ ? trans.__('Open in Simple Mode')
523
+ : trans.__('Open in New Browser Tab'),
461
524
  mnemonic: 0
462
525
  });
463
526
  }
@@ -495,12 +558,11 @@ export const fileUploadStatus = {
495
558
  const openUrlPlugin = {
496
559
  id: '@jupyterlab/filebrowser-extension:open-url',
497
560
  autoStart: true,
498
- requires: [IFileBrowserFactory, ITranslator],
561
+ requires: [IDefaultFileBrowser, ITranslator],
499
562
  optional: [ICommandPalette],
500
- activate: (app, factory, translator, palette) => {
563
+ activate: (app, browser, translator, palette) => {
501
564
  const { commands } = app;
502
565
  const trans = translator.load('jupyterlab');
503
- const { defaultBrowser: browser } = factory;
504
566
  const command = CommandIDs.openUrl;
505
567
  commands.addCommand(command, {
506
568
  label: args => args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL…'),
@@ -559,10 +621,10 @@ const openUrlPlugin = {
559
621
  /**
560
622
  * Add the main file browser commands to the application's command registry.
561
623
  */
562
- function addCommands(app, factory, translator, settingRegistry, commandPalette) {
624
+ function addCommands(app, browser, factory, translator, settingRegistry, commandPalette) {
563
625
  const trans = translator.load('jupyterlab');
564
626
  const { docRegistry: registry, commands } = app;
565
- const { defaultBrowser: browser, tracker } = factory;
627
+ const { tracker } = factory;
566
628
  commands.addCommand(CommandIDs.del, {
567
629
  execute: () => {
568
630
  const widget = tracker.currentWidget;
@@ -606,14 +668,15 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
606
668
  label: trans.__('Duplicate')
607
669
  });
608
670
  commands.addCommand(CommandIDs.goToPath, {
671
+ label: trans.__('Update the file browser to display the provided `path`.'),
609
672
  execute: async (args) => {
610
673
  var _a;
611
674
  const path = args.path || '';
612
675
  const showBrowser = !((_a = args === null || args === void 0 ? void 0 : args.dontShowBrowser) !== null && _a !== void 0 ? _a : false);
613
676
  try {
614
- const item = await Private.navigateToPath(path, factory, translator);
677
+ const item = await Private.navigateToPath(path, browser, factory, translator);
615
678
  if (item.type !== 'directory' && showBrowser) {
616
- const browserForPath = Private.getBrowserForPath(path, factory);
679
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
617
680
  if (browserForPath) {
618
681
  browserForPath.clearSelectedItems();
619
682
  const parts = path.split('/');
@@ -635,7 +698,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
635
698
  commands.addCommand(CommandIDs.goUp, {
636
699
  label: 'go up',
637
700
  execute: async () => {
638
- const browserForPath = Private.getBrowserForPath('', factory);
701
+ const browserForPath = Private.getBrowserForPath('', browser, factory);
639
702
  if (!browserForPath) {
640
703
  return;
641
704
  }
@@ -679,7 +742,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
679
742
  // The normal contents service errors on paths ending in slash
680
743
  path = path.slice(0, path.length - 1);
681
744
  }
682
- const browserForPath = Private.getBrowserForPath(path, factory);
745
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
683
746
  const { services } = browserForPath.model.manager;
684
747
  const item = await services.contents.get(path, {
685
748
  content: false
@@ -719,7 +782,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
719
782
  return;
720
783
  }
721
784
  const { contents } = widget.model.manager.services;
722
- return Promise.all(toArray(map(widget.selectedItems(), item => {
785
+ return Promise.all(Array.from(map(widget.selectedItems(), item => {
723
786
  if (item.type === 'directory') {
724
787
  const localPath = contents.localPath(item.path);
725
788
  return widget.model.cd(`/${localPath}`);
@@ -744,7 +807,6 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
744
807
  return folderIcon.bindprops({ stylesheet: 'menuItem' });
745
808
  }
746
809
  },
747
- // FIXME-TRANS: Is this localizable?
748
810
  label: args => (args['label'] || args['factory'] || trans.__('Open')),
749
811
  mnemonic: 0
750
812
  });
@@ -818,13 +880,13 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
818
880
  return;
819
881
  }
820
882
  const item = widget.selectedItems().next();
821
- if (!item) {
883
+ if (item.done) {
822
884
  return;
823
885
  }
824
- Clipboard.copyToSystem(item.path);
886
+ Clipboard.copyToSystem(item.value.path);
825
887
  },
826
888
  isVisible: () => !!tracker.currentWidget &&
827
- tracker.currentWidget.selectedItems().next !== undefined,
889
+ !tracker.currentWidget.selectedItems().next().done,
828
890
  icon: fileIcon.bindprops({ stylesheet: 'menuItem' }),
829
891
  label: trans.__('Copy Path')
830
892
  });
@@ -838,50 +900,32 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
838
900
  icon: stopIcon.bindprops({ stylesheet: 'menuItem' }),
839
901
  label: trans.__('Shut Down Kernel')
840
902
  });
841
- commands.addCommand(CommandIDs.toggleBrowser, {
842
- label: trans.__('File Browser'),
903
+ commands.addCommand(CommandIDs.toggleLastModified, {
904
+ label: trans.__('Show Last Modified Column'),
905
+ isToggled: () => browser.showLastModifiedColumn,
843
906
  execute: () => {
844
- if (browser.isHidden) {
845
- return commands.execute(CommandIDs.showBrowser, void 0);
846
- }
847
- return commands.execute(CommandIDs.hideBrowser, void 0);
848
- }
849
- });
850
- commands.addCommand(CommandIDs.createLauncher, {
851
- label: trans.__('New Launcher'),
852
- icon: args => (args.toolbar ? addIcon : undefined),
853
- execute: (args) => {
854
- if (commands.hasCommand('launcher:create')) {
855
- return Private.createLauncher(commands, browser, args);
856
- }
857
- }
858
- });
859
- if (settingRegistry) {
860
- commands.addCommand(CommandIDs.toggleNavigateToCurrentDirectory, {
861
- label: trans.__('Show Active File in File Browser'),
862
- isToggled: () => browser.navigateToCurrentDirectory,
863
- execute: () => {
864
- const value = !browser.navigateToCurrentDirectory;
865
- const key = 'navigateToCurrentDirectory';
907
+ const value = !browser.showLastModifiedColumn;
908
+ const key = 'showLastModifiedColumn';
909
+ if (settingRegistry) {
866
910
  return settingRegistry
867
911
  .set(FILE_BROWSER_PLUGIN_ID, key, value)
868
912
  .catch((reason) => {
869
- console.error(`Failed to set navigateToCurrentDirectory setting`);
913
+ console.error(`Failed to set ${key} setting`);
870
914
  });
871
915
  }
872
- });
873
- }
874
- commands.addCommand(CommandIDs.toggleLastModified, {
875
- label: trans.__('Show Last Modified Column'),
876
- isToggled: () => browser.showLastModifiedColumn,
916
+ }
917
+ });
918
+ commands.addCommand(CommandIDs.toggleFileSize, {
919
+ label: trans.__('Show File Size Column'),
920
+ isToggled: () => browser.showFileSizeColumn,
877
921
  execute: () => {
878
- const value = !browser.showLastModifiedColumn;
879
- const key = 'showLastModifiedColumn';
922
+ const value = !browser.showFileSizeColumn;
923
+ const key = 'showFileSizeColumn';
880
924
  if (settingRegistry) {
881
925
  return settingRegistry
882
926
  .set(FILE_BROWSER_PLUGIN_ID, key, value)
883
927
  .catch((reason) => {
884
- console.error(`Failed to set showLastModifiedColumn setting`);
928
+ console.error(`Failed to set ${key} setting`);
885
929
  });
886
930
  }
887
931
  }
@@ -921,40 +965,33 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
921
965
  label: trans.__('Search on File Names'),
922
966
  execute: () => alert('search')
923
967
  });
924
- if (commandPalette) {
925
- commandPalette.addItem({
926
- command: CommandIDs.toggleNavigateToCurrentDirectory,
927
- category: trans.__('File Operations')
928
- });
929
- }
930
968
  }
969
+ /**
970
+ * Export the plugins as default.
971
+ */
972
+ const plugins = [
973
+ factory,
974
+ defaultFileBrowser,
975
+ browser,
976
+ shareFile,
977
+ fileUploadStatus,
978
+ downloadPlugin,
979
+ browserWidget,
980
+ openWithPlugin,
981
+ openBrowserTabPlugin,
982
+ openUrlPlugin
983
+ ];
984
+ export default plugins;
931
985
  /**
932
986
  * A namespace for private module data.
933
987
  */
934
988
  var Private;
935
989
  (function (Private) {
936
- /**
937
- * Create a launcher for a given filebrowser widget.
938
- */
939
- function createLauncher(commands, browser, args) {
940
- const { model } = browser;
941
- return commands
942
- .execute('launcher:create', Object.assign({ cwd: model.path }, args))
943
- .then((launcher) => {
944
- model.pathChanged.connect(() => {
945
- if (launcher.content) {
946
- launcher.content.cwd = model.path;
947
- }
948
- }, launcher);
949
- return launcher;
950
- });
951
- }
952
- Private.createLauncher = createLauncher;
953
990
  /**
954
991
  * Get browser object given file path.
955
992
  */
956
- function getBrowserForPath(path, factory) {
957
- const { defaultBrowser: browser, tracker } = factory;
993
+ function getBrowserForPath(path, browser, factory) {
994
+ const { tracker } = factory;
958
995
  const driveName = browser.model.manager.services.contents.driveName(path);
959
996
  if (driveName) {
960
997
  const browserForPath = tracker.find(_path => _path.model.driveName === driveName);
@@ -972,9 +1009,9 @@ var Private;
972
1009
  /**
973
1010
  * Navigate to a path or the path containing a file.
974
1011
  */
975
- async function navigateToPath(path, factory, translator) {
1012
+ async function navigateToPath(path, browser, factory, translator) {
976
1013
  const trans = translator.load('jupyterlab');
977
- const browserForPath = Private.getBrowserForPath(path, factory);
1014
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
978
1015
  if (!browserForPath) {
979
1016
  throw new Error(trans.__('No browser for path'));
980
1017
  }
@@ -996,7 +1033,7 @@ var Private;
996
1033
  /**
997
1034
  * Restores file browser state and overrides state if tree resolver resolves.
998
1035
  */
999
- async function restoreBrowser(browser, commands, router, tree) {
1036
+ async function restoreBrowser(browser, commands, router, tree, app, labShell) {
1000
1037
  const restoring = 'jp-mod-restoring';
1001
1038
  browser.addClass(restoring);
1002
1039
  if (!router) {
@@ -1029,27 +1066,13 @@ var Private;
1029
1066
  await browser.model.refresh();
1030
1067
  }
1031
1068
  browser.removeClass(restoring);
1069
+ if (labShell === null || labShell === void 0 ? void 0 : labShell.isEmpty('main')) {
1070
+ void commands.execute('launcher:create');
1071
+ }
1032
1072
  };
1033
1073
  router.routed.connect(listener);
1034
1074
  }
1035
1075
  Private.restoreBrowser = restoreBrowser;
1036
- })(Private || (Private = {}));
1037
- /**
1038
- * Export the plugins as default.
1039
- */
1040
- const plugins = [
1041
- factory,
1042
- browser,
1043
- shareFile,
1044
- fileUploadStatus,
1045
- downloadPlugin,
1046
- browserWidget,
1047
- openWithPlugin,
1048
- openBrowserTabPlugin,
1049
- openUrlPlugin
1050
- ];
1051
- export default plugins;
1052
- (function (Private) {
1053
1076
  let OpenWith;
1054
1077
  (function (OpenWith) {
1055
1078
  /**
@@ -1071,26 +1094,33 @@ export default plugins;
1071
1094
  }
1072
1095
  OpenWith.getFactories = getFactories;
1073
1096
  /**
1074
- * Return the intersection of multiple arrays.
1097
+ * Return the intersection of multiple iterables.
1075
1098
  *
1076
- * @param iter Iterator of arrays
1077
- * @returns Set of common elements to all arrays
1099
+ * @param iterables Iterator of iterables
1100
+ * @returns Set of common elements to all iterables
1078
1101
  */
1079
- function intersection(iter) {
1080
- // pop the first element of iter
1081
- const first = iter.next();
1082
- // first will be undefined if iter is empty
1083
- if (!first) {
1084
- return new Set();
1102
+ function intersection(iterables) {
1103
+ let accumulator = undefined;
1104
+ for (const current of iterables) {
1105
+ // Initialize accumulator.
1106
+ if (accumulator === undefined) {
1107
+ accumulator = new Set(current);
1108
+ continue;
1109
+ }
1110
+ // Return early if empty.
1111
+ if (accumulator.size === 0) {
1112
+ return accumulator;
1113
+ }
1114
+ // Keep the intersection of accumulator and current.
1115
+ let intersection = new Set();
1116
+ for (const value of current) {
1117
+ if (accumulator.has(value)) {
1118
+ intersection.add(value);
1119
+ }
1120
+ }
1121
+ accumulator = intersection;
1085
1122
  }
1086
- // "initialize" the intersection from first
1087
- const isect = new Set(first);
1088
- // reduce over the remaining elements of iter
1089
- return reduce(iter, (isect, subarr) => {
1090
- // filter out all elements not present in both isect and subarr,
1091
- // accumulate result in new set
1092
- return new Set(subarr.filter(x => isect.has(x)));
1093
- }, isect);
1123
+ return accumulator !== null && accumulator !== void 0 ? accumulator : new Set();
1094
1124
  }
1095
1125
  OpenWith.intersection = intersection;
1096
1126
  })(OpenWith = Private.OpenWith || (Private.OpenWith = {}));