@jupyterlab/filebrowser-extension 4.0.0-alpha.8 → 4.0.0-beta.0

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,15 +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, 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
+ 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';
20
25
  /**
21
26
  * The command IDs used by the file browser plugin.
22
27
  */
@@ -24,8 +29,6 @@ var CommandIDs;
24
29
  (function (CommandIDs) {
25
30
  CommandIDs.copy = 'filebrowser:copy';
26
31
  CommandIDs.copyDownloadLink = 'filebrowser:copy-download-link';
27
- // For main browser only.
28
- CommandIDs.createLauncher = 'filebrowser:create-main-launcher';
29
32
  CommandIDs.cut = 'filebrowser:cut';
30
33
  CommandIDs.del = 'filebrowser:delete';
31
34
  CommandIDs.download = 'filebrowser:download';
@@ -54,8 +57,10 @@ var CommandIDs;
54
57
  CommandIDs.toggleBrowser = 'filebrowser:toggle-main';
55
58
  CommandIDs.toggleNavigateToCurrentDirectory = 'filebrowser:toggle-navigate-to-current-directory';
56
59
  CommandIDs.toggleLastModified = 'filebrowser:toggle-last-modified';
60
+ CommandIDs.toggleFileSize = 'filebrowser:toggle-file-size';
57
61
  CommandIDs.search = 'filebrowser:search';
58
62
  CommandIDs.toggleHiddenFiles = 'filebrowser:toggle-hidden-files';
63
+ CommandIDs.toggleFileCheckboxes = 'filebrowser:toggle-file-checkboxes';
59
64
  })(CommandIDs || (CommandIDs = {}));
60
65
  /**
61
66
  * The file browser namespace token.
@@ -65,18 +70,18 @@ const namespace = 'filebrowser';
65
70
  * The default file browser extension.
66
71
  */
67
72
  const browser = {
68
- id: '@jupyterlab/filebrowser-extension:browser',
69
- requires: [IFileBrowserFactory, ITranslator],
73
+ id: FILE_BROWSER_PLUGIN_ID,
74
+ requires: [IDefaultFileBrowser, IFileBrowserFactory, ITranslator],
70
75
  optional: [
71
76
  ILayoutRestorer,
72
77
  ISettingRegistry,
73
78
  ITreePathUpdater,
74
79
  ICommandPalette
75
80
  ],
81
+ provides: IFileBrowserCommands,
76
82
  autoStart: true,
77
- activate: async (app, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
78
- const trans = translator.load('jupyterlab');
79
- const browser = factory.defaultBrowser;
83
+ activate: async (app, defaultFileBrowser, factory, translator, restorer, settingRegistry, treePathUpdater, commandPalette) => {
84
+ const browser = defaultFileBrowser;
80
85
  // Let the application restorer track the primary file browser (that is
81
86
  // automatically created) for restoration of application state (e.g. setting
82
87
  // the file browser as the current side bar widget).
@@ -91,66 +96,42 @@ const browser = {
91
96
  if (preferredPath) {
92
97
  await browser.model.cd(preferredPath);
93
98
  }
94
- addCommands(app, factory, translator, settingRegistry, commandPalette);
95
- // Show the current file browser shortcut in its title.
96
- const updateBrowserTitle = () => {
97
- const binding = find(app.commands.keyBindings, b => b.command === CommandIDs.toggleBrowser);
98
- if (binding) {
99
- const ks = CommandRegistry.formatKeystroke(binding.keys.join(' '));
100
- browser.title.caption = trans.__('File Browser (%1)', ks);
101
- }
102
- else {
103
- browser.title.caption = trans.__('File Browser');
104
- }
105
- };
106
- updateBrowserTitle();
107
- app.commands.keyBindingChanged.connect(() => {
108
- updateBrowserTitle();
109
- });
110
- void Promise.all([app.restored, browser.model.restored]).then(() => {
99
+ addCommands(app, browser, factory, translator, settingRegistry, commandPalette);
100
+ return void Promise.all([app.restored, browser.model.restored]).then(() => {
111
101
  if (treePathUpdater) {
112
102
  browser.model.pathChanged.connect((sender, args) => {
113
103
  treePathUpdater(args.newValue);
114
104
  });
115
105
  }
116
- let navigateToCurrentDirectory = false;
117
- let showLastModifiedColumn = true;
118
- let useFuzzyFilter = true;
119
- let showHiddenFiles = false;
120
106
  if (settingRegistry) {
121
- void settingRegistry
122
- .load('@jupyterlab/filebrowser-extension:browser')
123
- .then(settings => {
124
- settings.changed.connect(settings => {
125
- navigateToCurrentDirectory = settings.get('navigateToCurrentDirectory').composite;
126
- browser.navigateToCurrentDirectory = navigateToCurrentDirectory;
127
- });
128
- navigateToCurrentDirectory = settings.get('navigateToCurrentDirectory').composite;
129
- browser.navigateToCurrentDirectory = navigateToCurrentDirectory;
130
- settings.changed.connect(settings => {
131
- showLastModifiedColumn = settings.get('showLastModifiedColumn')
132
- .composite;
133
- browser.showLastModifiedColumn = showLastModifiedColumn;
134
- });
135
- showLastModifiedColumn = settings.get('showLastModifiedColumn')
136
- .composite;
137
- browser.showLastModifiedColumn = showLastModifiedColumn;
138
- settings.changed.connect(settings => {
139
- useFuzzyFilter = settings.get('useFuzzyFilter')
140
- .composite;
141
- browser.useFuzzyFilter = useFuzzyFilter;
142
- });
143
- useFuzzyFilter = settings.get('useFuzzyFilter')
144
- .composite;
145
- browser.useFuzzyFilter = useFuzzyFilter;
146
- settings.changed.connect(settings => {
147
- showHiddenFiles = settings.get('showHiddenFiles')
107
+ void settingRegistry.load(FILE_BROWSER_PLUGIN_ID).then(settings => {
108
+ /**
109
+ * File browser configuration.
110
+ */
111
+ const fileBrowserConfig = {
112
+ navigateToCurrentDirectory: false,
113
+ showLastModifiedColumn: true,
114
+ showFileSizeColumn: false,
115
+ showHiddenFiles: false,
116
+ showFileCheckboxes: false
117
+ };
118
+ const fileBrowserModelConfig = {
119
+ filterDirectories: true
120
+ };
121
+ function onSettingsChanged(settings) {
122
+ let key;
123
+ for (key in fileBrowserConfig) {
124
+ const value = settings.get(key).composite;
125
+ fileBrowserConfig[key] = value;
126
+ browser[key] = value;
127
+ }
128
+ const value = settings.get('filterDirectories')
148
129
  .composite;
149
- browser.showHiddenFiles = showHiddenFiles;
150
- });
151
- showHiddenFiles = settings.get('showHiddenFiles')
152
- .composite;
153
- browser.showHiddenFiles = showHiddenFiles;
130
+ fileBrowserModelConfig.filterDirectories = value;
131
+ browser.model.filterDirectories = value;
132
+ }
133
+ settings.changed.connect(onSettingsChanged);
134
+ onSettingsChanged(settings);
154
135
  });
155
136
  }
156
137
  });
@@ -163,14 +144,8 @@ const factory = {
163
144
  id: '@jupyterlab/filebrowser-extension:factory',
164
145
  provides: IFileBrowserFactory,
165
146
  requires: [IDocumentManager, ITranslator],
166
- optional: [
167
- IStateDB,
168
- IRouter,
169
- JupyterFrontEnd.ITreeResolver,
170
- JupyterLab.IInfo
171
- ],
172
- activate: async (app, docManager, translator, state, router, tree, info) => {
173
- const { commands } = app;
147
+ optional: [IStateDB, JupyterLab.IInfo],
148
+ activate: async (app, docManager, translator, state, info) => {
174
149
  const tracker = new WidgetTracker({ namespace });
175
150
  const createFileBrowser = (id, options = {}) => {
176
151
  var _a;
@@ -196,13 +171,26 @@ const factory = {
196
171
  void tracker.add(widget);
197
172
  return widget;
198
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;
199
187
  // Manually restore and load the default file browser.
200
- const defaultBrowser = createFileBrowser('filebrowser', {
188
+ const defaultBrowser = fileBrowserFactory.createFileBrowser('filebrowser', {
201
189
  auto: false,
202
190
  restore: false
203
191
  });
204
- void Private.restoreBrowser(defaultBrowser, commands, router, tree);
205
- return { createFileBrowser, defaultBrowser, tracker };
192
+ void Private.restoreBrowser(defaultBrowser, commands, router, tree, app, labShell);
193
+ return defaultBrowser;
206
194
  }
207
195
  };
208
196
  /**
@@ -237,7 +225,7 @@ const downloadPlugin = {
237
225
  return;
238
226
  }
239
227
  return widget.model.manager.services.contents
240
- .getDownloadUrl(widget.selectedItems().next().path)
228
+ .getDownloadUrl(widget.selectedItems().next().value.path)
241
229
  .then(url => {
242
230
  Clipboard.copyToSystem(url);
243
231
  });
@@ -255,29 +243,73 @@ const browserWidget = {
255
243
  id: '@jupyterlab/filebrowser-extension:widget',
256
244
  requires: [
257
245
  IDocumentManager,
246
+ IDefaultFileBrowser,
258
247
  IFileBrowserFactory,
259
248
  ISettingRegistry,
260
249
  IToolbarWidgetRegistry,
261
250
  ITranslator,
262
- ILabShell
251
+ ILabShell,
252
+ IFileBrowserCommands
263
253
  ],
254
+ optional: [ICommandPalette],
264
255
  autoStart: true,
265
- 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) => {
266
259
  const { commands } = app;
267
- const { defaultBrowser: browser, tracker } = factory;
260
+ const { tracker } = factory;
268
261
  const trans = translator.load('jupyterlab');
269
262
  // Set attributes when adding the browser to the UI
270
263
  browser.node.setAttribute('role', 'region');
271
264
  browser.node.setAttribute('aria-label', trans.__('File Browser Section'));
272
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
+ });
273
281
  // Toolbar
274
- toolbarRegistry.registerFactory(FILE_BROWSER_FACTORY, 'uploader', (browser) => new Uploader({ model: browser.model, translator }));
275
- setToolbar(browser, createToolbarFactory(toolbarRegistry, settings, FILE_BROWSER_FACTORY, browserWidget.id, translator));
276
- 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
+ });
277
308
  commands.addCommand(CommandIDs.showBrowser, {
309
+ label: trans.__('Open the file browser for the provided `path`.'),
278
310
  execute: args => {
279
311
  const path = args.path || '';
280
- const browserForPath = Private.getBrowserForPath(path, factory);
312
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
281
313
  // Check for browser not found
282
314
  if (!browserForPath) {
283
315
  return;
@@ -290,20 +322,18 @@ const browserWidget = {
290
322
  else {
291
323
  const areas = ['left', 'right'];
292
324
  for (const area of areas) {
293
- const it = labShell.widgets(area);
294
- let widget = it.next();
295
- while (widget) {
325
+ for (const widget of labShell.widgets(area)) {
296
326
  if (widget.contains(browserForPath)) {
297
327
  labShell.activateById(widget.id);
298
328
  return;
299
329
  }
300
- widget = it.next();
301
330
  }
302
331
  }
303
332
  }
304
333
  }
305
334
  });
306
335
  commands.addCommand(CommandIDs.hideBrowser, {
336
+ label: trans.__('Hide the file browser.'),
307
337
  execute: () => {
308
338
  const widget = tracker.currentWidget;
309
339
  if (widget && !widget.isHidden) {
@@ -311,6 +341,25 @@ const browserWidget = {
311
341
  }
312
342
  }
313
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
+ }
314
363
  // If the layout is a fresh session without saved data and not in single document
315
364
  // mode, open file browser.
316
365
  void labShell.restored.then(layout => {
@@ -319,17 +368,6 @@ const browserWidget = {
319
368
  }
320
369
  });
321
370
  void Promise.all([app.restored, browser.model.restored]).then(() => {
322
- function maybeCreate() {
323
- // Create a launcher if there are no open items.
324
- if (labShell.isEmpty('main') &&
325
- commands.hasCommand('launcher:create')) {
326
- void Private.createLauncher(commands, browser);
327
- }
328
- }
329
- // When layout is modified, create a launcher if there are no open items.
330
- labShell.layoutModified.connect(() => {
331
- maybeCreate();
332
- });
333
371
  // Whether to automatically navigate to a document's current directory
334
372
  labShell.currentChanged.connect(async (_, change) => {
335
373
  if (browser.navigateToCurrentDirectory && change.newValue) {
@@ -338,7 +376,7 @@ const browserWidget = {
338
376
  if (context) {
339
377
  const { path } = context;
340
378
  try {
341
- await Private.navigateToPath(path, factory, translator);
379
+ await Private.navigateToPath(path, browser, factory, translator);
342
380
  }
343
381
  catch (reason) {
344
382
  console.warn(`${CommandIDs.goToPath} failed to open: ${path}`, reason);
@@ -346,7 +384,6 @@ const browserWidget = {
346
384
  }
347
385
  }
348
386
  });
349
- maybeCreate();
350
387
  });
351
388
  }
352
389
  };
@@ -373,17 +410,17 @@ const shareFile = {
373
410
  execute: () => {
374
411
  const widget = tracker.currentWidget;
375
412
  const model = widget === null || widget === void 0 ? void 0 : widget.selectedItems().next();
376
- if (!model) {
413
+ if (model === undefined || model.done) {
377
414
  return;
378
415
  }
379
416
  Clipboard.copyToSystem(PageConfig.getUrl({
380
417
  workspace: PageConfig.defaultWorkspace,
381
- treePath: model.path,
418
+ treePath: model.value.path,
382
419
  toShare: true
383
420
  }));
384
421
  },
385
422
  isVisible: () => !!tracker.currentWidget &&
386
- toArray(tracker.currentWidget.selectedItems()).length === 1,
423
+ Array.from(tracker.currentWidget.selectedItems()).length === 1,
387
424
  icon: linkIcon.bindprops({ stylesheet: 'menuItem' }),
388
425
  label: trans.__('Copy Shareable Link')
389
426
  });
@@ -402,6 +439,7 @@ const openWithPlugin = {
402
439
  activate: (app, factory) => {
403
440
  const { docRegistry } = app;
404
441
  const { tracker } = factory;
442
+ let items = [];
405
443
  function updateOpenWithMenu(contextMenu) {
406
444
  var _a, _b;
407
445
  const openWith = (_b = (_a = contextMenu.menu.items.find(item => {
@@ -413,6 +451,9 @@ const openWithPlugin = {
413
451
  return; // Bail early if the open with menu is not displayed
414
452
  }
415
453
  // clear the current menu items
454
+ items.forEach(item => item.dispose());
455
+ items.length = 0;
456
+ // Ensure that the menu is empty
416
457
  openWith.clearItems();
417
458
  // get the widget factories that could be used to open all of the items
418
459
  // in the current filebrowser selection
@@ -422,12 +463,10 @@ const openWithPlugin = {
422
463
  }))
423
464
  : new Set();
424
465
  // make new menu items from the widget factories
425
- factories.forEach(factory => {
426
- openWith.addItem({
427
- args: { factory: factory.name, label: factory.label || factory.name },
428
- command: CommandIDs.open
429
- });
430
- });
466
+ items = [...factories].map(factory => openWith.addItem({
467
+ args: { factory: factory.name, label: factory.label || factory.name },
468
+ command: CommandIDs.open
469
+ }));
431
470
  }
432
471
  app.contextMenu.opened.connect(updateOpenWithMenu);
433
472
  }
@@ -450,19 +489,38 @@ const openBrowserTabPlugin = {
450
489
  const trans = translator.load('jupyterlab');
451
490
  const { tracker } = factory;
452
491
  commands.addCommand(CommandIDs.openBrowserTab, {
453
- execute: () => {
492
+ execute: args => {
454
493
  const widget = tracker.currentWidget;
455
494
  if (!widget) {
456
495
  return;
457
496
  }
458
- return Promise.all(toArray(map(widget.selectedItems(), item => {
459
- return commands.execute('docmanager:open-browser-tab', {
460
- path: item.path
461
- });
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
+ }
462
518
  })));
463
519
  },
464
520
  icon: addIcon.bindprops({ stylesheet: 'menuItem' }),
465
- 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'),
466
524
  mnemonic: 0
467
525
  });
468
526
  }
@@ -500,12 +558,11 @@ export const fileUploadStatus = {
500
558
  const openUrlPlugin = {
501
559
  id: '@jupyterlab/filebrowser-extension:open-url',
502
560
  autoStart: true,
503
- requires: [IFileBrowserFactory, ITranslator],
561
+ requires: [IDefaultFileBrowser, ITranslator],
504
562
  optional: [ICommandPalette],
505
- activate: (app, factory, translator, palette) => {
563
+ activate: (app, browser, translator, palette) => {
506
564
  const { commands } = app;
507
565
  const trans = translator.load('jupyterlab');
508
- const { defaultBrowser: browser } = factory;
509
566
  const command = CommandIDs.openUrl;
510
567
  commands.addCommand(command, {
511
568
  label: args => args.url ? trans.__('Open %1', args.url) : trans.__('Open from URL…'),
@@ -564,10 +621,10 @@ const openUrlPlugin = {
564
621
  /**
565
622
  * Add the main file browser commands to the application's command registry.
566
623
  */
567
- function addCommands(app, factory, translator, settingRegistry, commandPalette) {
624
+ function addCommands(app, browser, factory, translator, settingRegistry, commandPalette) {
568
625
  const trans = translator.load('jupyterlab');
569
626
  const { docRegistry: registry, commands } = app;
570
- const { defaultBrowser: browser, tracker } = factory;
627
+ const { tracker } = factory;
571
628
  commands.addCommand(CommandIDs.del, {
572
629
  execute: () => {
573
630
  const widget = tracker.currentWidget;
@@ -611,14 +668,15 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
611
668
  label: trans.__('Duplicate')
612
669
  });
613
670
  commands.addCommand(CommandIDs.goToPath, {
671
+ label: trans.__('Update the file browser to display the provided `path`.'),
614
672
  execute: async (args) => {
615
673
  var _a;
616
674
  const path = args.path || '';
617
675
  const showBrowser = !((_a = args === null || args === void 0 ? void 0 : args.dontShowBrowser) !== null && _a !== void 0 ? _a : false);
618
676
  try {
619
- const item = await Private.navigateToPath(path, factory, translator);
677
+ const item = await Private.navigateToPath(path, browser, factory, translator);
620
678
  if (item.type !== 'directory' && showBrowser) {
621
- const browserForPath = Private.getBrowserForPath(path, factory);
679
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
622
680
  if (browserForPath) {
623
681
  browserForPath.clearSelectedItems();
624
682
  const parts = path.split('/');
@@ -640,7 +698,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
640
698
  commands.addCommand(CommandIDs.goUp, {
641
699
  label: 'go up',
642
700
  execute: async () => {
643
- const browserForPath = Private.getBrowserForPath('', factory);
701
+ const browserForPath = Private.getBrowserForPath('', browser, factory);
644
702
  if (!browserForPath) {
645
703
  return;
646
704
  }
@@ -684,7 +742,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
684
742
  // The normal contents service errors on paths ending in slash
685
743
  path = path.slice(0, path.length - 1);
686
744
  }
687
- const browserForPath = Private.getBrowserForPath(path, factory);
745
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
688
746
  const { services } = browserForPath.model.manager;
689
747
  const item = await services.contents.get(path, {
690
748
  content: false
@@ -724,7 +782,7 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
724
782
  return;
725
783
  }
726
784
  const { contents } = widget.model.manager.services;
727
- return Promise.all(toArray(map(widget.selectedItems(), item => {
785
+ return Promise.all(Array.from(map(widget.selectedItems(), item => {
728
786
  if (item.type === 'directory') {
729
787
  const localPath = contents.localPath(item.path);
730
788
  return widget.model.cd(`/${localPath}`);
@@ -749,7 +807,6 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
749
807
  return folderIcon.bindprops({ stylesheet: 'menuItem' });
750
808
  }
751
809
  },
752
- // FIXME-TRANS: Is this localizable?
753
810
  label: args => (args['label'] || args['factory'] || trans.__('Open')),
754
811
  mnemonic: 0
755
812
  });
@@ -823,13 +880,13 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
823
880
  return;
824
881
  }
825
882
  const item = widget.selectedItems().next();
826
- if (!item) {
883
+ if (item.done) {
827
884
  return;
828
885
  }
829
- Clipboard.copyToSystem(item.path);
886
+ Clipboard.copyToSystem(item.value.path);
830
887
  },
831
888
  isVisible: () => !!tracker.currentWidget &&
832
- tracker.currentWidget.selectedItems().next !== undefined,
889
+ !tracker.currentWidget.selectedItems().next().done,
833
890
  icon: fileIcon.bindprops({ stylesheet: 'menuItem' }),
834
891
  label: trans.__('Copy Path')
835
892
  });
@@ -843,39 +900,6 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
843
900
  icon: stopIcon.bindprops({ stylesheet: 'menuItem' }),
844
901
  label: trans.__('Shut Down Kernel')
845
902
  });
846
- commands.addCommand(CommandIDs.toggleBrowser, {
847
- label: trans.__('File Browser'),
848
- execute: () => {
849
- if (browser.isHidden) {
850
- return commands.execute(CommandIDs.showBrowser, void 0);
851
- }
852
- return commands.execute(CommandIDs.hideBrowser, void 0);
853
- }
854
- });
855
- commands.addCommand(CommandIDs.createLauncher, {
856
- label: trans.__('New Launcher'),
857
- icon: args => (args.toolbar ? addIcon : undefined),
858
- execute: (args) => {
859
- if (commands.hasCommand('launcher:create')) {
860
- return Private.createLauncher(commands, browser, args);
861
- }
862
- }
863
- });
864
- if (settingRegistry) {
865
- commands.addCommand(CommandIDs.toggleNavigateToCurrentDirectory, {
866
- label: trans.__('Show Active File in File Browser'),
867
- isToggled: () => browser.navigateToCurrentDirectory,
868
- execute: () => {
869
- const value = !browser.navigateToCurrentDirectory;
870
- const key = 'navigateToCurrentDirectory';
871
- return settingRegistry
872
- .set('@jupyterlab/filebrowser-extension:browser', key, value)
873
- .catch((reason) => {
874
- console.error(`Failed to set navigateToCurrentDirectory setting`);
875
- });
876
- }
877
- });
878
- }
879
903
  commands.addCommand(CommandIDs.toggleLastModified, {
880
904
  label: trans.__('Show Last Modified Column'),
881
905
  isToggled: () => browser.showLastModifiedColumn,
@@ -884,9 +908,24 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
884
908
  const key = 'showLastModifiedColumn';
885
909
  if (settingRegistry) {
886
910
  return settingRegistry
887
- .set('@jupyterlab/filebrowser-extension:browser', key, value)
911
+ .set(FILE_BROWSER_PLUGIN_ID, key, value)
888
912
  .catch((reason) => {
889
- console.error(`Failed to set showLastModifiedColumn setting`);
913
+ console.error(`Failed to set ${key} setting`);
914
+ });
915
+ }
916
+ }
917
+ });
918
+ commands.addCommand(CommandIDs.toggleFileSize, {
919
+ label: trans.__('Show File Size Column'),
920
+ isToggled: () => browser.showFileSizeColumn,
921
+ execute: () => {
922
+ const value = !browser.showFileSizeColumn;
923
+ const key = 'showFileSizeColumn';
924
+ if (settingRegistry) {
925
+ return settingRegistry
926
+ .set(FILE_BROWSER_PLUGIN_ID, key, value)
927
+ .catch((reason) => {
928
+ console.error(`Failed to set ${key} setting`);
890
929
  });
891
930
  }
892
931
  }
@@ -900,51 +939,59 @@ function addCommands(app, factory, translator, settingRegistry, commandPalette)
900
939
  const key = 'showHiddenFiles';
901
940
  if (settingRegistry) {
902
941
  return settingRegistry
903
- .set('@jupyterlab/filebrowser-extension:browser', key, value)
942
+ .set(FILE_BROWSER_PLUGIN_ID, key, value)
904
943
  .catch((reason) => {
905
944
  console.error(`Failed to set showHiddenFiles setting`);
906
945
  });
907
946
  }
908
947
  }
909
948
  });
949
+ commands.addCommand(CommandIDs.toggleFileCheckboxes, {
950
+ label: trans.__('Show File Checkboxes'),
951
+ isToggled: () => browser.showFileCheckboxes,
952
+ execute: () => {
953
+ const value = !browser.showFileCheckboxes;
954
+ const key = 'showFileCheckboxes';
955
+ if (settingRegistry) {
956
+ return settingRegistry
957
+ .set(FILE_BROWSER_PLUGIN_ID, key, value)
958
+ .catch((reason) => {
959
+ console.error(`Failed to set showFileCheckboxes setting`);
960
+ });
961
+ }
962
+ }
963
+ });
910
964
  commands.addCommand(CommandIDs.search, {
911
965
  label: trans.__('Search on File Names'),
912
966
  execute: () => alert('search')
913
967
  });
914
- if (commandPalette) {
915
- commandPalette.addItem({
916
- command: CommandIDs.toggleNavigateToCurrentDirectory,
917
- category: trans.__('File Operations')
918
- });
919
- }
920
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;
921
985
  /**
922
986
  * A namespace for private module data.
923
987
  */
924
988
  var Private;
925
989
  (function (Private) {
926
- /**
927
- * Create a launcher for a given filebrowser widget.
928
- */
929
- function createLauncher(commands, browser, args) {
930
- const { model } = browser;
931
- return commands
932
- .execute('launcher:create', Object.assign({ cwd: model.path }, args))
933
- .then((launcher) => {
934
- model.pathChanged.connect(() => {
935
- if (launcher.content) {
936
- launcher.content.cwd = model.path;
937
- }
938
- }, launcher);
939
- return launcher;
940
- });
941
- }
942
- Private.createLauncher = createLauncher;
943
990
  /**
944
991
  * Get browser object given file path.
945
992
  */
946
- function getBrowserForPath(path, factory) {
947
- const { defaultBrowser: browser, tracker } = factory;
993
+ function getBrowserForPath(path, browser, factory) {
994
+ const { tracker } = factory;
948
995
  const driveName = browser.model.manager.services.contents.driveName(path);
949
996
  if (driveName) {
950
997
  const browserForPath = tracker.find(_path => _path.model.driveName === driveName);
@@ -962,9 +1009,9 @@ var Private;
962
1009
  /**
963
1010
  * Navigate to a path or the path containing a file.
964
1011
  */
965
- async function navigateToPath(path, factory, translator) {
1012
+ async function navigateToPath(path, browser, factory, translator) {
966
1013
  const trans = translator.load('jupyterlab');
967
- const browserForPath = Private.getBrowserForPath(path, factory);
1014
+ const browserForPath = Private.getBrowserForPath(path, browser, factory);
968
1015
  if (!browserForPath) {
969
1016
  throw new Error(trans.__('No browser for path'));
970
1017
  }
@@ -986,7 +1033,7 @@ var Private;
986
1033
  /**
987
1034
  * Restores file browser state and overrides state if tree resolver resolves.
988
1035
  */
989
- async function restoreBrowser(browser, commands, router, tree) {
1036
+ async function restoreBrowser(browser, commands, router, tree, app, labShell) {
990
1037
  const restoring = 'jp-mod-restoring';
991
1038
  browser.addClass(restoring);
992
1039
  if (!router) {
@@ -1019,27 +1066,13 @@ var Private;
1019
1066
  await browser.model.refresh();
1020
1067
  }
1021
1068
  browser.removeClass(restoring);
1069
+ if (labShell === null || labShell === void 0 ? void 0 : labShell.isEmpty('main')) {
1070
+ void commands.execute('launcher:create');
1071
+ }
1022
1072
  };
1023
1073
  router.routed.connect(listener);
1024
1074
  }
1025
1075
  Private.restoreBrowser = restoreBrowser;
1026
- })(Private || (Private = {}));
1027
- /**
1028
- * Export the plugins as default.
1029
- */
1030
- const plugins = [
1031
- factory,
1032
- browser,
1033
- shareFile,
1034
- fileUploadStatus,
1035
- downloadPlugin,
1036
- browserWidget,
1037
- openWithPlugin,
1038
- openBrowserTabPlugin,
1039
- openUrlPlugin
1040
- ];
1041
- export default plugins;
1042
- (function (Private) {
1043
1076
  let OpenWith;
1044
1077
  (function (OpenWith) {
1045
1078
  /**
@@ -1061,26 +1094,33 @@ export default plugins;
1061
1094
  }
1062
1095
  OpenWith.getFactories = getFactories;
1063
1096
  /**
1064
- * Return the intersection of multiple arrays.
1097
+ * Return the intersection of multiple iterables.
1065
1098
  *
1066
- * @param iter Iterator of arrays
1067
- * @returns Set of common elements to all arrays
1099
+ * @param iterables Iterator of iterables
1100
+ * @returns Set of common elements to all iterables
1068
1101
  */
1069
- function intersection(iter) {
1070
- // pop the first element of iter
1071
- const first = iter.next();
1072
- // first will be undefined if iter is empty
1073
- if (!first) {
1074
- 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;
1075
1122
  }
1076
- // "initialize" the intersection from first
1077
- const isect = new Set(first);
1078
- // reduce over the remaining elements of iter
1079
- return reduce(iter, (isect, subarr) => {
1080
- // filter out all elements not present in both isect and subarr,
1081
- // accumulate result in new set
1082
- return new Set(subarr.filter(x => isect.has(x)));
1083
- }, isect);
1123
+ return accumulator !== null && accumulator !== void 0 ? accumulator : new Set();
1084
1124
  }
1085
1125
  OpenWith.intersection = intersection;
1086
1126
  })(OpenWith = Private.OpenWith || (Private.OpenWith = {}));